@ixo/oracle-runtime-workers 0.12.0 → 0.15.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/README.md +32 -10
- package/package.json +14 -9
- package/src/artifacts/config.ts +158 -0
- package/src/artifacts/crypto.ts +94 -0
- package/src/artifacts/routes.ts +73 -0
- package/src/artifacts/store.ts +238 -0
- package/src/artifacts/sweep.ts +200 -0
- package/src/artifacts/tool.ts +103 -0
- package/src/artifacts/viewer-page.ts +614 -0
- package/src/attachments/download.ts +168 -45
- package/src/attachments/index.ts +9 -0
- package/src/attachments/magic.ts +33 -0
- package/src/attachments/pipeline.ts +105 -49
- package/src/attachments/sandbox-archive.ts +9 -8
- package/src/attachments/view-cache.ts +142 -0
- package/src/attachments/view.ts +53 -12
- package/src/channels/auth.ts +267 -0
- package/src/channels/contract.ts +137 -0
- package/src/channels/turns.ts +270 -0
- package/src/core/budgeted-llm.ts +223 -0
- package/src/core/capability-router.ts +273 -0
- package/src/core/context-budget.ts +97 -10
- package/src/core/env.ts +111 -0
- package/src/core/execution-profile.ts +17 -0
- package/src/core/index.ts +267 -9
- package/src/core/llm.ts +30 -9
- package/src/core/main-agent-types.ts +41 -1
- package/src/core/main-agent.ts +367 -65
- package/src/core/manifest.ts +34 -0
- package/src/core/meta-tools.ts +128 -18
- package/src/core/middlewares/capability-gate.ts +130 -12
- package/src/core/middlewares/context-guard.ts +55 -24
- package/src/core/middlewares/index.ts +20 -2
- package/src/core/middlewares/page-context.ts +46 -2
- package/src/core/middlewares/return-direct-first.ts +47 -0
- package/src/core/middlewares/summarization.ts +152 -10
- package/src/core/middlewares/tool-execution.ts +323 -0
- package/src/core/middlewares/tool-repetition-guard.ts +202 -35
- package/src/core/middlewares/turn-boundary.ts +162 -0
- package/src/core/openrouter-pricing.ts +155 -22
- package/src/core/prompt-composer.ts +112 -7
- package/src/core/registries.ts +253 -48
- package/src/core/request-admission.ts +159 -0
- package/src/core/runtime-context.ts +107 -8
- package/src/core/state.ts +1 -1
- package/src/core/sub-agent-fallback.ts +21 -2
- package/src/core/subagent-as-tool.ts +78 -43
- package/src/core/tool-access.ts +189 -0
- package/src/core/tool-scheduler.ts +125 -0
- package/src/core/turn-budget.ts +205 -0
- package/src/core/turn-time-note.ts +43 -0
- package/src/core/user-kv.ts +446 -0
- package/src/core/wrap-plugin-tool.ts +20 -0
- package/src/delivery/plan.ts +278 -0
- package/src/delivery/profile.ts +161 -0
- package/src/delivery/prompt.ts +27 -0
- package/src/delivery/schema.ts +61 -0
- package/src/delivery/shaper.ts +389 -0
- package/src/delivery/types.ts +84 -0
- package/src/do/ambient.ts +51 -15
- package/src/do/contracts.ts +198 -6
- package/src/do/idle-eviction.ts +38 -0
- package/src/do/matrix-turn-ledger.ts +24 -5
- package/src/do/result-store-test-do.ts +137 -3
- package/src/do/result-store.ts +239 -58
- package/src/do/room-mirror.ts +29 -0
- package/src/do/run-buffer.ts +64 -9
- package/src/do/run-coordinator.ts +451 -76
- package/src/do/run-request.ts +2 -0
- package/src/do/run-store-test-do.ts +301 -1
- package/src/do/run-store.ts +385 -43
- package/src/do/secrets-adapter.ts +22 -3
- package/src/do/sse-stream.ts +60 -9
- package/src/do/transcript.ts +48 -5
- package/src/do/turn-body.ts +105 -5
- package/src/do/ucan-service.ts +204 -34
- package/src/do/user-oracle-do.ts +1883 -264
- package/src/feedback/config.ts +127 -0
- package/src/feedback/contract.ts +148 -0
- package/src/feedback/linear-sink.ts +285 -0
- package/src/feedback/privacy.ts +150 -0
- package/src/feedback/reservation.ts +298 -0
- package/src/feedback/submit.ts +247 -0
- package/src/index.ts +138 -6
- package/src/llm/byo-adapter.ts +27 -6
- package/src/llm/byo-catalog.ts +13 -0
- package/src/llm/byo-model-fallback.ts +248 -0
- package/src/llm/byo-service.ts +45 -8
- package/src/llm/provider-error.ts +139 -2
- package/src/matrix/gateway-do.ts +523 -111
- package/src/matrix/group-chat.ts +66 -23
- package/src/matrix/inbox-store.ts +16 -1
- package/src/matrix/ingest.ts +79 -14
- package/src/matrix/media-deadline.ts +130 -0
- package/src/matrix/replay-format.ts +5 -3
- package/src/matrix/reply-parts.ts +92 -0
- package/src/matrix/room-alias.ts +96 -0
- package/src/matrix/user-homeserver.ts +166 -7
- package/src/owner-store/ixo-vfs-store.ts +170 -43
- package/src/owner-store/migrating-store.ts +31 -15
- package/src/owner-store/types.ts +18 -3
- package/src/plugin-api/index.ts +43 -2
- package/src/plugin-api/oracle-plugin.ts +35 -0
- package/src/plugin-api/request-admission.ts +41 -0
- package/src/plugin-api/tool-helper.ts +25 -1
- package/src/plugin-api/tool-plane.ts +92 -0
- package/src/plugin-api/types.ts +194 -6
- package/src/plugins/agui/agui.plugin.ts +31 -16
- package/src/plugins/composio/composio-tools.ts +57 -6
- package/src/plugins/composio/composio-ucan.ts +3 -2
- package/src/plugins/composio/composio.plugin.ts +2 -0
- package/src/plugins/delegated-capabilities.ts +25 -0
- package/src/plugins/editor/content-session.ts +173 -83
- package/src/plugins/editor/content-tools.ts +76 -17
- package/src/plugins/editor/editor-mx.ts +28 -9
- package/src/plugins/editor/editor.plugin.ts +3 -1
- package/src/plugins/editor/failures.ts +28 -7
- package/src/plugins/editor/prompts.ts +2 -1
- package/src/plugins/editor/provider.ts +553 -40
- package/src/plugins/editor/standalone-editor-tool.ts +61 -18
- package/src/plugins/firecrawl/firecrawl-tools.ts +13 -5
- package/src/plugins/flows/action-metadata.ts +111 -11
- package/src/plugins/flows/actions.ts +18 -0
- package/src/plugins/flows/decision-context.ts +31 -0
- package/src/plugins/flows/edit.ts +223 -52
- package/src/plugins/flows/errors.ts +22 -1
- package/src/plugins/flows/flow-doc.ts +92 -9
- package/src/plugins/flows/flows.plugin.ts +11 -5
- package/src/plugins/flows/forms.ts +34 -0
- package/src/plugins/flows/index.ts +2 -0
- package/src/plugins/flows/input-policy.ts +140 -0
- package/src/plugins/flows/prompts.ts +14 -11
- package/src/plugins/flows/read.ts +49 -9
- package/src/plugins/flows/ref-syntax.ts +129 -0
- package/src/plugins/flows/test-homeserver.ts +245 -0
- package/src/plugins/flows/tools/authoring.ts +216 -112
- package/src/plugins/flows/tools/discovery.ts +4 -1
- package/src/plugins/flows/tools/forms.ts +21 -2
- package/src/plugins/flows/tools/settings.ts +34 -13
- package/src/plugins/flows/translator.ts +13 -10
- package/src/plugins/flows/types.ts +167 -42
- package/src/plugins/index.ts +11 -1
- package/src/plugins/ixo-transaction/chain-ids.ts +43 -0
- package/src/plugins/ixo-transaction/index.ts +2 -0
- package/src/plugins/ixo-transaction/ixo-transaction.plugin.ts +119 -0
- package/src/plugins/ixo-transaction/receipts.ts +99 -0
- package/src/plugins/ixo-transaction/tools.ts +329 -0
- package/src/plugins/ixo-transaction/wallet-signing.ts +202 -0
- package/src/plugins/mcp-tool-adapter.ts +23 -2
- package/src/plugins/memory/memory-tools.ts +7 -2
- package/src/plugins/memory/memory-ucan.ts +3 -2
- package/src/plugins/memory/memory.plugin.ts +2 -0
- package/src/plugins/pod-creator/blueprint-store.ts +163 -0
- package/src/plugins/pod-creator/blueprint-types.ts +61 -0
- package/src/plugins/pod-creator/bounded-map.ts +87 -0
- package/src/plugins/pod-creator/capsule-content-client.ts +285 -0
- package/src/plugins/pod-creator/chain-gateway.ts +110 -0
- package/src/plugins/pod-creator/config.ts +85 -0
- package/src/plugins/pod-creator/create-session-store.ts +180 -0
- package/src/plugins/pod-creator/create-tools.ts +538 -0
- package/src/plugins/pod-creator/design-pod-roles.ts +111 -0
- package/src/plugins/pod-creator/index.ts +65 -0
- package/src/plugins/pod-creator/orchestration-tools.ts +172 -0
- package/src/plugins/pod-creator/pod-creator.plugin.ts +190 -0
- package/src/plugins/pod-creator/stage.ts +201 -0
- package/src/plugins/pod-creator/sub-agents.ts +189 -0
- package/src/plugins/portal/action-log.ts +21 -1
- package/src/plugins/portal/declared-tools.ts +90 -0
- package/src/plugins/portal/portal.plugin.ts +42 -16
- package/src/plugins/sandbox/sandbox-bridge.ts +79 -10
- package/src/plugins/sandbox/sandbox-mcp.ts +3 -2
- package/src/plugins/sandbox/sandbox-write-blob.ts +11 -1
- package/src/plugins/sandbox/sandbox.plugin.ts +8 -16
- package/src/plugins/tasks/manifest.ts +1 -1
- package/src/plugins/tasks/middleware.ts +20 -7
- package/src/plugins/tasks/tasks-tools.ts +30 -3
- package/src/plugins/tasks/tasks.plugin.ts +64 -11
- package/src/plugins/user-preferences/schema.ts +12 -1
- package/src/plugins/user-preferences/user-preferences-store.ts +38 -13
- package/src/plugins/user-preferences/user-preferences-tool.ts +3 -2
- package/src/plugins/vfs/vfs-client.ts +250 -71
- package/src/plugins/vfs/vfs-content.ts +30 -11
- package/src/plugins/vfs/vfs-errors.ts +22 -0
- package/src/plugins/vfs/vfs-sandbox-tools.ts +12 -5
- package/src/plugins/vfs/vfs-tools.ts +60 -20
- package/src/realtime/frontend-call-registry.ts +228 -81
- package/src/realtime/realtime-endpoint.ts +215 -44
- package/src/realtime/session-socket-hub.ts +47 -2
- package/src/secrets/secrets-service.ts +9 -3
- package/src/shell/app.ts +529 -82
- package/src/shell/auth.ts +131 -37
- package/src/shell/turn-body-cap.ts +11 -13
- package/src/sqlite/blob-compactor.ts +61 -28
- package/src/sqlite/database.ts +156 -90
- package/src/sqlite/do-vfs.ts +416 -129
- package/src/sqlite/page-tier.ts +133 -40
- package/src/sqlite/serialization.ts +40 -0
- package/src/sqlite/sessions-store.ts +10 -2
- package/src/sqlite/sqlite-saver.ts +139 -19
- package/src/sqlite/user-kv-store.ts +317 -0
- package/src/tasks/schedule.ts +201 -12
- package/src/tasks/scheduler.ts +586 -98
- package/src/tasks/spec.ts +67 -3
- package/src/tasks/store.ts +292 -27
- package/src/tasks/topic-deliverables.ts +70 -0
package/src/do/contracts.ts
CHANGED
|
@@ -1,8 +1,26 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
TopicDeliverableCommand,
|
|
3
|
+
TopicDeliverableResult,
|
|
4
|
+
} from '../tasks/topic-deliverables';
|
|
5
|
+
import type {
|
|
6
|
+
ChannelIdentity,
|
|
7
|
+
ChannelTurnInput,
|
|
8
|
+
ChannelTurnOutcome,
|
|
9
|
+
} from '../channels/contract';
|
|
10
|
+
import type { TaskExecutionProfile } from '../core/execution-profile';
|
|
11
|
+
import type { CapabilityRouterMode } from '@ixo/common/ai/decisions';
|
|
1
12
|
import type { TranscriptPageOptions } from './transcript';
|
|
13
|
+
import type { TurnUsage } from '../core/turn-budget';
|
|
2
14
|
import type { BotStatus, EncryptedFileInfo } from '@ixo/matrix-bot-workers-sdk';
|
|
3
15
|
import type { AttachmentInput } from '../attachments/types';
|
|
4
16
|
import type { RealtimeStatus } from '../realtime/realtime-endpoint';
|
|
5
17
|
import type { TierFlushResult, TierStatus } from '../sqlite/do-vfs';
|
|
18
|
+
import type { OwnedArtifact } from '../artifacts/store';
|
|
19
|
+
import type {
|
|
20
|
+
FeedbackReservation,
|
|
21
|
+
FeedbackSettlement,
|
|
22
|
+
FeedbackTarget,
|
|
23
|
+
} from '../feedback/contract';
|
|
6
24
|
/**
|
|
7
25
|
* Cross-object contracts for the Workers runtime.
|
|
8
26
|
*
|
|
@@ -25,6 +43,7 @@ import type { TierFlushResult, TierStatus } from '../sqlite/do-vfs';
|
|
|
25
43
|
|
|
26
44
|
/** Bindings every oracle Worker must declare (see the example `wrangler.jsonc`). */
|
|
27
45
|
export interface OracleWorkerEnv {
|
|
46
|
+
TOPIC_DELIVERABLES_ENABLED?: string;
|
|
28
47
|
USER_ORACLE: DurableObjectNamespace<UserOracleObject>;
|
|
29
48
|
MATRIX_GATEWAY: DurableObjectNamespace<MatrixGatewayObject>;
|
|
30
49
|
|
|
@@ -40,6 +59,12 @@ export interface OracleWorkerEnv {
|
|
|
40
59
|
/** Blocksync GraphQL endpoint used to resolve `did:ixo` verification keys. */
|
|
41
60
|
BLOCKSYNC_GRAPHQL_URL: string;
|
|
42
61
|
UCAN_AUTH_MAX_TTL_SECONDS?: string;
|
|
62
|
+
/** `true` restores the legacy bare-delegation authentication (off by default). */
|
|
63
|
+
UCAN_ALLOW_BARE_DELEGATION_AUTH?: string;
|
|
64
|
+
CHANNEL_SERVICE_DID?: string;
|
|
65
|
+
AUTH_HUB?: Fetcher;
|
|
66
|
+
AUTH_HUB_URL?: string;
|
|
67
|
+
AUTH_HUB_CHANNEL_SERVICE_KEY?: string;
|
|
43
68
|
|
|
44
69
|
// --- matrix -------------------------------------------------------------
|
|
45
70
|
MATRIX_BASE_URL: string;
|
|
@@ -73,6 +98,34 @@ export interface OracleWorkerEnv {
|
|
|
73
98
|
MATRIX_HOT_ROOMS?: string;
|
|
74
99
|
/** Cap on events replayed per room after a restart (SDK default 0 = unbounded). */
|
|
75
100
|
MATRIX_BACKFILL_MAX_EVENTS?: string;
|
|
101
|
+
/**
|
|
102
|
+
* Gateway memory guards (`@ixo/matrix-bot-workers-sdk` 0.5+): events per
|
|
103
|
+
* room per `/sync` batch (default 30), the largest `/sync` response held
|
|
104
|
+
* in memory (default 16 MiB; over it the batch is re-requested at timeline
|
|
105
|
+
* limit 1 and the rooms catch up through `/messages`), events per catch-up
|
|
106
|
+
* page (default 100), the largest catch-up page (default 10 MiB; a bigger
|
|
107
|
+
* one is fetched again smaller) and the largest ciphertext the bot will
|
|
108
|
+
* decrypt (default 0 = no cap). A cap of `0` switches that guard off.
|
|
109
|
+
*/
|
|
110
|
+
MATRIX_SYNC_TIMELINE_LIMIT?: string;
|
|
111
|
+
MATRIX_SYNC_BYTES_CAP?: string;
|
|
112
|
+
MATRIX_CATCHUP_PAGE_SIZE?: string;
|
|
113
|
+
MATRIX_PAGE_BYTES_CAP?: string;
|
|
114
|
+
MATRIX_MAX_DECRYPT_BYTES?: string;
|
|
115
|
+
/**
|
|
116
|
+
* `true` restores every room key the account's key backup holds on a
|
|
117
|
+
* device's first start (SDK 0.6+), capped at `..._MAX_KEYS` (default
|
|
118
|
+
* 5,000; a larger backup is skipped and keys stay on demand). Off by
|
|
119
|
+
* default: the gateway reads keys from the backup one session at a time.
|
|
120
|
+
*/
|
|
121
|
+
MATRIX_BACKUP_BULK_RESTORE?: string;
|
|
122
|
+
MATRIX_BACKUP_BULK_RESTORE_MAX_KEYS?: string;
|
|
123
|
+
/**
|
|
124
|
+
* Keep-alive fuse (SDK 0.7+): while the gateway object works, its alarm is
|
|
125
|
+
* re-armed this far ahead so a host drain brings it back within the fuse
|
|
126
|
+
* (default 10 s, floor 2 s, `0` = off).
|
|
127
|
+
*/
|
|
128
|
+
MATRIX_KEEPALIVE_FUSE_MS?: string;
|
|
76
129
|
|
|
77
130
|
// --- storage ------------------------------------------------------------
|
|
78
131
|
/**
|
|
@@ -103,6 +156,22 @@ export interface OracleWorkerEnv {
|
|
|
103
156
|
TIER_EVICT_AFTER_PERIODS?: string;
|
|
104
157
|
/** Length of one access-tracking period in ms (default one day; tests shorten it). */
|
|
105
158
|
TIER_PERIOD_MS?: string;
|
|
159
|
+
/**
|
|
160
|
+
* Artefacts (chat delivery): long replies on chat surfaces become documents
|
|
161
|
+
* behind a link. The R2 bucket holds only ciphertext share copies; absent
|
|
162
|
+
* bucket = artefacts off; a bucket without a public origin keeps stored
|
|
163
|
+
* artefacts readable and revocable but makes no new ones. Long replies are
|
|
164
|
+
* then split into messages instead.
|
|
165
|
+
*/
|
|
166
|
+
ARTIFACT_BUCKET?: R2Bucket;
|
|
167
|
+
/** The oracle Worker's public origin; artefact links point at `/a/:id` under it. */
|
|
168
|
+
ORACLE_PUBLIC_URL?: string;
|
|
169
|
+
/** A shared viewer page (Qi.Space). Unset = the oracle's own `/a/:id` page. */
|
|
170
|
+
ARTIFACT_VIEWER_URL?: string;
|
|
171
|
+
/** Days an artefact link stays valid (default 30). */
|
|
172
|
+
ARTIFACT_LINK_TTL_DAYS?: string;
|
|
173
|
+
/** Hours between the cron's sweeps of expired share copies (default 24, 1–168). */
|
|
174
|
+
ARTIFACT_SWEEP_INTERVAL_HOURS?: string;
|
|
106
175
|
/** IXO VFS worker base URL when `OWNER_STORE=vfs` (defaults per NETWORK). */
|
|
107
176
|
VFS_BASE_URL?: string;
|
|
108
177
|
/** UCAN store worker base URL when `OWNER_STORE=vfs` (defaults per NETWORK). */
|
|
@@ -121,6 +190,36 @@ export interface OracleWorkerEnv {
|
|
|
121
190
|
* local harness and vitest pool, where the shell degrades to no limiting.
|
|
122
191
|
*/
|
|
123
192
|
RATE_LIMIT?: RateLimit;
|
|
193
|
+
/**
|
|
194
|
+
* Optional rate-limit binding for anonymous feedback, keyed per client IP
|
|
195
|
+
* pseudonym (declare it with `simple: { limit: 3, period: 60 }`). Without
|
|
196
|
+
* it the per-IP check uses `RATE_LIMIT`'s limit under a feedback-only key.
|
|
197
|
+
*/
|
|
198
|
+
FEEDBACK_RATE_LIMIT?: RateLimit;
|
|
199
|
+
|
|
200
|
+
// --- anonymous response feedback (docs/configuration.md) ------------------
|
|
201
|
+
// Read by the shell only (`src/feedback/config.ts`); never part of the
|
|
202
|
+
// validated config plugins receive. Off unless the key and secret are set.
|
|
203
|
+
/** Linear API key allowed to create issues in the feedback team. */
|
|
204
|
+
FEEDBACK_LINEAR_API_KEY?: string;
|
|
205
|
+
/** At least 32 characters; derives the user/session/message pseudonyms. */
|
|
206
|
+
FEEDBACK_HMAC_SECRET?: string;
|
|
207
|
+
/** Linear team (default: Studio). */
|
|
208
|
+
FEEDBACK_LINEAR_TEAM_ID?: string;
|
|
209
|
+
/** Linear project (default: "User Feedback from Portal"). */
|
|
210
|
+
FEEDBACK_LINEAR_PROJECT_ID?: string;
|
|
211
|
+
/** Optional comma-separated Linear label ids. */
|
|
212
|
+
FEEDBACK_LINEAR_LABEL_IDS?: string;
|
|
213
|
+
/** Linear GraphQL endpoint override (default `https://api.linear.app/graphql`). */
|
|
214
|
+
FEEDBACK_LINEAR_API_URL?: string;
|
|
215
|
+
/** Release or commit identifier shown in feedback issues (default `unknown`). */
|
|
216
|
+
QIFORGE_BUILD_VERSION?: string;
|
|
217
|
+
/**
|
|
218
|
+
* Workers AI binding. Optional: `DECISION_PROVIDER=cloudflare-jev` runs the
|
|
219
|
+
* Jev model through it instead of the account credentials. Declare it with
|
|
220
|
+
* `"ai": { "binding": "AI" }` in the wrangler config.
|
|
221
|
+
*/
|
|
222
|
+
AI?: Ai;
|
|
124
223
|
|
|
125
224
|
// --- llm ----------------------------------------------------------------
|
|
126
225
|
OPEN_ROUTER_API_KEY: string;
|
|
@@ -130,6 +229,15 @@ export interface OracleWorkerEnv {
|
|
|
130
229
|
MAIN_REASONING_EFFORT?: string;
|
|
131
230
|
/** LangGraph steps one turn may take before `GraphRecursionError` (default 600; Node hard-codes 200). */
|
|
132
231
|
TURN_RECURSION_LIMIT?: string;
|
|
232
|
+
/** Turn budget: cumulative model tokens (default 500000), tool attempts (120), deadline in ms (600000). */
|
|
233
|
+
TURN_MAX_TOKENS?: string;
|
|
234
|
+
TURN_MAX_TOOL_CALLS?: string;
|
|
235
|
+
TURN_TIMEOUT_MS?: string;
|
|
236
|
+
/** Repetition guard: identical successful writes (default 1) and reads (5) per turn. */
|
|
237
|
+
TURN_MAX_IDENTICAL_WRITES?: string;
|
|
238
|
+
TURN_MAX_IDENTICAL_READS?: string;
|
|
239
|
+
/** Time limit per request-admission handler in ms (default 2000). */
|
|
240
|
+
REQUEST_ADMISSION_TIMEOUT_MS?: string;
|
|
133
241
|
/** Durable-run knobs (docs/plans/durable-runs.md); see `runDurabilityConfig`. */
|
|
134
242
|
RUN_KEEPALIVE_MS?: string;
|
|
135
243
|
RUN_SEGMENT_FLUSH_MS?: string;
|
|
@@ -155,6 +263,22 @@ export interface OracleWorkerEnv {
|
|
|
155
263
|
/** Nebius Token Factory API key — required when `LLM_PROVIDER=nebius`. */
|
|
156
264
|
NEBIUS_API_KEY?: string;
|
|
157
265
|
|
|
266
|
+
// --- decisions ----------------------------------------------------------
|
|
267
|
+
/** Bounded semantic Decision provider; unset leaves Decisions unconfigured. `openrouter-jev` reuses `OPEN_ROUTER_API_KEY`. */
|
|
268
|
+
DECISION_PROVIDER?: 'cloudflare-jev' | 'openrouter-jev';
|
|
269
|
+
/** Jev model id override (`typesafe/jev` on Workers AI, `typesafe/jev-1.13` on OpenRouter). */
|
|
270
|
+
DECISION_MODEL?: string;
|
|
271
|
+
/** Cloudflare account for `cloudflare-jev` over the REST API; not needed when the `AI` binding is declared. */
|
|
272
|
+
CLOUDFLARE_ACCOUNT_ID?: string;
|
|
273
|
+
/** Workers AI token for `cloudflare-jev` over the REST API; not needed when the `AI` binding is declared. */
|
|
274
|
+
CLOUDFLARE_API_TOKEN?: string;
|
|
275
|
+
/**
|
|
276
|
+
* Capability router (needs a `DECISION_PROVIDER`). `off` (default) never
|
|
277
|
+
* evaluates; `shadow` evaluates and logs the verdict without preloading;
|
|
278
|
+
* `on` preloads the predicted on-demand plugin's tools for the turn.
|
|
279
|
+
*/
|
|
280
|
+
CAPABILITY_ROUTER?: CapabilityRouterMode;
|
|
281
|
+
|
|
158
282
|
// --- langsmith tracing ---------------------------------------------------
|
|
159
283
|
/** `'true'` traces every turn (explicit tracer — Workers has no env auto-attach). */
|
|
160
284
|
LANGSMITH_TRACING?: string;
|
|
@@ -206,6 +330,7 @@ export type JsonString = string;
|
|
|
206
330
|
|
|
207
331
|
/** Who is talking and through which transport. */
|
|
208
332
|
export interface TurnIdentity {
|
|
333
|
+
channel?: ChannelIdentity;
|
|
209
334
|
/** Validated user DID (from the UCAN signer or the Matrix sender mapping). */
|
|
210
335
|
userDid: string;
|
|
211
336
|
/** `@did-ixo-…:server` when known. */
|
|
@@ -219,10 +344,16 @@ export interface TurnIdentity {
|
|
|
219
344
|
|
|
220
345
|
/** Request the shell / gateway sends to `UserOracleDO` to run one turn. */
|
|
221
346
|
export interface TurnRequest {
|
|
347
|
+
channel?: {
|
|
348
|
+
provider: 'whatsapp';
|
|
349
|
+
bindingId: string;
|
|
350
|
+
remoteMessageRef: string;
|
|
351
|
+
};
|
|
352
|
+
executionProfile?: TaskExecutionProfile;
|
|
222
353
|
identity: TurnIdentity;
|
|
223
354
|
sessionId: string;
|
|
224
355
|
message: string;
|
|
225
|
-
client: 'portal' | 'matrix';
|
|
356
|
+
client: 'portal' | 'matrix' | 'channel';
|
|
226
357
|
/** Matrix room the turn arrived in (Matrix turns) or the user's DM room. */
|
|
227
358
|
roomId?: string;
|
|
228
359
|
/** Thread root when the user replied inside a thread. */
|
|
@@ -314,7 +445,7 @@ export interface RunSummary {
|
|
|
314
445
|
runId: string;
|
|
315
446
|
sessionId: string;
|
|
316
447
|
requestId: string;
|
|
317
|
-
client: 'portal' | 'matrix';
|
|
448
|
+
client: 'portal' | 'matrix' | 'channel';
|
|
318
449
|
status:
|
|
319
450
|
| 'queued'
|
|
320
451
|
| 'running'
|
|
@@ -370,16 +501,29 @@ export interface RunsStatus {
|
|
|
370
501
|
nextAttemptAt: number | null;
|
|
371
502
|
error: string | null;
|
|
372
503
|
taskRunId: string | null;
|
|
504
|
+
/** The turn's budget usage, once the run ended (`TurnUsage`). */
|
|
505
|
+
usage: TurnUsage | null;
|
|
373
506
|
}
|
|
374
507
|
>;
|
|
508
|
+
/** Writes whose outcome is still unknown (tool-execution.ts); never their arguments. */
|
|
509
|
+
claims: Array<{
|
|
510
|
+
fingerprint: string;
|
|
511
|
+
toolName: string;
|
|
512
|
+
runId: string;
|
|
513
|
+
sessionId: string;
|
|
514
|
+
startedAt: string;
|
|
515
|
+
state: 'pending' | 'warned';
|
|
516
|
+
}>;
|
|
375
517
|
}
|
|
376
518
|
|
|
377
519
|
/** Non-streaming turn result. */
|
|
378
520
|
export interface TurnResult {
|
|
379
521
|
sessionId: string;
|
|
380
522
|
requestId: string;
|
|
381
|
-
/**
|
|
523
|
+
/** The model's final text, on every surface (never a chat rendering). */
|
|
382
524
|
text: string;
|
|
525
|
+
/** Chat-surface turns: the Reply Plan (JSON) to deliver part by part. */
|
|
526
|
+
plan?: JsonString;
|
|
383
527
|
/** Id of the final assistant message, when one was produced. */
|
|
384
528
|
messageId?: string;
|
|
385
529
|
/** Tool calls made during the turn (name + summarized status), for logging. */
|
|
@@ -485,6 +629,16 @@ export interface StorageStatus {
|
|
|
485
629
|
* piped straight to the client.
|
|
486
630
|
*/
|
|
487
631
|
export interface UserOracleObject extends Rpc.DurableObjectBranded {
|
|
632
|
+
topicDeliverable(
|
|
633
|
+
identity: TurnIdentity,
|
|
634
|
+
operationId: string,
|
|
635
|
+
command: TopicDeliverableCommand,
|
|
636
|
+
): Promise<TopicDeliverableResult>;
|
|
637
|
+
channelTurn(
|
|
638
|
+
identity: TurnIdentity,
|
|
639
|
+
input: ChannelTurnInput,
|
|
640
|
+
requestHash: string,
|
|
641
|
+
): Promise<ChannelTurnOutcome>;
|
|
488
642
|
/** Run a turn and return the final text (used by the Matrix gateway). */
|
|
489
643
|
runTurn(req: TurnRequest): Promise<TurnResult>;
|
|
490
644
|
/**
|
|
@@ -503,6 +657,13 @@ export interface UserOracleObject extends Rpc.DurableObjectBranded {
|
|
|
503
657
|
opts?: { limit?: number; offset?: number },
|
|
504
658
|
): Promise<{ sessions: SessionSummary[]; total: number }>;
|
|
505
659
|
deleteSession(identity: TurnIdentity, sessionId: string): Promise<boolean>;
|
|
660
|
+
/** `GET /artifacts/:id` — the owner's canonical copy, or null. */
|
|
661
|
+
artifact(
|
|
662
|
+
identity: TurnIdentity,
|
|
663
|
+
artifactId: string,
|
|
664
|
+
): Promise<OwnedArtifact | null>;
|
|
665
|
+
/** `DELETE /artifacts/:id` — false when there is no such artefact. */
|
|
666
|
+
revokeArtifact(identity: TurnIdentity, artifactId: string): Promise<boolean>;
|
|
506
667
|
/** JSON-encoded `MessageDto[]` for `GET /messages/:sessionId`. */
|
|
507
668
|
listMessages(identity: TurnIdentity, sessionId: string): Promise<JsonString>;
|
|
508
669
|
/**
|
|
@@ -517,6 +678,20 @@ export interface UserOracleObject extends Rpc.DurableObjectBranded {
|
|
|
517
678
|
): Promise<
|
|
518
679
|
{ ok: true; json: JsonString } | { ok: false; status: 400; message: string }
|
|
519
680
|
>;
|
|
681
|
+
/**
|
|
682
|
+
* Anonymous feedback (src/feedback/): validate the target message in this
|
|
683
|
+
* user's database and reserve its idempotency marker. Never sees the text.
|
|
684
|
+
*/
|
|
685
|
+
reserveMessageFeedback(
|
|
686
|
+
identity: TurnIdentity,
|
|
687
|
+
target: FeedbackTarget,
|
|
688
|
+
): Promise<FeedbackReservation>;
|
|
689
|
+
/** Record the delivery, keep the earlier submission's, or release the reservation. */
|
|
690
|
+
settleMessageFeedback(
|
|
691
|
+
identity: TurnIdentity,
|
|
692
|
+
target: FeedbackTarget,
|
|
693
|
+
outcome: FeedbackSettlement,
|
|
694
|
+
): Promise<void>;
|
|
520
695
|
abortTurn(sessionId: string): Promise<boolean>;
|
|
521
696
|
/** The session's active (queued/running/recovering) run, if any. */
|
|
522
697
|
sessionRun(
|
|
@@ -567,9 +742,12 @@ export interface UserOracleObject extends Rpc.DurableObjectBranded {
|
|
|
567
742
|
/**
|
|
568
743
|
* Drop the working copy (pages + caches) so the next access re-imports from
|
|
569
744
|
* the owner store. Simulates eviction/loss of the DO cache in tests and is
|
|
570
|
-
* the "user deleted their file upstream" path.
|
|
745
|
+
* the "user deleted their file upstream" path. `userDid` is the caller the
|
|
746
|
+
* shell authenticated, so an object that never booted boots for it.
|
|
571
747
|
*/
|
|
572
|
-
resetWorkingCopy(
|
|
748
|
+
resetWorkingCopy(
|
|
749
|
+
identity?: TurnIdentity,
|
|
750
|
+
): Promise<{ reloadedFromOwnerStore: boolean }>;
|
|
573
751
|
/** Run one R2 page-tier eviction pass now (debug routes; `force` ignores recency). */
|
|
574
752
|
tierFlush(opts?: {
|
|
575
753
|
force?: boolean;
|
|
@@ -630,6 +808,16 @@ export interface CreateRoomOptions {
|
|
|
630
808
|
userDid: string;
|
|
631
809
|
}
|
|
632
810
|
|
|
811
|
+
/**
|
|
812
|
+
* Deadline for one gateway media download, from opening it to the last byte
|
|
813
|
+
* (the gateway's default applies when omitted). Only the timeout crosses the
|
|
814
|
+
* RPC: an `AbortSignal` cannot, so the caller stops a transfer early by
|
|
815
|
+
* cancelling the returned stream.
|
|
816
|
+
*/
|
|
817
|
+
export interface MediaDownloadDeadline {
|
|
818
|
+
timeoutMs?: number;
|
|
819
|
+
}
|
|
820
|
+
|
|
633
821
|
/**
|
|
634
822
|
* RPC surface of `MatrixGatewayDO`. User objects call it for anything that
|
|
635
823
|
* needs the bot identity (sending, media, room lookups); the shell calls
|
|
@@ -648,9 +836,13 @@ export interface MatrixGatewayObject extends Rpc.DurableObjectBranded {
|
|
|
648
836
|
downloadEventMediaStream(
|
|
649
837
|
roomId: string,
|
|
650
838
|
eventId: string,
|
|
839
|
+
opts?: MediaDownloadDeadline,
|
|
651
840
|
): Promise<MediaStream | null>;
|
|
652
841
|
/** Authenticated media download of an `mxc://` URI, as a stream. */
|
|
653
|
-
downloadMxcMediaStream(
|
|
842
|
+
downloadMxcMediaStream(
|
|
843
|
+
mxc: string,
|
|
844
|
+
opts?: MediaDownloadDeadline,
|
|
845
|
+
): Promise<ReadableStream<Uint8Array>>;
|
|
654
846
|
/** Forcibly reset the gateway object like a platform host drain (debug routes only). The call itself rejects. */
|
|
655
847
|
debugAbortObject(): Promise<void>;
|
|
656
848
|
/** Stop the bot (operator/debug; `ensureStarted`/cron brings it back). */
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dropping an idle user's working copy. The object only drops it when the
|
|
3
|
+
* owner copy upstream is verified current, and that check awaits (the
|
|
4
|
+
* checksum reads the whole file, from R2 for tiered pages), and while an
|
|
5
|
+
* object awaits I/O other than its own storage a request can arrive. So
|
|
6
|
+
* whatever the tick decided before is re-read after the check, and the
|
|
7
|
+
* in-memory handles are dropped before the first await of the wipe: from
|
|
8
|
+
* then on a request boots afresh from the owner copy instead of reaching a
|
|
9
|
+
* database being deleted under it.
|
|
10
|
+
*/
|
|
11
|
+
export interface IdleEvictionSteps {
|
|
12
|
+
/** The owner copy matches the working copy (may await network I/O). */
|
|
13
|
+
ownerCopyIsCurrent(): Promise<boolean>;
|
|
14
|
+
/** Everything that decides idleness, read again: still no activity. */
|
|
15
|
+
stillIdle(): Promise<boolean>;
|
|
16
|
+
/** Forget the in-memory handles. Synchronous, so nothing interleaves. */
|
|
17
|
+
dropHandles(): void;
|
|
18
|
+
/** Delete the working copy. */
|
|
19
|
+
wipe(): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type IdleEvictionOutcome =
|
|
23
|
+
/** The working copy was deleted. */
|
|
24
|
+
| 'evicted'
|
|
25
|
+
/** The owner copy is not verified current: keep the working copy. */
|
|
26
|
+
| 'not-current'
|
|
27
|
+
/** Activity arrived while the owner copy was checked: keep it. */
|
|
28
|
+
| 'active';
|
|
29
|
+
|
|
30
|
+
export async function evictIdleWorkingCopy(
|
|
31
|
+
steps: IdleEvictionSteps,
|
|
32
|
+
): Promise<IdleEvictionOutcome> {
|
|
33
|
+
if (!(await steps.ownerCopyIsCurrent())) return 'not-current';
|
|
34
|
+
if (!(await steps.stillIdle())) return 'active';
|
|
35
|
+
steps.dropHandles();
|
|
36
|
+
await steps.wipe();
|
|
37
|
+
return 'evicted';
|
|
38
|
+
}
|
|
@@ -29,6 +29,8 @@ export interface MatrixTurnRecord {
|
|
|
29
29
|
startedAt: string;
|
|
30
30
|
answeredAt: string | null;
|
|
31
31
|
replyText: string | null;
|
|
32
|
+
/** The Reply Plan (JSON) a chat-style reply was delivered as. */
|
|
33
|
+
replyPlan?: string;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
export type MatrixTurnDecision = 'run' | 'answered' | 'interrupted';
|
|
@@ -50,6 +52,7 @@ type LedgerRow = {
|
|
|
50
52
|
started_at: string;
|
|
51
53
|
answered_at: string | null;
|
|
52
54
|
reply_text: string | null;
|
|
55
|
+
reply_plan: string | null;
|
|
53
56
|
} & Record<string, string | null>;
|
|
54
57
|
|
|
55
58
|
export class MatrixTurnLedger {
|
|
@@ -76,8 +79,14 @@ export class MatrixTurnLedger {
|
|
|
76
79
|
request_id TEXT NOT NULL,
|
|
77
80
|
started_at TEXT NOT NULL,
|
|
78
81
|
answered_at TEXT,
|
|
79
|
-
reply_text TEXT
|
|
82
|
+
reply_text TEXT,
|
|
83
|
+
reply_plan TEXT
|
|
80
84
|
)`);
|
|
85
|
+
const columns = await this.db.exec<{ name: string }>(
|
|
86
|
+
`PRAGMA table_info(matrix_turns)`,
|
|
87
|
+
);
|
|
88
|
+
if (!columns.some((c) => c.name === 'reply_plan'))
|
|
89
|
+
await this.db.run(`ALTER TABLE matrix_turns ADD COLUMN reply_plan TEXT`);
|
|
81
90
|
await this.db.run(`DELETE FROM matrix_turns WHERE started_at < ?`, [
|
|
82
91
|
new Date(this.now() - RETENTION_MS).toISOString(),
|
|
83
92
|
]);
|
|
@@ -86,7 +95,7 @@ export class MatrixTurnLedger {
|
|
|
86
95
|
async get(eventId: string): Promise<MatrixTurnRecord | undefined> {
|
|
87
96
|
await this.setup();
|
|
88
97
|
const row = await this.db.get<LedgerRow>(
|
|
89
|
-
`SELECT event_id, session_id, request_id, started_at, answered_at, reply_text
|
|
98
|
+
`SELECT event_id, session_id, request_id, started_at, answered_at, reply_text, reply_plan
|
|
90
99
|
FROM matrix_turns WHERE event_id = ?`,
|
|
91
100
|
[eventId],
|
|
92
101
|
);
|
|
@@ -98,6 +107,7 @@ export class MatrixTurnLedger {
|
|
|
98
107
|
startedAt: row.started_at,
|
|
99
108
|
answeredAt: row.answered_at,
|
|
100
109
|
replyText: row.reply_text,
|
|
110
|
+
...(row.reply_plan !== null ? { replyPlan: row.reply_plan } : {}),
|
|
101
111
|
};
|
|
102
112
|
}
|
|
103
113
|
|
|
@@ -116,11 +126,20 @@ export class MatrixTurnLedger {
|
|
|
116
126
|
}
|
|
117
127
|
|
|
118
128
|
/** Record the reply; from now on the event is answered, never re-run. */
|
|
119
|
-
async answer(
|
|
129
|
+
async answer(
|
|
130
|
+
eventId: string,
|
|
131
|
+
replyText: string,
|
|
132
|
+
replyPlan?: string,
|
|
133
|
+
): Promise<void> {
|
|
120
134
|
await this.setup();
|
|
121
135
|
await this.db.run(
|
|
122
|
-
`UPDATE matrix_turns SET reply_text = ?, answered_at = ? WHERE event_id = ?`,
|
|
123
|
-
[
|
|
136
|
+
`UPDATE matrix_turns SET reply_text = ?, reply_plan = ?, answered_at = ? WHERE event_id = ?`,
|
|
137
|
+
[
|
|
138
|
+
replyText,
|
|
139
|
+
replyPlan ?? null,
|
|
140
|
+
new Date(this.now()).toISOString(),
|
|
141
|
+
eventId,
|
|
142
|
+
],
|
|
124
143
|
);
|
|
125
144
|
}
|
|
126
145
|
}
|
|
@@ -22,13 +22,58 @@ export class ResultStoreTestDO extends DurableObject<{ TIER_TEST: R2Bucket }> {
|
|
|
22
22
|
|
|
23
23
|
private withBucket = true;
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
/**
|
|
26
|
+
* How R2 deletes behave: `fail` throws (an outage during a sweep),
|
|
27
|
+
* `delete-then-fail` deletes and then throws (a lost response),
|
|
28
|
+
* `fail-first` throws on the first call only.
|
|
29
|
+
*/
|
|
30
|
+
private deletes: 'ok' | 'fail' | 'delete-then-fail' | 'fail-first' = 'ok';
|
|
31
|
+
|
|
32
|
+
private deleteCalls = 0;
|
|
33
|
+
|
|
34
|
+
/** While set, an R2 delete waits for `release` (after signalling `reached`). */
|
|
35
|
+
private deleteHold: { reached: () => void; release: Promise<void> } | null =
|
|
36
|
+
null;
|
|
37
|
+
|
|
38
|
+
private async database(): Promise<DoSqliteDatabase> {
|
|
26
39
|
if (!this.db || !this.db.isOpen) {
|
|
27
40
|
this.db = await DoSqliteDatabase.open(this.ctx, 'results-test.db');
|
|
28
41
|
this.store = undefined;
|
|
29
42
|
}
|
|
30
|
-
|
|
31
|
-
|
|
43
|
+
return this.db;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private bucket(): R2Bucket {
|
|
47
|
+
const remove = async (keys: string | string[]): Promise<void> => {
|
|
48
|
+
this.deleteCalls += 1;
|
|
49
|
+
const hold = this.deleteHold;
|
|
50
|
+
if (hold) {
|
|
51
|
+
hold.reached();
|
|
52
|
+
await hold.release;
|
|
53
|
+
}
|
|
54
|
+
if (
|
|
55
|
+
this.deletes === 'fail' ||
|
|
56
|
+
(this.deletes === 'fail-first' && this.deleteCalls === 1)
|
|
57
|
+
)
|
|
58
|
+
throw new Error('R2 unavailable');
|
|
59
|
+
await this.env.TIER_TEST.delete(keys);
|
|
60
|
+
if (this.deletes === 'delete-then-fail')
|
|
61
|
+
throw new Error('R2 response lost');
|
|
62
|
+
};
|
|
63
|
+
return new Proxy(this.env.TIER_TEST, {
|
|
64
|
+
get: (target, property) => {
|
|
65
|
+
if (property === 'delete') return remove;
|
|
66
|
+
// The binding's methods only run on the binding itself.
|
|
67
|
+
const value: unknown = Reflect.get(target, property);
|
|
68
|
+
return typeof value === 'function' ? value.bind(target) : value;
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private async resultStore(): Promise<ResultStore> {
|
|
74
|
+
const db = await this.database();
|
|
75
|
+
this.store ??= new ResultStore(db, {
|
|
76
|
+
...(this.withBucket ? { bucket: this.bucket() } : {}),
|
|
32
77
|
prefix: `test-${this.ctx.id.toString()}`,
|
|
33
78
|
ttlMs: 60 * 60 * 1000,
|
|
34
79
|
r2MinBytes: this.r2MinBytes,
|
|
@@ -40,12 +85,101 @@ export class ResultStoreTestDO extends DurableObject<{ TIER_TEST: R2Bucket }> {
|
|
|
40
85
|
async configure(opts: {
|
|
41
86
|
r2MinBytes?: number;
|
|
42
87
|
withBucket?: boolean;
|
|
88
|
+
failingDeletes?: boolean;
|
|
89
|
+
deletes?: 'ok' | 'fail' | 'delete-then-fail' | 'fail-first';
|
|
43
90
|
}): Promise<void> {
|
|
44
91
|
if (opts.r2MinBytes !== undefined) this.r2MinBytes = opts.r2MinBytes;
|
|
45
92
|
if (opts.withBucket !== undefined) this.withBucket = opts.withBucket;
|
|
93
|
+
if (opts.failingDeletes !== undefined)
|
|
94
|
+
this.deletes = opts.failingDeletes ? 'fail' : 'ok';
|
|
95
|
+
if (opts.deletes !== undefined) this.deletes = opts.deletes;
|
|
96
|
+
this.deleteCalls = 0;
|
|
46
97
|
this.store = undefined;
|
|
47
98
|
}
|
|
48
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Delete one session's results while another session stores the same
|
|
102
|
+
* content: the put starts while the delete waits on R2, and R2 answers
|
|
103
|
+
* after the put had time to finish. Returns how the result reads then.
|
|
104
|
+
*/
|
|
105
|
+
async deleteWhilePutting(input: {
|
|
106
|
+
deleting: string;
|
|
107
|
+
putting: string;
|
|
108
|
+
content: string;
|
|
109
|
+
}): Promise<ReadResultOutcome> {
|
|
110
|
+
const store = await this.resultStore();
|
|
111
|
+
await store.setup();
|
|
112
|
+
let reached!: () => void;
|
|
113
|
+
const atDelete = new Promise<void>((resolve) => {
|
|
114
|
+
reached = resolve;
|
|
115
|
+
});
|
|
116
|
+
let release!: () => void;
|
|
117
|
+
this.deleteHold = {
|
|
118
|
+
reached,
|
|
119
|
+
release: new Promise<void>((resolve) => {
|
|
120
|
+
release = resolve;
|
|
121
|
+
}),
|
|
122
|
+
};
|
|
123
|
+
const deleting = store.deleteForSession(input.deleting);
|
|
124
|
+
await atDelete;
|
|
125
|
+
const putting = store.put({
|
|
126
|
+
sessionId: input.putting,
|
|
127
|
+
toolName: 't',
|
|
128
|
+
content: input.content,
|
|
129
|
+
});
|
|
130
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
131
|
+
release();
|
|
132
|
+
const [, ref] = await Promise.all([deleting, putting]);
|
|
133
|
+
this.deleteHold = null;
|
|
134
|
+
if (!ref) throw new Error('the result was not stored');
|
|
135
|
+
return store.read(ref.id, 0, 5);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Rows as a store written before results were shared across sessions
|
|
140
|
+
* left them: the `tool_results` table alone. Call before any other method.
|
|
141
|
+
*/
|
|
142
|
+
async seedLegacy(
|
|
143
|
+
rows: Array<{ id: string; sessionId: string; content: string }>,
|
|
144
|
+
): Promise<void> {
|
|
145
|
+
const db = await this.database();
|
|
146
|
+
await db.run(`
|
|
147
|
+
CREATE TABLE IF NOT EXISTS tool_results (
|
|
148
|
+
id TEXT PRIMARY KEY,
|
|
149
|
+
session_id TEXT NOT NULL,
|
|
150
|
+
tool_name TEXT NOT NULL,
|
|
151
|
+
size INTEGER NOT NULL,
|
|
152
|
+
tier TEXT NOT NULL,
|
|
153
|
+
content TEXT,
|
|
154
|
+
created_at TEXT NOT NULL,
|
|
155
|
+
expires_at INTEGER NOT NULL
|
|
156
|
+
)`);
|
|
157
|
+
for (const row of rows)
|
|
158
|
+
await db.run(
|
|
159
|
+
`INSERT INTO tool_results VALUES (?, ?, 't', ?, 'sqlite', ?, ?, ?)`,
|
|
160
|
+
[
|
|
161
|
+
row.id,
|
|
162
|
+
row.sessionId,
|
|
163
|
+
new TextEncoder().encode(row.content).byteLength,
|
|
164
|
+
row.content,
|
|
165
|
+
new Date(this.nowMs).toISOString(),
|
|
166
|
+
this.nowMs + 60 * 60 * 1000,
|
|
167
|
+
],
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Re-open the store over the same database (the next boot). */
|
|
172
|
+
async reboot(): Promise<void> {
|
|
173
|
+
this.store = undefined;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async references(): Promise<Array<{ id: string; session_id: string }>> {
|
|
177
|
+
const db = await this.database();
|
|
178
|
+
return db.exec<{ id: string; session_id: string }>(
|
|
179
|
+
`SELECT id, session_id FROM tool_result_sessions ORDER BY id, session_id`,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
49
183
|
async setNow(ms: number): Promise<void> {
|
|
50
184
|
this.nowMs = ms;
|
|
51
185
|
}
|