@adonis-agora/agent 0.52.0 → 0.53.0
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 +102 -0
- package/dist/providers/agent_provider.d.ts +2 -2
- package/dist/providers/agent_provider.d.ts.map +1 -1
- package/dist/providers/agent_provider.js +6 -4
- package/dist/providers/agent_provider.js.map +1 -1
- package/dist/providers/mcp_provider.d.ts.map +1 -1
- package/dist/providers/mcp_provider.js +2 -1
- package/dist/providers/mcp_provider.js.map +1 -1
- package/dist/src/agent-deps-factory.d.ts.map +1 -1
- package/dist/src/agent-deps-factory.js +4 -0
- package/dist/src/agent-deps-factory.js.map +1 -1
- package/dist/src/agent-deps.d.ts +5 -0
- package/dist/src/agent-deps.d.ts.map +1 -1
- package/dist/src/agent-deps.js.map +1 -1
- package/dist/src/authorizer.d.ts +11 -3
- package/dist/src/authorizer.d.ts.map +1 -1
- package/dist/src/authorizer.js +13 -3
- package/dist/src/authorizer.js.map +1 -1
- package/dist/src/client/index.d.ts +1 -1
- package/dist/src/client/index.d.ts.map +1 -1
- package/dist/src/client/index.js +1 -1
- package/dist/src/client/index.js.map +1 -1
- package/dist/src/client/sse.d.ts +5 -1
- package/dist/src/client/sse.d.ts.map +1 -1
- package/dist/src/client/sse.js +20 -2
- package/dist/src/client/sse.js.map +1 -1
- package/dist/src/confirm-token.d.ts +52 -0
- package/dist/src/confirm-token.d.ts.map +1 -0
- package/dist/src/confirm-token.js +103 -0
- package/dist/src/confirm-token.js.map +1 -0
- package/dist/src/confirmed-tool.d.ts +139 -0
- package/dist/src/confirmed-tool.d.ts.map +1 -0
- package/dist/src/confirmed-tool.js +203 -0
- package/dist/src/confirmed-tool.js.map +1 -0
- package/dist/src/define_config.d.ts +11 -2
- package/dist/src/define_config.d.ts.map +1 -1
- package/dist/src/define_config.js.map +1 -1
- package/dist/src/durable/agent-run-workflow.d.ts.map +1 -1
- package/dist/src/durable/agent-run-workflow.js +38 -7
- package/dist/src/durable/agent-run-workflow.js.map +1 -1
- package/dist/src/index.d.ts +13 -5
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +9 -4
- package/dist/src/index.js.map +1 -1
- package/dist/src/lucid-token-stream-sink.d.ts +124 -0
- package/dist/src/lucid-token-stream-sink.d.ts.map +1 -0
- package/dist/src/lucid-token-stream-sink.js +324 -0
- package/dist/src/lucid-token-stream-sink.js.map +1 -0
- package/dist/src/mcp/define_config.d.ts +7 -0
- package/dist/src/mcp/define_config.d.ts.map +1 -1
- package/dist/src/mcp/define_config.js.map +1 -1
- package/dist/src/spi/confirm-token-store.d.ts +30 -0
- package/dist/src/spi/confirm-token-store.d.ts.map +1 -0
- package/dist/src/spi/confirm-token-store.js +2 -0
- package/dist/src/spi/confirm-token-store.js.map +1 -0
- package/dist/src/spi/token-stream-sink.d.ts +9 -1
- package/dist/src/spi/token-stream-sink.d.ts.map +1 -1
- package/dist/src/spi/token-stream-sink.js +5 -4
- package/dist/src/spi/token-stream-sink.js.map +1 -1
- package/dist/src/stores/factory.d.ts +60 -1
- package/dist/src/stores/factory.d.ts.map +1 -1
- package/dist/src/stores/factory.js +21 -1
- package/dist/src/stores/factory.js.map +1 -1
- package/dist/src/stores/lucid-confirm-tokens.d.ts +38 -0
- package/dist/src/stores/lucid-confirm-tokens.d.ts.map +1 -0
- package/dist/src/stores/lucid-confirm-tokens.js +81 -0
- package/dist/src/stores/lucid-confirm-tokens.js.map +1 -0
- package/dist/src/stores/lucid-schema.d.ts +26 -6
- package/dist/src/stores/lucid-schema.d.ts.map +1 -1
- package/dist/src/stores/lucid-schema.js +50 -7
- package/dist/src/stores/lucid-schema.js.map +1 -1
- package/dist/src/testing/index.d.ts +1 -0
- package/dist/src/testing/index.d.ts.map +1 -1
- package/dist/src/testing/index.js +1 -0
- package/dist/src/testing/index.js.map +1 -1
- package/dist/src/tool-registry.d.ts +20 -1
- package/dist/src/tool-registry.d.ts.map +1 -1
- package/dist/src/tool-registry.js +15 -2
- package/dist/src/tool-registry.js.map +1 -1
- package/dist/src/types.d.ts +16 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/stubs/config/agent.stub +1 -0
- package/dist/stubs/config/mcp.stub +5 -0
- package/dist/stubs/database/migrations/create_agent_tables.stub +3 -2
- package/package.json +1 -1
- package/skills/agent-governance/SKILL.md +1 -0
- package/skills/agent-setup/SKILL.md +22 -1
- package/skills/agent-tools/SKILL.md +37 -1
package/dist/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC;AAEhC,uFAAuF;AACvF,qFAAqF;AACrF,sDAAsD;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,cAAc,mBAAmB,CAAC;AAElC,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,yBAAyB,GAC1B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAIL,uBAAuB,EACvB,6BAA6B,EAC7B,oBAAoB,EACpB,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EAEZ,SAAS,EACT,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EACL,YAAY,EAEZ,sBAAsB,EACtB,cAAc,EAId,0BAA0B,GAC3B,MAAM,oBAAoB,CAAC;AAQ5B,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,MAAM,EACN,UAAU,EACV,uBAAuB,EACvB,cAAc,EACd,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAEL,sBAAsB,GACvB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type { SinkWriter, StreamFrame, TokenStreamSink } from './spi/token-stream-sink.js';
|
|
2
|
+
import type { LucidDatabaseLike } from './stores/lucid.js';
|
|
3
|
+
export interface LucidTokenStreamSinkOptions {
|
|
4
|
+
/** The frame table. Defaults to `agent_stream_frame` (`AGENT_TABLES.streamFrames`). */
|
|
5
|
+
tableName?: string;
|
|
6
|
+
/**
|
|
7
|
+
* Gap (ms) between two reads of a run a subscriber is following — the most a frame waits in the
|
|
8
|
+
* table before a browser sees it. Defaults to 250.
|
|
9
|
+
*/
|
|
10
|
+
pollIntervalMs?: number;
|
|
11
|
+
/**
|
|
12
|
+
* Gap (ms) between two reads once the run has written nothing for five seconds (parked on a
|
|
13
|
+
* human, a slow tool), so an open SSE connection on a quiet run costs one query a second rather
|
|
14
|
+
* than four. The first frame to arrive puts the subscriber back on `pollIntervalMs`. Defaults to
|
|
15
|
+
* 1000; never below `pollIntervalMs`.
|
|
16
|
+
*/
|
|
17
|
+
idlePollIntervalMs?: number;
|
|
18
|
+
/**
|
|
19
|
+
* Window (ms) consecutive `text` frames are gathered over and written as ONE row — a model
|
|
20
|
+
* streams token by token, and a row per token is more writes than a database should take for a
|
|
21
|
+
* chat answer. Any other frame, and `end()`, write what is gathered first, so order is kept.
|
|
22
|
+
* Defaults to 50. `0` writes every frame as its own row.
|
|
23
|
+
*/
|
|
24
|
+
flushMs?: number;
|
|
25
|
+
/**
|
|
26
|
+
* TTL (seconds) of a run's rows, counted from its LAST write — so a long run stays, and a run
|
|
27
|
+
* that crashed without ending still lapses. {@link LucidTokenStreamSink.purgeExpired} is what
|
|
28
|
+
* deletes them. Defaults to 3600 (1h). `0` keeps rows until `close()`/manual cleanup.
|
|
29
|
+
*/
|
|
30
|
+
ttlSeconds?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Purge lapsed runs from this instance as a side effect of ending a run, at most once a minute,
|
|
33
|
+
* so the table stays bounded in an app that never schedules {@link LucidTokenStreamSink.purgeExpired}.
|
|
34
|
+
* Default `true`. Set `false` to purge only from your own schedule.
|
|
35
|
+
*/
|
|
36
|
+
autoPurge?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Create the frame table on first use (and at startup, under the agent provider). Default `true`.
|
|
39
|
+
* Set `false` when the published migration (`createAgentTables`) owns the schema.
|
|
40
|
+
*/
|
|
41
|
+
autoCreateTables?: boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A multi-replica {@link TokenStreamSink} over the app's SQL database (through `@adonisjs/lucid`) —
|
|
45
|
+
* for a deployment with several replicas and NO Redis. The replica running the model turn appends
|
|
46
|
+
* each frame as a row of `agent_stream_frame`; any replica serves the run's SSE by reading the rows
|
|
47
|
+
* past its cursor, in order, until it meets the end marker. A subscriber that attaches late, or
|
|
48
|
+
* after the run ended, replays from the first row — the same guarantee the in-process and Redis
|
|
49
|
+
* sinks give.
|
|
50
|
+
*
|
|
51
|
+
* Two things differ from the Redis sink, and both are the price of having no broker:
|
|
52
|
+
*
|
|
53
|
+
* - **Delivery is polled.** There is no publish to wake a subscriber, so a frame waits up to
|
|
54
|
+
* `pollIntervalMs` (250 ms) before a browser sees it.
|
|
55
|
+
* - **Text is coalesced on the way in.** Consecutive `text` frames written within `flushMs`
|
|
56
|
+
* (50 ms) are stored as ONE `text` frame holding their concatenation. The stored rows are the
|
|
57
|
+
* run's stream: every subscriber, on every replica, reads the same rows in the same order, so
|
|
58
|
+
* the SSE event ids `AgentSseEncoder` counts from them — and an `after` cursor — stay exact. They
|
|
59
|
+
* are NOT the ids the in-process sink would have produced for the same run (it numbers every
|
|
60
|
+
* token), which only matters to a cursor carried across a change of sink.
|
|
61
|
+
*
|
|
62
|
+
* Sequence numbers are per run, start at 1 and have no gaps: each append takes `MAX(seq) + 1` for
|
|
63
|
+
* its run in the same statement that inserts it, and the `(run_id, seq)` primary key turns a
|
|
64
|
+
* collision into a retry. A run normally has ONE writer, and then no append ever collides; the
|
|
65
|
+
* retry exists for a parent whose delegated children (each forwarding into its stream, possibly
|
|
66
|
+
* from another replica) write at once. Nothing depends on ordering across runs.
|
|
67
|
+
*
|
|
68
|
+
* Text a writer is still gathering lives in the process for at most `flushMs`; a process that dies
|
|
69
|
+
* in that window loses it from the LIVE stream (the persisted message is unaffected).
|
|
70
|
+
*
|
|
71
|
+
* Wire it with `defineConfig({ sink: tokenSinks.lucid() })`.
|
|
72
|
+
*/
|
|
73
|
+
export declare class LucidTokenStreamSink implements TokenStreamSink {
|
|
74
|
+
private readonly db;
|
|
75
|
+
private readonly table;
|
|
76
|
+
private readonly pollIntervalMs;
|
|
77
|
+
private readonly idlePollIntervalMs;
|
|
78
|
+
private readonly flushMs;
|
|
79
|
+
private readonly ttlSeconds;
|
|
80
|
+
private readonly autoPurge;
|
|
81
|
+
private readonly autoCreateTables;
|
|
82
|
+
private readonly writes;
|
|
83
|
+
private provisioned;
|
|
84
|
+
private lastAutoPurge;
|
|
85
|
+
constructor(db: LucidDatabaseLike, options?: LucidTokenStreamSinkOptions);
|
|
86
|
+
/**
|
|
87
|
+
* Create the frame table if it is missing. The agent provider calls this once as the app starts;
|
|
88
|
+
* a sink built by hand falls back to it on first use. A no-op under `autoCreateTables: false`.
|
|
89
|
+
*/
|
|
90
|
+
ensureSchema(): Promise<void>;
|
|
91
|
+
private runWrites;
|
|
92
|
+
/** Forget a run's write state once nothing is gathered, queued or left to report. */
|
|
93
|
+
private release;
|
|
94
|
+
/** Queue `work` behind every write already queued for the run, so rows land in call order. */
|
|
95
|
+
private enqueue;
|
|
96
|
+
/** Queue the gathered text (if any) as one `text` row. */
|
|
97
|
+
private flushText;
|
|
98
|
+
/** Raise, once, a gathered write that failed with nobody awaiting it. */
|
|
99
|
+
private raiseFailure;
|
|
100
|
+
/** Append one row to the run — a frame, or the end marker (`null`). */
|
|
101
|
+
private append;
|
|
102
|
+
/**
|
|
103
|
+
* The run's writer. Writers opened for the same run in one process share what they are gathering,
|
|
104
|
+
* so a delegated run's text (forwarded through a {@link childSinkWriter}) and its parent's next
|
|
105
|
+
* frame keep the order they were written in.
|
|
106
|
+
*/
|
|
107
|
+
open(runId: string): SinkWriter;
|
|
108
|
+
subscribe(runId: string): AsyncIterable<StreamFrame>;
|
|
109
|
+
has(runId: string): Promise<boolean>;
|
|
110
|
+
close(runId: string): Promise<void>;
|
|
111
|
+
/**
|
|
112
|
+
* Delete every run whose last write is older than `ttlSeconds` at `now` (epoch ms) — ended runs
|
|
113
|
+
* past their replay window and runs that crashed without ending alike. Returns how many runs went.
|
|
114
|
+
* Safe to call from any replica, and from several at once. A no-op under `ttlSeconds: 0`.
|
|
115
|
+
*
|
|
116
|
+
* An instance already calls it after a run ends (at most once a minute; `autoPurge`), which keeps
|
|
117
|
+
* the table bounded on a replica that streams. Call it from a schedule as well if a quiet
|
|
118
|
+
* deployment must not keep the last hour's streams around indefinitely.
|
|
119
|
+
*/
|
|
120
|
+
purgeExpired(now?: number): Promise<number>;
|
|
121
|
+
/** {@link purgeExpired}, unawaited and at most once a minute. Its failure is not the run's. */
|
|
122
|
+
private purgeInBackground;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=lucid-token-stream-sink.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lucid-token-stream-sink.d.ts","sourceRoot":"","sources":["../../src/lucid-token-stream-sink.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC3F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AA8B3D,MAAM,WAAW,2BAA2B;IAC1C,uFAAuF;IACvF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAmCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,oBAAqB,YAAW,eAAe;IAaxD,OAAO,CAAC,QAAQ,CAAC,EAAE;IAZrB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAU;IACpC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAU;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgC;IACvD,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,aAAa,CAAK;IAE1B,YACmB,EAAE,EAAE,iBAAiB,EACtC,OAAO,GAAE,2BAAgC,EAiB1C;IAED;;;OAGG;IACH,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAS5B;IAED,OAAO,CAAC,SAAS;IAejB,qFAAqF;IACrF,OAAO,CAAC,OAAO;IAWf,8FAA8F;IAC9F,OAAO,CAAC,OAAO;IAef,0DAA0D;IAC1D,OAAO,CAAC,SAAS;IAWjB,yEAAyE;IACzE,OAAO,CAAC,YAAY;IAQpB,uEAAuE;YACzD,MAAM;IAkBpB;;;;OAIG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CA0C9B;IAEM,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAAC,WAAW,CAAC,CAiC1D;IAEK,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAGzC;IAEK,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYxC;IAED;;;;;;;;OAQG;IACG,YAAY,CAAC,GAAG,GAAE,MAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAmB5D;IAED,+FAA+F;IAC/F,OAAO,CAAC,iBAAiB;CAS1B"}
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { AGENT_TABLES, ensureStreamFrameTable, rowsOf } from './stores/lucid-schema.js';
|
|
2
|
+
/** Default TTL (seconds) for a run's rows: 1h past its last write, the same window the Redis sink keeps. */
|
|
3
|
+
const DEFAULT_TTL_SECONDS = 3600;
|
|
4
|
+
/** Default gap (ms) between two reads of a run a subscriber is following. */
|
|
5
|
+
const DEFAULT_POLL_INTERVAL_MS = 250;
|
|
6
|
+
/** Default gap (ms) between two reads once a run has been quiet for {@link IDLE_AFTER_MS}. */
|
|
7
|
+
const DEFAULT_IDLE_POLL_INTERVAL_MS = 1000;
|
|
8
|
+
/** How long a run has to write nothing before its subscribers slow down to the idle interval. */
|
|
9
|
+
const IDLE_AFTER_MS = 5000;
|
|
10
|
+
/** Default window (ms) consecutive `text` frames are gathered over before they are written as one row. */
|
|
11
|
+
const DEFAULT_FLUSH_MS = 50;
|
|
12
|
+
/** Rows read per query, so a long run's replay is paged rather than loaded whole. */
|
|
13
|
+
const READ_PAGE = 500;
|
|
14
|
+
/** Run ids deleted per statement by {@link LucidTokenStreamSink.purgeExpired}. */
|
|
15
|
+
const PURGE_CHUNK = 200;
|
|
16
|
+
/** The least time between two purges an instance starts on its own (see `autoPurge`). */
|
|
17
|
+
const AUTO_PURGE_EVERY_MS = 60_000;
|
|
18
|
+
/** Attempts at one append before a sequence-number collision is reported instead of retried. */
|
|
19
|
+
const APPEND_ATTEMPTS = 8;
|
|
20
|
+
function toInt(value) {
|
|
21
|
+
if (typeof value === 'number')
|
|
22
|
+
return value;
|
|
23
|
+
if (typeof value === 'bigint')
|
|
24
|
+
return Number(value);
|
|
25
|
+
if (typeof value === 'string')
|
|
26
|
+
return Number.parseInt(value, 10) || 0;
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Did this insert lose the race for a `(run_id, seq)`? Read off the driver's own code — Postgres
|
|
31
|
+
* `23505`, SQLite `SQLITE_CONSTRAINT_*`, MySQL `ER_DUP_ENTRY`/1062, SQL Server 2627 — so nothing
|
|
32
|
+
* else (a missing table, a lost connection) is ever retried as though it were one.
|
|
33
|
+
*/
|
|
34
|
+
function isUniqueViolation(error) {
|
|
35
|
+
const e = error;
|
|
36
|
+
if (e === null || typeof e !== 'object')
|
|
37
|
+
return false;
|
|
38
|
+
if (e.code === '23505' || e.code === 'ER_DUP_ENTRY')
|
|
39
|
+
return true;
|
|
40
|
+
if (typeof e.code === 'string' && e.code.startsWith('SQLITE_CONSTRAINT'))
|
|
41
|
+
return true;
|
|
42
|
+
return e.errno === 1062 || e.number === 2627;
|
|
43
|
+
}
|
|
44
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
45
|
+
/**
|
|
46
|
+
* A multi-replica {@link TokenStreamSink} over the app's SQL database (through `@adonisjs/lucid`) —
|
|
47
|
+
* for a deployment with several replicas and NO Redis. The replica running the model turn appends
|
|
48
|
+
* each frame as a row of `agent_stream_frame`; any replica serves the run's SSE by reading the rows
|
|
49
|
+
* past its cursor, in order, until it meets the end marker. A subscriber that attaches late, or
|
|
50
|
+
* after the run ended, replays from the first row — the same guarantee the in-process and Redis
|
|
51
|
+
* sinks give.
|
|
52
|
+
*
|
|
53
|
+
* Two things differ from the Redis sink, and both are the price of having no broker:
|
|
54
|
+
*
|
|
55
|
+
* - **Delivery is polled.** There is no publish to wake a subscriber, so a frame waits up to
|
|
56
|
+
* `pollIntervalMs` (250 ms) before a browser sees it.
|
|
57
|
+
* - **Text is coalesced on the way in.** Consecutive `text` frames written within `flushMs`
|
|
58
|
+
* (50 ms) are stored as ONE `text` frame holding their concatenation. The stored rows are the
|
|
59
|
+
* run's stream: every subscriber, on every replica, reads the same rows in the same order, so
|
|
60
|
+
* the SSE event ids `AgentSseEncoder` counts from them — and an `after` cursor — stay exact. They
|
|
61
|
+
* are NOT the ids the in-process sink would have produced for the same run (it numbers every
|
|
62
|
+
* token), which only matters to a cursor carried across a change of sink.
|
|
63
|
+
*
|
|
64
|
+
* Sequence numbers are per run, start at 1 and have no gaps: each append takes `MAX(seq) + 1` for
|
|
65
|
+
* its run in the same statement that inserts it, and the `(run_id, seq)` primary key turns a
|
|
66
|
+
* collision into a retry. A run normally has ONE writer, and then no append ever collides; the
|
|
67
|
+
* retry exists for a parent whose delegated children (each forwarding into its stream, possibly
|
|
68
|
+
* from another replica) write at once. Nothing depends on ordering across runs.
|
|
69
|
+
*
|
|
70
|
+
* Text a writer is still gathering lives in the process for at most `flushMs`; a process that dies
|
|
71
|
+
* in that window loses it from the LIVE stream (the persisted message is unaffected).
|
|
72
|
+
*
|
|
73
|
+
* Wire it with `defineConfig({ sink: tokenSinks.lucid() })`.
|
|
74
|
+
*/
|
|
75
|
+
export class LucidTokenStreamSink {
|
|
76
|
+
db;
|
|
77
|
+
table;
|
|
78
|
+
pollIntervalMs;
|
|
79
|
+
idlePollIntervalMs;
|
|
80
|
+
flushMs;
|
|
81
|
+
ttlSeconds;
|
|
82
|
+
autoPurge;
|
|
83
|
+
autoCreateTables;
|
|
84
|
+
writes = new Map();
|
|
85
|
+
provisioned;
|
|
86
|
+
lastAutoPurge = 0;
|
|
87
|
+
constructor(db, options = {}) {
|
|
88
|
+
this.db = db;
|
|
89
|
+
const table = options.tableName ?? AGENT_TABLES.streamFrames;
|
|
90
|
+
// The name is spliced into SQL (DDL takes no bindings), so it has to be a plain identifier.
|
|
91
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(table)) {
|
|
92
|
+
throw new Error(`LucidTokenStreamSink: "${table}" is not a valid table name`);
|
|
93
|
+
}
|
|
94
|
+
this.table = table;
|
|
95
|
+
this.pollIntervalMs = Math.max(1, options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS);
|
|
96
|
+
this.idlePollIntervalMs = Math.max(this.pollIntervalMs, options.idlePollIntervalMs ?? DEFAULT_IDLE_POLL_INTERVAL_MS);
|
|
97
|
+
this.flushMs = Math.max(0, options.flushMs ?? DEFAULT_FLUSH_MS);
|
|
98
|
+
this.ttlSeconds = options.ttlSeconds ?? DEFAULT_TTL_SECONDS;
|
|
99
|
+
this.autoPurge = options.autoPurge ?? true;
|
|
100
|
+
this.autoCreateTables = options.autoCreateTables ?? true;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Create the frame table if it is missing. The agent provider calls this once as the app starts;
|
|
104
|
+
* a sink built by hand falls back to it on first use. A no-op under `autoCreateTables: false`.
|
|
105
|
+
*/
|
|
106
|
+
ensureSchema() {
|
|
107
|
+
if (!this.autoCreateTables)
|
|
108
|
+
return Promise.resolve();
|
|
109
|
+
if (this.provisioned === undefined) {
|
|
110
|
+
this.provisioned = ensureStreamFrameTable(this.db, this.table).catch((error) => {
|
|
111
|
+
this.provisioned = undefined;
|
|
112
|
+
throw error;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return this.provisioned;
|
|
116
|
+
}
|
|
117
|
+
runWrites(runId) {
|
|
118
|
+
let state = this.writes.get(runId);
|
|
119
|
+
if (state === undefined) {
|
|
120
|
+
state = {
|
|
121
|
+
text: null,
|
|
122
|
+
timer: undefined,
|
|
123
|
+
tail: Promise.resolve(),
|
|
124
|
+
pending: 0,
|
|
125
|
+
failure: undefined,
|
|
126
|
+
};
|
|
127
|
+
this.writes.set(runId, state);
|
|
128
|
+
}
|
|
129
|
+
return state;
|
|
130
|
+
}
|
|
131
|
+
/** Forget a run's write state once nothing is gathered, queued or left to report. */
|
|
132
|
+
release(runId, state) {
|
|
133
|
+
if (state.pending === 0 &&
|
|
134
|
+
state.text === null &&
|
|
135
|
+
state.failure === undefined &&
|
|
136
|
+
this.writes.get(runId) === state) {
|
|
137
|
+
this.writes.delete(runId);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/** Queue `work` behind every write already queued for the run, so rows land in call order. */
|
|
141
|
+
enqueue(runId, state, work) {
|
|
142
|
+
state.pending += 1;
|
|
143
|
+
const next = state.tail.then(work);
|
|
144
|
+
state.tail = next
|
|
145
|
+
.then(() => undefined, () => undefined)
|
|
146
|
+
.then(() => {
|
|
147
|
+
state.pending -= 1;
|
|
148
|
+
this.release(runId, state);
|
|
149
|
+
});
|
|
150
|
+
return next;
|
|
151
|
+
}
|
|
152
|
+
/** Queue the gathered text (if any) as one `text` row. */
|
|
153
|
+
flushText(runId, state) {
|
|
154
|
+
if (state.timer !== undefined) {
|
|
155
|
+
clearTimeout(state.timer);
|
|
156
|
+
state.timer = undefined;
|
|
157
|
+
}
|
|
158
|
+
if (state.text === null)
|
|
159
|
+
return Promise.resolve();
|
|
160
|
+
const frame = { t: 'text', v: state.text };
|
|
161
|
+
state.text = null;
|
|
162
|
+
return this.enqueue(runId, state, () => this.append(runId, JSON.stringify(frame)));
|
|
163
|
+
}
|
|
164
|
+
/** Raise, once, a gathered write that failed with nobody awaiting it. */
|
|
165
|
+
raiseFailure(state) {
|
|
166
|
+
const failure = state.failure;
|
|
167
|
+
if (failure !== undefined) {
|
|
168
|
+
state.failure = undefined;
|
|
169
|
+
throw failure.error;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/** Append one row to the run — a frame, or the end marker (`null`). */
|
|
173
|
+
async append(runId, frame) {
|
|
174
|
+
await this.ensureSchema();
|
|
175
|
+
for (let attempt = 1;; attempt += 1) {
|
|
176
|
+
try {
|
|
177
|
+
// The next number and the insert are one statement; see the class comment for why the
|
|
178
|
+
// primary key, not a lock, is what settles two writers asking at once.
|
|
179
|
+
await this.db.rawQuery(`INSERT INTO "${this.table}" ("run_id", "seq", "frame", "created_at") ` +
|
|
180
|
+
`SELECT ?, COALESCE(MAX("seq"), 0) + 1, ?, ? FROM "${this.table}" WHERE "run_id" = ?`, [runId, frame, Date.now(), runId]);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
if (attempt >= APPEND_ATTEMPTS || !isUniqueViolation(error))
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* The run's writer. Writers opened for the same run in one process share what they are gathering,
|
|
191
|
+
* so a delegated run's text (forwarded through a {@link childSinkWriter}) and its parent's next
|
|
192
|
+
* frame keep the order they were written in.
|
|
193
|
+
*/
|
|
194
|
+
open(runId) {
|
|
195
|
+
return {
|
|
196
|
+
write: async (frame) => {
|
|
197
|
+
const state = this.runWrites(runId);
|
|
198
|
+
this.raiseFailure(state);
|
|
199
|
+
if (frame.t === 'text' && this.flushMs > 0) {
|
|
200
|
+
state.text = (state.text ?? '') + frame.v;
|
|
201
|
+
// Counted from the FIRST gathered token, not the last: a steady stream still reaches the
|
|
202
|
+
// table every `flushMs`, instead of being held for as long as the model keeps talking.
|
|
203
|
+
if (state.timer === undefined) {
|
|
204
|
+
state.timer = setTimeout(() => {
|
|
205
|
+
state.timer = undefined;
|
|
206
|
+
this.flushText(runId, state).catch((error) => {
|
|
207
|
+
state.failure = { error };
|
|
208
|
+
});
|
|
209
|
+
}, this.flushMs);
|
|
210
|
+
}
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const flushed = this.flushText(runId, state);
|
|
214
|
+
const appended = this.enqueue(runId, state, () => this.append(runId, JSON.stringify(frame)));
|
|
215
|
+
await Promise.all([flushed, appended]);
|
|
216
|
+
},
|
|
217
|
+
flush: async () => {
|
|
218
|
+
const state = this.runWrites(runId);
|
|
219
|
+
const flushed = this.flushText(runId, state);
|
|
220
|
+
await Promise.all([flushed, state.tail]);
|
|
221
|
+
this.raiseFailure(state);
|
|
222
|
+
this.release(runId, state);
|
|
223
|
+
},
|
|
224
|
+
end: async () => {
|
|
225
|
+
const state = this.runWrites(runId);
|
|
226
|
+
const flushed = this.flushText(runId, state);
|
|
227
|
+
const ended = this.enqueue(runId, state, () => this.append(runId, null));
|
|
228
|
+
await Promise.all([flushed, ended]);
|
|
229
|
+
this.raiseFailure(state);
|
|
230
|
+
this.release(runId, state);
|
|
231
|
+
this.purgeInBackground();
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
async *subscribe(runId) {
|
|
236
|
+
await this.ensureSchema();
|
|
237
|
+
let cursor = 0;
|
|
238
|
+
let quietSince = Date.now();
|
|
239
|
+
while (true) {
|
|
240
|
+
const rows = await this.db
|
|
241
|
+
.from(this.table)
|
|
242
|
+
.where('run_id', runId)
|
|
243
|
+
.where('seq', '>', cursor)
|
|
244
|
+
.orderBy('seq', 'asc')
|
|
245
|
+
.limit(READ_PAGE)
|
|
246
|
+
.select('seq', 'frame');
|
|
247
|
+
let ended = false;
|
|
248
|
+
for (const row of rows) {
|
|
249
|
+
cursor = toInt(row.seq);
|
|
250
|
+
if (row.frame === null || row.frame === undefined) {
|
|
251
|
+
ended = true;
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
yield JSON.parse(String(row.frame));
|
|
255
|
+
}
|
|
256
|
+
if (ended) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
if (rows.length > 0) {
|
|
260
|
+
quietSince = Date.now();
|
|
261
|
+
// A full page means there may be more already written: read on without waiting.
|
|
262
|
+
if (rows.length >= READ_PAGE)
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
await sleep(Date.now() - quietSince >= IDLE_AFTER_MS ? this.idlePollIntervalMs : this.pollIntervalMs);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
async has(runId) {
|
|
269
|
+
await this.ensureSchema();
|
|
270
|
+
return (await this.db.from(this.table).where('run_id', runId).first()) !== null;
|
|
271
|
+
}
|
|
272
|
+
async close(runId) {
|
|
273
|
+
const state = this.writes.get(runId);
|
|
274
|
+
if (state !== undefined) {
|
|
275
|
+
if (state.timer !== undefined)
|
|
276
|
+
clearTimeout(state.timer);
|
|
277
|
+
state.timer = undefined;
|
|
278
|
+
state.text = null;
|
|
279
|
+
state.failure = undefined;
|
|
280
|
+
await state.tail;
|
|
281
|
+
this.writes.delete(runId);
|
|
282
|
+
}
|
|
283
|
+
await this.ensureSchema();
|
|
284
|
+
await this.db.from(this.table).where('run_id', runId).delete();
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Delete every run whose last write is older than `ttlSeconds` at `now` (epoch ms) — ended runs
|
|
288
|
+
* past their replay window and runs that crashed without ending alike. Returns how many runs went.
|
|
289
|
+
* Safe to call from any replica, and from several at once. A no-op under `ttlSeconds: 0`.
|
|
290
|
+
*
|
|
291
|
+
* An instance already calls it after a run ends (at most once a minute; `autoPurge`), which keeps
|
|
292
|
+
* the table bounded on a replica that streams. Call it from a schedule as well if a quiet
|
|
293
|
+
* deployment must not keep the last hour's streams around indefinitely.
|
|
294
|
+
*/
|
|
295
|
+
async purgeExpired(now = Date.now()) {
|
|
296
|
+
if (this.ttlSeconds <= 0)
|
|
297
|
+
return 0;
|
|
298
|
+
await this.ensureSchema();
|
|
299
|
+
const cutoff = now - this.ttlSeconds * 1000;
|
|
300
|
+
// Two steps rather than one `DELETE … WHERE run_id IN (SELECT … FROM the same table)`, which
|
|
301
|
+
// MySQL refuses. A run that writes between the two was idle for the whole TTL a moment ago.
|
|
302
|
+
const lapsed = rowsOf(await this.db.rawQuery(`SELECT "run_id" AS run_id FROM "${this.table}" GROUP BY "run_id" HAVING MAX("created_at") < ?`, [cutoff])).map((row) => String(row.run_id));
|
|
303
|
+
for (let index = 0; index < lapsed.length; index += PURGE_CHUNK) {
|
|
304
|
+
await this.db
|
|
305
|
+
.from(this.table)
|
|
306
|
+
.whereIn('run_id', lapsed.slice(index, index + PURGE_CHUNK))
|
|
307
|
+
.delete();
|
|
308
|
+
}
|
|
309
|
+
return lapsed.length;
|
|
310
|
+
}
|
|
311
|
+
/** {@link purgeExpired}, unawaited and at most once a minute. Its failure is not the run's. */
|
|
312
|
+
purgeInBackground() {
|
|
313
|
+
if (!this.autoPurge || this.ttlSeconds <= 0)
|
|
314
|
+
return;
|
|
315
|
+
const now = Date.now();
|
|
316
|
+
if (now - this.lastAutoPurge < AUTO_PURGE_EVERY_MS)
|
|
317
|
+
return;
|
|
318
|
+
this.lastAutoPurge = now;
|
|
319
|
+
this.purgeExpired(now).catch(() => {
|
|
320
|
+
// Retried after the next run to end; a purge that cannot run must not fail a finished turn.
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
//# sourceMappingURL=lucid-token-stream-sink.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lucid-token-stream-sink.js","sourceRoot":"","sources":["../../src/lucid-token-stream-sink.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { RolesPolicy } from '../spi/roles-policy.js';
|
|
2
|
+
import type { EmptyRoles } from '../tool-registry.js';
|
|
2
3
|
import type { McpAuth, McpAuthFactory } from './auth.js';
|
|
3
4
|
import { anyOf, apiKeyAuth, authKitAuth, McpAuthError } from './auth.js';
|
|
4
5
|
/**
|
|
@@ -58,6 +59,12 @@ export interface McpConfig {
|
|
|
58
59
|
authorizer?: RolesPolicy;
|
|
59
60
|
/** Roles a tool requires when it declares none. Defaults to `['ADMIN']`. */
|
|
60
61
|
defaultRoles?: string[];
|
|
62
|
+
/**
|
|
63
|
+
* What an empty roles list means to the default authorizer: `'allow'` (default) — no restriction;
|
|
64
|
+
* `'deny'` — nobody, so a tool needs `roles` (or a non-empty `defaultRoles`) to be reachable. Only
|
|
65
|
+
* consulted when no `authorizer` is set.
|
|
66
|
+
*/
|
|
67
|
+
emptyRoles?: EmptyRoles;
|
|
61
68
|
/**
|
|
62
69
|
* Restrict the exposed tools to this allow-list of names. Omit → all tools the acting actor's roles
|
|
63
70
|
* permit are exposed.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define_config.d.ts","sourceRoot":"","sources":["../../../src/mcp/define_config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEzE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,SAAS;IACxB,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,IAAI,CAAC,EAAE,OAAO,GAAG,cAAc,CAAC;IAChC;;;;OAIG;IACH,KAAK,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7D;;;OAGG;IACH,UAAU,CAAC,EAAE,WAAW,CAAC;IACzB,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,iEAAiE;AACjE,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,CAE5D;AAED,YAAY,EACV,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EACpB,YAAY,EACZ,qBAAqB,EACrB,OAAO,EACP,cAAc,EACd,cAAc,EACd,gBAAgB,GACjB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC"}
|
|
1
|
+
{"version":3,"file":"define_config.d.ts","sourceRoot":"","sources":["../../../src/mcp/define_config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEzE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,SAAS;IACxB,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,IAAI,CAAC,EAAE,OAAO,GAAG,cAAc,CAAC;IAChC;;;;OAIG;IACH,KAAK,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7D;;;OAGG;IACH,UAAU,CAAC,EAAE,WAAW,CAAC;IACzB,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB;;;;OAIG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,iEAAiE;AACjE,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,CAE5D;AAED,YAAY,EACV,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EACpB,YAAY,EACZ,qBAAqB,EACrB,OAAO,EACP,cAAc,EACd,cAAc,EACd,gBAAgB,GACjB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define_config.js","sourceRoot":"","sources":["../../../src/mcp/define_config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"define_config.js","sourceRoot":"","sources":["../../../src/mcp/define_config.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAoEzE,iEAAiE;AACjE,MAAM,UAAU,eAAe,CAAC,MAAiB;IAC/C,OAAO,MAAM,CAAC;AAChB,CAAC;AAeD,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** One confirmation being spent — what {@link ConfirmTokenStore.claim} records. */
|
|
2
|
+
export interface ConfirmTokenClaim {
|
|
3
|
+
/** SHA-256 (hex) of the token. The token itself is never stored. */
|
|
4
|
+
hash: string;
|
|
5
|
+
/** `ctx.actor.id` of the confirming actor. */
|
|
6
|
+
actorRef: string;
|
|
7
|
+
/** The tool the token was issued for. */
|
|
8
|
+
tool: string;
|
|
9
|
+
/** Epoch-ms the token stops being valid — after it the mark is dead weight and can be purged. */
|
|
10
|
+
expiresAt: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Makes a confirm token single use (see `defineConfirmedTool`). A signed token is stateless, so on
|
|
14
|
+
* its own it stays valid until it expires: a double tap, a network retry or a model repeating the
|
|
15
|
+
* call would commit twice inside that window. The store remembers which tokens were spent.
|
|
16
|
+
*
|
|
17
|
+
* Only the token's hash, the actor and the tool name are kept — never an argument or a value.
|
|
18
|
+
*/
|
|
19
|
+
export interface ConfirmTokenStore {
|
|
20
|
+
/**
|
|
21
|
+
* Spend the token. `true` = this caller holds it and may commit; `false` = it was already spent.
|
|
22
|
+
* MUST be atomic: of two concurrent claims for one hash, exactly one gets `true`.
|
|
23
|
+
*/
|
|
24
|
+
claim(input: ConfirmTokenClaim): Promise<boolean>;
|
|
25
|
+
/** Give the token back — the commit failed after the claim, so the caller may try again. */
|
|
26
|
+
release(hash: string): Promise<void>;
|
|
27
|
+
/** Drop the marks of tokens that expired before `now` (epoch-ms, default the clock). Returns how many. */
|
|
28
|
+
purgeExpired?(now?: number): Promise<number>;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=confirm-token-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confirm-token-store.d.ts","sourceRoot":"","sources":["../../../src/spi/confirm-token-store.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,MAAM,WAAW,iBAAiB;IAChC,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,iGAAiG;IACjG,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,KAAK,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClD,4FAA4F;IAC5F,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,0GAA0G;IAC1G,YAAY,CAAC,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC9C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confirm-token-store.js","sourceRoot":"","sources":["../../../src/spi/confirm-token-store.ts"],"names":[],"mappings":""}
|
|
@@ -83,6 +83,12 @@ export interface SinkWriter {
|
|
|
83
83
|
write(frame: StreamFrame): void | Promise<void>;
|
|
84
84
|
/** Mark the run's stream finished (no more frames). */
|
|
85
85
|
end(): void | Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* OPTIONAL: write out anything this writer is still holding back, without ending the stream. Only
|
|
88
|
+
* a sink that gathers frames before writing them (the Lucid sink coalesces text) has anything to
|
|
89
|
+
* do here; {@link childSinkWriter} calls it where a delegated run would have ended the stream.
|
|
90
|
+
*/
|
|
91
|
+
flush?(): void | Promise<void>;
|
|
86
92
|
}
|
|
87
93
|
export interface TokenStreamSink {
|
|
88
94
|
/** Open (or reopen) the writer for a run. */
|
|
@@ -106,7 +112,9 @@ export interface TokenStreamSink {
|
|
|
106
112
|
* Forwarding is what makes a sub-agent visible at all: its own runId is not a stream anyone
|
|
107
113
|
* subscribed to, so a question set it parks on would otherwise be written where nobody reads it.
|
|
108
114
|
* The ancestor owns the stream's lifecycle across however many delegations it spans, so the child's
|
|
109
|
-
* `end()` is a no-op — ending the shared stream mid-parent-run would cut the human off.
|
|
115
|
+
* `end()` is a no-op — ending the shared stream mid-parent-run would cut the human off. It does
|
|
116
|
+
* {@link SinkWriter.flush} what the child wrote, so nothing of the delegate's is still held back when
|
|
117
|
+
* its parent (possibly on another replica) writes the next frame.
|
|
110
118
|
*/
|
|
111
119
|
export declare function childSinkWriter(inner: SinkWriter): SinkWriter;
|
|
112
120
|
//# sourceMappingURL=token-stream-sink.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-stream-sink.d.ts","sourceRoot":"","sources":["../../../src/spi/token-stream-sink.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAElF;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GACnB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE;AAC1B;;;;GAIG;GACD;IACE,CAAC,EAAE,WAAW,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AACH;;;;GAIG;GACD;IAAE,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,gBAAgB,CAAA;CAAE;AACzC;;GAEG;GACD;IAAE,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,oBAAoB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;AAC7D;;;;;;;;;GASG;GACD;IAAE,CAAC,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,kBAAkB,CAAA;CAAE;AAC9E;;;;;;;;GAQG;GACD;IACE,CAAC,EAAE,UAAU,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,0GAA0G;IAC1G,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEN,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,uDAAuD;IACvD,GAAG,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"token-stream-sink.d.ts","sourceRoot":"","sources":["../../../src/spi/token-stream-sink.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAElF;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GACnB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE;AAC1B;;;;GAIG;GACD;IACE,CAAC,EAAE,WAAW,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AACH;;;;GAIG;GACD;IAAE,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,gBAAgB,CAAA;CAAE;AACzC;;GAEG;GACD;IAAE,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,oBAAoB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;AAC7D;;;;;;;;;GASG;GACD;IAAE,CAAC,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,kBAAkB,CAAA;CAAE;AAC9E;;;;;;;;GAQG;GACD;IACE,CAAC,EAAE,UAAU,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,0GAA0G;IAC1G,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEN,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,uDAAuD;IACvD,GAAG,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B;;;;OAIG;IACH,KAAK,CAAC,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC9B,6CAA6C;IAC7C,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACtD,8EAA8E;IAC9E,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;IACrD,6CAA6C;IAC7C,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C;;;;;OAKG;IACH,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACjD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,CAM7D"}
|
|
@@ -5,14 +5,15 @@
|
|
|
5
5
|
* Forwarding is what makes a sub-agent visible at all: its own runId is not a stream anyone
|
|
6
6
|
* subscribed to, so a question set it parks on would otherwise be written where nobody reads it.
|
|
7
7
|
* The ancestor owns the stream's lifecycle across however many delegations it spans, so the child's
|
|
8
|
-
* `end()` is a no-op — ending the shared stream mid-parent-run would cut the human off.
|
|
8
|
+
* `end()` is a no-op — ending the shared stream mid-parent-run would cut the human off. It does
|
|
9
|
+
* {@link SinkWriter.flush} what the child wrote, so nothing of the delegate's is still held back when
|
|
10
|
+
* its parent (possibly on another replica) writes the next frame.
|
|
9
11
|
*/
|
|
10
12
|
export function childSinkWriter(inner) {
|
|
11
13
|
return {
|
|
12
14
|
write: (chunk) => inner.write(chunk),
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
},
|
|
15
|
+
// The top-level run owns end().
|
|
16
|
+
end: () => inner.flush?.(),
|
|
16
17
|
};
|
|
17
18
|
}
|
|
18
19
|
//# sourceMappingURL=token-stream-sink.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-stream-sink.js","sourceRoot":"","sources":["../../../src/spi/token-stream-sink.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"token-stream-sink.js","sourceRoot":"","sources":["../../../src/spi/token-stream-sink.ts"],"names":[],"mappings":"AAiGA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAAC,KAAiB;IAC/C,OAAO;QACL,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC;QACpC,gCAAgC;QAChC,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE;KAC3B,CAAC;AACJ,CAAC"}
|