@getfamiliar/shared 0.1.2
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/build/AgentRun.d.ts +187 -0
- package/build/AgentRun.js +1 -0
- package/build/AgentRunBus.d.ts +147 -0
- package/build/AgentRunBus.js +362 -0
- package/build/Calendar.d.ts +450 -0
- package/build/Calendar.js +37 -0
- package/build/ChatMessage.d.ts +53 -0
- package/build/ChatMessage.js +11 -0
- package/build/ChatMessageBus.d.ts +110 -0
- package/build/ChatMessageBus.js +257 -0
- package/build/Config.d.ts +83 -0
- package/build/Config.js +1 -0
- package/build/ContainerTool.d.ts +34 -0
- package/build/ContainerTool.js +1 -0
- package/build/CronExpression.d.ts +53 -0
- package/build/CronExpression.js +77 -0
- package/build/Event.d.ts +280 -0
- package/build/Event.js +14 -0
- package/build/EventBus.d.ts +198 -0
- package/build/EventBus.js +482 -0
- package/build/HandlerCatalog.d.ts +75 -0
- package/build/HandlerCatalog.js +165 -0
- package/build/InferenceEvent.d.ts +30 -0
- package/build/InferenceEvent.js +1 -0
- package/build/InferenceEventBus.d.ts +39 -0
- package/build/InferenceEventBus.js +80 -0
- package/build/Mail.d.ts +250 -0
- package/build/Mail.js +73 -0
- package/build/MailStyleTemplate.d.ts +71 -0
- package/build/MailStyleTemplate.js +47 -0
- package/build/ModelMetaData.d.ts +63 -0
- package/build/ModelMetaData.js +12 -0
- package/build/PathGlob.d.ts +20 -0
- package/build/PathGlob.js +33 -0
- package/build/Plugin.d.ts +709 -0
- package/build/Plugin.js +29 -0
- package/build/PostgresConnection.d.ts +75 -0
- package/build/PostgresConnection.js +162 -0
- package/build/ScheduledSubagent.d.ts +49 -0
- package/build/ScheduledSubagent.js +1 -0
- package/build/ScheduledSubagentBus.d.ts +94 -0
- package/build/ScheduledSubagentBus.js +187 -0
- package/build/Schema.d.ts +84 -0
- package/build/Schema.js +570 -0
- package/build/StepResult.d.ts +103 -0
- package/build/StepResult.js +1 -0
- package/build/StepResultBus.d.ts +64 -0
- package/build/StepResultBus.js +169 -0
- package/build/Timezone.d.ts +58 -0
- package/build/Timezone.js +84 -0
- package/build/ToolCall.d.ts +25 -0
- package/build/ToolCall.js +1 -0
- package/build/ToolCallBus.d.ts +65 -0
- package/build/ToolCallBus.js +110 -0
- package/build/ToolLevel.d.ts +20 -0
- package/build/ToolLevel.js +2 -0
- package/build/ToolRunner.d.ts +118 -0
- package/build/ToolRunner.js +192 -0
- package/build/ToolsExpressionUtils.d.ts +80 -0
- package/build/ToolsExpressionUtils.js +102 -0
- package/build/WorkspaceFile.d.ts +104 -0
- package/build/WorkspaceFile.js +7 -0
- package/build/estimateTokens.d.ts +16 -0
- package/build/estimateTokens.js +18 -0
- package/build/index.d.ts +37 -0
- package/build/index.js +26 -0
- package/build/logging/Logger.d.ts +66 -0
- package/build/logging/Logger.js +90 -0
- package/build/markdownHtml.d.ts +14 -0
- package/build/markdownHtml.js +21 -0
- package/build/markdownTerminal.d.ts +14 -0
- package/build/markdownTerminal.js +376 -0
- package/build/testing/MockAgentRunBus.d.ts +52 -0
- package/build/testing/MockAgentRunBus.js +175 -0
- package/build/testing/MockBusStore.d.ts +22 -0
- package/build/testing/MockBusStore.js +22 -0
- package/build/testing/MockEventBus.d.ts +20 -0
- package/build/testing/MockEventBus.js +57 -0
- package/build/testing/MockLogger.d.ts +52 -0
- package/build/testing/MockLogger.js +65 -0
- package/build/testing/index.d.ts +4 -0
- package/build/testing/index.js +4 -0
- package/package.json +63 -0
package/build/Schema.js
ADDED
|
@@ -0,0 +1,570 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQL that defines the bus-state schema. Idempotent: every statement is
|
|
3
|
+
* `IF NOT EXISTS` / `CREATE OR REPLACE` / `DROP IF EXISTS`, so it's safe
|
|
4
|
+
* to run on every daemon start without a migrations framework.
|
|
5
|
+
*
|
|
6
|
+
* Two tables:
|
|
7
|
+
*
|
|
8
|
+
* - `events` is the immutable record of "the world said this happened".
|
|
9
|
+
* One row per external trigger, idempotency-keyed. Events do not
|
|
10
|
+
* participate in any worker state machine themselves; they are
|
|
11
|
+
* referenced by `agentruns` rows that do the actual work.
|
|
12
|
+
* - `agentruns` is the assistant's response tree. Each row is one agent
|
|
13
|
+
* invocation. The container's input-event watcher inserts the root
|
|
14
|
+
* agentrun for each new event; child agentruns are spawned by the
|
|
15
|
+
* `schedule_subagent` (fire-and-forget when called without `when`) and
|
|
16
|
+
* `start_subagent` (suspending) tools inside a running handler.
|
|
17
|
+
*
|
|
18
|
+
* Four tables, five NOTIFY channels:
|
|
19
|
+
*
|
|
20
|
+
* - `events_new` — fires on INSERT into `events`. Wakes the container's
|
|
21
|
+
* input-event watcher.
|
|
22
|
+
* - `events_state` — fires on state UPDATE on `events`. Lets host-side
|
|
23
|
+
* plugins wait for a specific event to settle.
|
|
24
|
+
* - `agentruns_changed` — fires on INSERT and on state UPDATE on
|
|
25
|
+
* `agentruns`. Wakes the agentrun watcher. Payload format is
|
|
26
|
+
* `<event_id>:<id>` so subscribers can route on event_id without a
|
|
27
|
+
* JOIN.
|
|
28
|
+
* - `stepresults_new` — fires on INSERT into `stepresults`. Lets host-side
|
|
29
|
+
* subscribers (e.g. `HostContext.events.emit`'s `onStep` callback) tail
|
|
30
|
+
* per-step audit rows as the agent loop produces them. Payload format
|
|
31
|
+
* is `<event_id>:<agent_run_id>:<id>` so subscribers can route on
|
|
32
|
+
* event_id without a JOIN.
|
|
33
|
+
* - `chatmessages_new` — fires on INSERT into `chatmessages`. Lets
|
|
34
|
+
* host-side channel plugins (cli-chat, telegram, …) stream assistant
|
|
35
|
+
* replies. Payload format is `<channel_id>:<role>:<id>`; channel is
|
|
36
|
+
* JOINed from `events.preferred_chat_channel_id` so subscribers can
|
|
37
|
+
* filter on the prefix without a query roundtrip.
|
|
38
|
+
*/
|
|
39
|
+
export const EVENTS_NEW_CHANNEL = "events_new";
|
|
40
|
+
export const EVENTS_STATE_CHANNEL = "events_state";
|
|
41
|
+
export const AGENTRUNS_CHANNEL = "agentruns_changed";
|
|
42
|
+
export const STEPRESULTS_NEW_CHANNEL = "stepresults_new";
|
|
43
|
+
export const CHATMESSAGES_NEW_CHANNEL = "chatmessages_new";
|
|
44
|
+
/**
|
|
45
|
+
* NOTIFY channel for `scheduled_subagents` mutations. Payload format is
|
|
46
|
+
* `<key>:<op>` where `op` is `u` (insert / update) or `d` (delete). The
|
|
47
|
+
* host-side `ScheduledSubagentScheduler` listens here to install or
|
|
48
|
+
* cancel Croner jobs as the agent schedules / unschedules one-off
|
|
49
|
+
* subagents.
|
|
50
|
+
*/
|
|
51
|
+
export const SCHEDULED_SUBAGENTS_CHANNEL = "scheduled_subagents_changed";
|
|
52
|
+
/**
|
|
53
|
+
* Topic regex used by the events check constraint and by the container
|
|
54
|
+
* runtime when resolving handler files. One word, optionally followed
|
|
55
|
+
* by any number of `:`-prefixed sub-topic words. Examples:
|
|
56
|
+
* `mail`, `chat:cli`, `chat:telegram:group:reaction`.
|
|
57
|
+
*
|
|
58
|
+
* The container resolves a handler for topic `a:b:c` by walking the
|
|
59
|
+
* chain `a/b/c/<basename>.md → a/b/<basename>.md → a/<basename>.md`
|
|
60
|
+
* deepest-first and merging every existing layer (see HandlerFile.load).
|
|
61
|
+
*/
|
|
62
|
+
export const TOPIC_PATTERN = "^\\w+(:\\w+)*$";
|
|
63
|
+
export const SCHEMA_SQL = `
|
|
64
|
+
-- Drop legacy single-table artifacts from before the events/agentruns
|
|
65
|
+
-- split. Safe no-ops on fresh databases. The current schema reuses the
|
|
66
|
+
-- name events_notify_new() for its own NOTIFY function, so we do NOT
|
|
67
|
+
-- drop it here (CREATE OR REPLACE FUNCTION below handles the update);
|
|
68
|
+
-- dropping would fail anyway once the current events_new_trg exists.
|
|
69
|
+
DROP TRIGGER IF EXISTS events_notify_changed_trg ON events;
|
|
70
|
+
DROP TRIGGER IF EXISTS events_notify_new_trg ON events;
|
|
71
|
+
DROP FUNCTION IF EXISTS events_notify_changed();
|
|
72
|
+
DROP INDEX IF EXISTS events_pending_priority_idx;
|
|
73
|
+
DROP INDEX IF EXISTS events_state_priority_idx;
|
|
74
|
+
|
|
75
|
+
CREATE TABLE IF NOT EXISTS events (
|
|
76
|
+
id bigserial PRIMARY KEY,
|
|
77
|
+
topic text NOT NULL,
|
|
78
|
+
priority smallint NOT NULL DEFAULT 50,
|
|
79
|
+
state text NOT NULL DEFAULT 'pending',
|
|
80
|
+
payload jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
81
|
+
idempotency_key text UNIQUE,
|
|
82
|
+
privileged boolean NOT NULL DEFAULT false,
|
|
83
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
84
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
85
|
+
CONSTRAINT events_topic_format CHECK (topic ~ '${TOPIC_PATTERN}')
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
-- Drop legacy columns from earlier schema generations. Single-instance
|
|
89
|
+
-- project; column drops are fine.
|
|
90
|
+
ALTER TABLE events DROP COLUMN IF EXISTS supervisor_prompt;
|
|
91
|
+
ALTER TABLE events DROP COLUMN IF EXISTS causation_chain;
|
|
92
|
+
|
|
93
|
+
-- Chat-aware event metadata. \`is_chat\` opts into automatic user-message
|
|
94
|
+
-- persistence in EventBus.add; \`preferred_chat_channel_id\` names the
|
|
95
|
+
-- channel an assistant reply should reach the user on. The container is
|
|
96
|
+
-- channel-blind — both fields are read host-side (for routing) or via
|
|
97
|
+
-- JOIN inside the chatmessages NOTIFY trigger.
|
|
98
|
+
ALTER TABLE events ADD COLUMN IF NOT EXISTS is_chat boolean NOT NULL DEFAULT false;
|
|
99
|
+
ALTER TABLE events ADD COLUMN IF NOT EXISTS preferred_chat_channel_id text;
|
|
100
|
+
-- Required for non-chat events (validated at \`EventBus.add\` time and at
|
|
101
|
+
-- the type level via \`NewEvent\`'s discriminated union); always null for
|
|
102
|
+
-- chat events because the user's text in \`chatmessages\` already serves
|
|
103
|
+
-- as the agent-visible "what happened" message. The EventWatcher copies
|
|
104
|
+
-- this verbatim into the root \`agentruns.prompt\` so the agent sees the
|
|
105
|
+
-- emitter's framing without the EventWatcher having to reach into the
|
|
106
|
+
-- payload schema.
|
|
107
|
+
ALTER TABLE events ADD COLUMN IF NOT EXISTS prompt text;
|
|
108
|
+
-- Optional override for the root agentrun's handler. When NULL the
|
|
109
|
+
-- input-event watcher falls back to 'index' — same behaviour as
|
|
110
|
+
-- before this column existed. Set by the emitter on NewEvent.
|
|
111
|
+
ALTER TABLE events ADD COLUMN IF NOT EXISTS start_handler text;
|
|
112
|
+
-- Privileged events originate from a trusted user-input source (operator
|
|
113
|
+
-- at the local terminal via cli-chat, operator on Telegram). The flag
|
|
114
|
+
-- propagates verbatim to the root agentrun and to every child agentrun
|
|
115
|
+
-- spawned via schedule_subagent / start_subagent, so future system tools can
|
|
116
|
+
-- gate risky reads / writes (editing SOUL.md etc.) on whether the run
|
|
117
|
+
-- descends from a trusted input. Default false: any plugin that doesn't
|
|
118
|
+
-- explicitly opt in produces non-privileged events.
|
|
119
|
+
ALTER TABLE events ADD COLUMN IF NOT EXISTS privileged boolean NOT NULL DEFAULT false;
|
|
120
|
+
-- When true and the event ends in 'failed', \`HostContextImpl.emitAndAwait\`
|
|
121
|
+
-- writes a role='assistant' chatmessage with the failure text before
|
|
122
|
+
-- rejecting handle.settled. Default false: only chat plugins (cli-chat,
|
|
123
|
+
-- telegram) opt in. See [[outputChatOnFailure]] on NewEvent for the
|
|
124
|
+
-- rationale.
|
|
125
|
+
ALTER TABLE events ADD COLUMN IF NOT EXISTS output_chat_on_failure boolean NOT NULL DEFAULT false;
|
|
126
|
+
|
|
127
|
+
CREATE INDEX IF NOT EXISTS events_state_priority_idx
|
|
128
|
+
ON events (state, priority DESC, id ASC);
|
|
129
|
+
CREATE INDEX IF NOT EXISTS events_topic_idx ON events (topic);
|
|
130
|
+
|
|
131
|
+
CREATE TABLE IF NOT EXISTS agentruns (
|
|
132
|
+
id bigserial PRIMARY KEY,
|
|
133
|
+
event_id bigint NOT NULL REFERENCES events(id) ON DELETE CASCADE,
|
|
134
|
+
parent_agentrun_id bigint REFERENCES agentruns(id) ON DELETE CASCADE,
|
|
135
|
+
topic text NOT NULL,
|
|
136
|
+
handler text NOT NULL,
|
|
137
|
+
priority smallint NOT NULL DEFAULT 50,
|
|
138
|
+
state text NOT NULL DEFAULT 'pending',
|
|
139
|
+
prompt text,
|
|
140
|
+
payload jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
141
|
+
result jsonb,
|
|
142
|
+
result_text text,
|
|
143
|
+
error text,
|
|
144
|
+
privileged boolean NOT NULL DEFAULT false,
|
|
145
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
146
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
147
|
+
CONSTRAINT agentruns_topic_format CHECK (topic ~ '${TOPIC_PATTERN}')
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
-- Forward-compat ALTER for databases created before result_text landed.
|
|
151
|
+
-- Safe no-op on fresh databases where the column already exists.
|
|
152
|
+
ALTER TABLE agentruns ADD COLUMN IF NOT EXISTS result_text text;
|
|
153
|
+
-- Inherited from the originating event (root agentrun) or parent
|
|
154
|
+
-- agentrun (children spawned via schedule_subagent / start_subagent). See
|
|
155
|
+
-- the matching ALTER on the events table for the trust-model rationale.
|
|
156
|
+
ALTER TABLE agentruns ADD COLUMN IF NOT EXISTS privileged boolean NOT NULL DEFAULT false;
|
|
157
|
+
-- Number of retry attempts already made on this agentrun. Bumped each
|
|
158
|
+
-- time AgentRunner postpones the row due to a retryable inference
|
|
159
|
+
-- error (APICallError.isRetryable). Capped per-handler via maxRetries
|
|
160
|
+
-- (frontmatter override) or globally via inference.maxRetries.
|
|
161
|
+
ALTER TABLE agentruns ADD COLUMN IF NOT EXISTS retry_count int NOT NULL DEFAULT 0;
|
|
162
|
+
-- Earliest moment the agentrun watcher is allowed to re-claim this
|
|
163
|
+
-- row. NULL = claim immediately (default for fresh rows). A future
|
|
164
|
+
-- timestamp = leave the row alone until then; the watcher's claim
|
|
165
|
+
-- query gates on this so a parked row doesn't waste the slot while
|
|
166
|
+
-- other agentruns (potentially using different models) can still run.
|
|
167
|
+
ALTER TABLE agentruns ADD COLUMN IF NOT EXISTS not_before timestamptz;
|
|
168
|
+
-- Resolved model id for this agentrun, of the form
|
|
169
|
+
-- \`<provider>/<modelId>\` (e.g. \`featherless/zai-org/GLM-5.1\`).
|
|
170
|
+
-- Populated by AgentRunner once it builds the model client; stays
|
|
171
|
+
-- NULL for any agentrun that never reached generate() (failed at
|
|
172
|
+
-- handler-load time, etc.).
|
|
173
|
+
ALTER TABLE agentruns ADD COLUMN IF NOT EXISTS model text;
|
|
174
|
+
-- Resolved system prompt (SOUL.md + CONTEXT.md + handler body +
|
|
175
|
+
-- tool list) used for this agentrun. Populated by AgentRunner
|
|
176
|
+
-- only when core.logSystemPrompt is true; otherwise NULL. Surfaced
|
|
177
|
+
-- by the report layer's Agentrun Start section when the consumer
|
|
178
|
+
-- opts into withDetails.
|
|
179
|
+
ALTER TABLE agentruns ADD COLUMN IF NOT EXISTS system_prompt text;
|
|
180
|
+
-- Verbatim model-message array assembled for the first agent.generate()
|
|
181
|
+
-- call (prior history + the run's leading user turn). Populated by
|
|
182
|
+
-- AgentRunner only when inference.captureInitialMessageHistory is true;
|
|
183
|
+
-- otherwise NULL. Surfaced by the report layer's -vv view so an operator
|
|
184
|
+
-- can see exactly what was sent into the model loop.
|
|
185
|
+
ALTER TABLE agentruns ADD COLUMN IF NOT EXISTS initial_messages jsonb;
|
|
186
|
+
|
|
187
|
+
-- Distinguishes how a child agentrun was spawned.
|
|
188
|
+
-- NULL — root agentrun (created by the input-event watcher, no parent).
|
|
189
|
+
-- 'scheduled' — fire-and-forget child spawned by \`schedule_subagent\` without
|
|
190
|
+
-- a future \`when\` (immediate dispatch under the same event).
|
|
191
|
+
-- 'started' — synchronous child spawned via the \`start_subagent\` tool; the
|
|
192
|
+
-- parent is parked in state='waiting' until this row settles.
|
|
193
|
+
-- The Scheduler reads this column to decide whether settling a row should
|
|
194
|
+
-- wake a waiting parent.
|
|
195
|
+
ALTER TABLE agentruns ADD COLUMN IF NOT EXISTS calltype text;
|
|
196
|
+
ALTER TABLE agentruns DROP CONSTRAINT IF EXISTS agentruns_calltype_format;
|
|
197
|
+
ALTER TABLE agentruns ADD CONSTRAINT agentruns_calltype_format
|
|
198
|
+
CHECK (calltype IS NULL OR calltype IN ('scheduled','started'));
|
|
199
|
+
|
|
200
|
+
-- Partial index for the "are all started children of $parent settled?"
|
|
201
|
+
-- check the Scheduler runs after every 'started' child settles. Indexes
|
|
202
|
+
-- only the rows that actually participate in the question.
|
|
203
|
+
CREATE INDEX IF NOT EXISTS agentruns_parent_calltype_state_idx
|
|
204
|
+
ON agentruns (parent_agentrun_id, state)
|
|
205
|
+
WHERE calltype = 'started';
|
|
206
|
+
|
|
207
|
+
-- Forward-compat: relax the topic CHECK constraint to allow arbitrarily
|
|
208
|
+
-- deep \`a:b:c:…\` topics. CREATE TABLE IF NOT EXISTS above doesn't
|
|
209
|
+
-- update existing constraints, so we drop+re-add unconditionally. The
|
|
210
|
+
-- DROP IF EXISTS / ADD pair is idempotent.
|
|
211
|
+
ALTER TABLE events DROP CONSTRAINT IF EXISTS events_topic_format;
|
|
212
|
+
ALTER TABLE events ADD CONSTRAINT events_topic_format CHECK (topic ~ '${TOPIC_PATTERN}');
|
|
213
|
+
ALTER TABLE agentruns DROP CONSTRAINT IF EXISTS agentruns_topic_format;
|
|
214
|
+
ALTER TABLE agentruns ADD CONSTRAINT agentruns_topic_format CHECK (topic ~ '${TOPIC_PATTERN}');
|
|
215
|
+
|
|
216
|
+
CREATE INDEX IF NOT EXISTS agentruns_state_priority_idx
|
|
217
|
+
ON agentruns (state, priority DESC, id ASC);
|
|
218
|
+
CREATE INDEX IF NOT EXISTS agentruns_event_id_idx ON agentruns (event_id);
|
|
219
|
+
CREATE INDEX IF NOT EXISTS agentruns_parent_idx ON agentruns (parent_agentrun_id);
|
|
220
|
+
|
|
221
|
+
CREATE TABLE IF NOT EXISTS stepresults (
|
|
222
|
+
id bigserial PRIMARY KEY,
|
|
223
|
+
agent_run_id bigint NOT NULL REFERENCES agentruns(id) ON DELETE CASCADE,
|
|
224
|
+
event_id bigint NOT NULL REFERENCES events(id) ON DELETE CASCADE,
|
|
225
|
+
step_number int NOT NULL,
|
|
226
|
+
finish_reason text NOT NULL,
|
|
227
|
+
result_text text,
|
|
228
|
+
reasoning_text text,
|
|
229
|
+
input_tokens int,
|
|
230
|
+
output_tokens int,
|
|
231
|
+
total_tokens int,
|
|
232
|
+
tool_call_count int NOT NULL DEFAULT 0,
|
|
233
|
+
tool_calls jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
234
|
+
tool_results jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
235
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
236
|
+
CONSTRAINT stepresults_step_number_unique UNIQUE (agent_run_id, step_number)
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
CREATE INDEX IF NOT EXISTS stepresults_agent_run_id_idx ON stepresults (agent_run_id);
|
|
240
|
+
CREATE INDEX IF NOT EXISTS stepresults_event_id_idx ON stepresults (event_id);
|
|
241
|
+
|
|
242
|
+
-- AI SDK per-step usage breakdown
|
|
243
|
+
-- (LanguageModelUsage.inputTokenDetails / outputTokenDetails). Each
|
|
244
|
+
-- column is NULL when the provider doesn't report that detail.
|
|
245
|
+
ALTER TABLE stepresults ADD COLUMN IF NOT EXISTS input_tokens_no_cache int;
|
|
246
|
+
ALTER TABLE stepresults ADD COLUMN IF NOT EXISTS input_tokens_cache_read int;
|
|
247
|
+
ALTER TABLE stepresults ADD COLUMN IF NOT EXISTS input_tokens_cache_write int;
|
|
248
|
+
ALTER TABLE stepresults ADD COLUMN IF NOT EXISTS output_tokens_text int;
|
|
249
|
+
ALTER TABLE stepresults ADD COLUMN IF NOT EXISTS output_tokens_reasoning int;
|
|
250
|
+
-- Full step object snapshot, populated only when the operator opts
|
|
251
|
+
-- in via inference.captureRawStepResultToDatabase: true. Used to
|
|
252
|
+
-- inspect provider-specific fields (Anthropic cache control,
|
|
253
|
+
-- OpenAI logprobs, ...) that don't fit dedicated columns.
|
|
254
|
+
ALTER TABLE stepresults ADD COLUMN IF NOT EXISTS raw_result jsonb;
|
|
255
|
+
|
|
256
|
+
-- ───────── chatmessages ─────────
|
|
257
|
+
--
|
|
258
|
+
-- Persistent chat history across all channels. Each row links to the
|
|
259
|
+
-- event whose lifecycle the message belongs to:
|
|
260
|
+
-- * For role='user': the event the host plugin emitted with isChat=true.
|
|
261
|
+
-- * For role='assistant': the event whose agentrun called send_chat.
|
|
262
|
+
-- The channel is reachable via JOIN to events.preferred_chat_channel_id;
|
|
263
|
+
-- there is intentionally no channel_id column here — channel routing is
|
|
264
|
+
-- a host-side concern, the container never names a channel.
|
|
265
|
+
--
|
|
266
|
+
-- delivered_at is set when ANY listener returns true from its async
|
|
267
|
+
-- handler. New subscribers replay all undelivered matching rows on
|
|
268
|
+
-- registration so a message produced when nobody was listening is still
|
|
269
|
+
-- delivered later.
|
|
270
|
+
CREATE TABLE IF NOT EXISTS chatmessages (
|
|
271
|
+
id bigserial PRIMARY KEY,
|
|
272
|
+
event_id bigint NOT NULL REFERENCES events(id) ON DELETE CASCADE,
|
|
273
|
+
role text NOT NULL CHECK (role IN ('user','assistant')),
|
|
274
|
+
text_content text NOT NULL,
|
|
275
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
276
|
+
delivered_at timestamptz
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
CREATE INDEX IF NOT EXISTS chatmessages_event_idx
|
|
280
|
+
ON chatmessages (event_id);
|
|
281
|
+
CREATE INDEX IF NOT EXISTS chatmessages_undelivered_idx
|
|
282
|
+
ON chatmessages (created_at) WHERE delivered_at IS NULL;
|
|
283
|
+
|
|
284
|
+
-- ───────── NOTIFY triggers ─────────
|
|
285
|
+
|
|
286
|
+
CREATE OR REPLACE FUNCTION events_notify_new() RETURNS trigger AS $$
|
|
287
|
+
BEGIN
|
|
288
|
+
PERFORM pg_notify('${EVENTS_NEW_CHANNEL}', NEW.id::text);
|
|
289
|
+
RETURN NEW;
|
|
290
|
+
END;
|
|
291
|
+
$$ LANGUAGE plpgsql;
|
|
292
|
+
|
|
293
|
+
DROP TRIGGER IF EXISTS events_new_trg ON events;
|
|
294
|
+
CREATE TRIGGER events_new_trg
|
|
295
|
+
AFTER INSERT ON events
|
|
296
|
+
FOR EACH ROW EXECUTE FUNCTION events_notify_new();
|
|
297
|
+
|
|
298
|
+
CREATE OR REPLACE FUNCTION events_notify_state() RETURNS trigger AS $$
|
|
299
|
+
BEGIN
|
|
300
|
+
PERFORM pg_notify('${EVENTS_STATE_CHANNEL}', NEW.id::text || ':' || NEW.state);
|
|
301
|
+
RETURN NEW;
|
|
302
|
+
END;
|
|
303
|
+
$$ LANGUAGE plpgsql;
|
|
304
|
+
|
|
305
|
+
DROP TRIGGER IF EXISTS events_state_trg ON events;
|
|
306
|
+
CREATE TRIGGER events_state_trg
|
|
307
|
+
AFTER UPDATE OF state ON events
|
|
308
|
+
FOR EACH ROW EXECUTE FUNCTION events_notify_state();
|
|
309
|
+
|
|
310
|
+
CREATE OR REPLACE FUNCTION agentruns_notify_changed() RETURNS trigger AS $$
|
|
311
|
+
BEGIN
|
|
312
|
+
PERFORM pg_notify(
|
|
313
|
+
'${AGENTRUNS_CHANNEL}',
|
|
314
|
+
NEW.event_id::text || ':' || NEW.id::text
|
|
315
|
+
);
|
|
316
|
+
RETURN NEW;
|
|
317
|
+
END;
|
|
318
|
+
$$ LANGUAGE plpgsql;
|
|
319
|
+
|
|
320
|
+
DROP TRIGGER IF EXISTS agentruns_notify_changed_trg ON agentruns;
|
|
321
|
+
CREATE TRIGGER agentruns_notify_changed_trg
|
|
322
|
+
AFTER INSERT OR UPDATE OF state ON agentruns
|
|
323
|
+
FOR EACH ROW EXECUTE FUNCTION agentruns_notify_changed();
|
|
324
|
+
|
|
325
|
+
CREATE OR REPLACE FUNCTION stepresults_notify_new() RETURNS trigger AS $$
|
|
326
|
+
BEGIN
|
|
327
|
+
PERFORM pg_notify(
|
|
328
|
+
'${STEPRESULTS_NEW_CHANNEL}',
|
|
329
|
+
NEW.event_id::text || ':' || NEW.agent_run_id::text || ':' || NEW.id::text
|
|
330
|
+
);
|
|
331
|
+
RETURN NEW;
|
|
332
|
+
END;
|
|
333
|
+
$$ LANGUAGE plpgsql;
|
|
334
|
+
|
|
335
|
+
DROP TRIGGER IF EXISTS stepresults_new_trg ON stepresults;
|
|
336
|
+
CREATE TRIGGER stepresults_new_trg
|
|
337
|
+
AFTER INSERT ON stepresults
|
|
338
|
+
FOR EACH ROW EXECUTE FUNCTION stepresults_notify_new();
|
|
339
|
+
|
|
340
|
+
-- chatmessages NOTIFY: payload is "<channel_id>:<role>:<id>". Channel
|
|
341
|
+
-- comes from the parent event's preferred_chat_channel_id, JOINed at
|
|
342
|
+
-- trigger time. Empty channel becomes empty prefix — listeners will
|
|
343
|
+
-- filter it out unless they explicitly subscribe to channelId="".
|
|
344
|
+
CREATE OR REPLACE FUNCTION chatmessages_notify_new() RETURNS trigger AS $$
|
|
345
|
+
DECLARE
|
|
346
|
+
channel text;
|
|
347
|
+
BEGIN
|
|
348
|
+
SELECT preferred_chat_channel_id INTO channel FROM events WHERE id = NEW.event_id;
|
|
349
|
+
PERFORM pg_notify(
|
|
350
|
+
'${CHATMESSAGES_NEW_CHANNEL}',
|
|
351
|
+
COALESCE(channel, '') || ':' || NEW.role || ':' || NEW.id::text
|
|
352
|
+
);
|
|
353
|
+
RETURN NEW;
|
|
354
|
+
END;
|
|
355
|
+
$$ LANGUAGE plpgsql;
|
|
356
|
+
|
|
357
|
+
DROP TRIGGER IF EXISTS chatmessages_new_trg ON chatmessages;
|
|
358
|
+
CREATE TRIGGER chatmessages_new_trg
|
|
359
|
+
AFTER INSERT ON chatmessages
|
|
360
|
+
FOR EACH ROW EXECUTE FUNCTION chatmessages_notify_new();
|
|
361
|
+
|
|
362
|
+
-- ───────── calendars + calendar_events ─────────
|
|
363
|
+
--
|
|
364
|
+
-- Plugin-agnostic calendar data layer. \`calendars\` lists every
|
|
365
|
+
-- subscription (own or shared) any provider plugin (ms365, future
|
|
366
|
+
-- google/caldav) has surfaced; \`calendar_events\` stores every
|
|
367
|
+
-- instance. Providers persist every occurrence of a recurring
|
|
368
|
+
-- series independently (no master-expansion in core).
|
|
369
|
+
--
|
|
370
|
+
-- Identity:
|
|
371
|
+
-- * \`calendars.unique_key\` is the provider's stable id. Uniqueness
|
|
372
|
+
-- within \`(plugin_id, unique_key)\` lets multiple providers
|
|
373
|
+
-- coexist.
|
|
374
|
+
-- * \`calendar_events.id\` is provider-prefixed (e.g. \`ms365:<graph-id>\`)
|
|
375
|
+
-- so collisions across providers are impossible by construction.
|
|
376
|
+
--
|
|
377
|
+
-- Refresh reconciliation uses a scan-generation tag: a refresh walk
|
|
378
|
+
-- bumps \`calendars.scan_generation\`, upserts every event in window
|
|
379
|
+
-- with the new value, then DELETEs events whose generation is older
|
|
380
|
+
-- than the calendar's current value.
|
|
381
|
+
|
|
382
|
+
CREATE TABLE IF NOT EXISTS calendars (
|
|
383
|
+
id bigserial PRIMARY KEY,
|
|
384
|
+
plugin_id text NOT NULL,
|
|
385
|
+
unique_key text NOT NULL,
|
|
386
|
+
name text NOT NULL,
|
|
387
|
+
type text NOT NULL CHECK (type IN ('own','shared')),
|
|
388
|
+
owner_name text,
|
|
389
|
+
scan_generation int NOT NULL DEFAULT 0,
|
|
390
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
391
|
+
UNIQUE (plugin_id, unique_key)
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
-- The provider's own "default calendar" flag (Graph's
|
|
395
|
+
-- \`isDefaultCalendar\`). Used by \`CalendarService.resolveDefaultCalendar\`
|
|
396
|
+
-- as the fallback when \`core.defaultCalendar\` is unset — we pick the
|
|
397
|
+
-- first registered calendar (lowest id) flagged here. Added via
|
|
398
|
+
-- ALTER so existing databases don't need a schema reset.
|
|
399
|
+
ALTER TABLE calendars ADD COLUMN IF NOT EXISTS is_default smallint NOT NULL DEFAULT 0;
|
|
400
|
+
|
|
401
|
+
CREATE INDEX IF NOT EXISTS calendars_plugin_id_idx ON calendars (plugin_id);
|
|
402
|
+
|
|
403
|
+
CREATE TABLE IF NOT EXISTS calendar_events (
|
|
404
|
+
id text PRIMARY KEY,
|
|
405
|
+
calendar_id bigint NOT NULL REFERENCES calendars(id) ON DELETE CASCADE,
|
|
406
|
+
series_master_id text,
|
|
407
|
+
type text NOT NULL CHECK (type IN ('singleInstance','occurrence','exception','seriesMaster')),
|
|
408
|
+
subject text,
|
|
409
|
+
start_dt text NOT NULL,
|
|
410
|
+
end_dt text NOT NULL,
|
|
411
|
+
event_tz text,
|
|
412
|
+
is_all_day smallint NOT NULL DEFAULT 0,
|
|
413
|
+
is_cancelled smallint NOT NULL DEFAULT 0,
|
|
414
|
+
show_as text,
|
|
415
|
+
sensitivity text,
|
|
416
|
+
importance text,
|
|
417
|
+
location text,
|
|
418
|
+
is_online_meeting smallint NOT NULL DEFAULT 0,
|
|
419
|
+
online_meeting_url text,
|
|
420
|
+
organizer_name text,
|
|
421
|
+
organizer_email text,
|
|
422
|
+
response_status text,
|
|
423
|
+
attendees_json jsonb,
|
|
424
|
+
body text,
|
|
425
|
+
attachments jsonb,
|
|
426
|
+
scan_generation int NOT NULL,
|
|
427
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
428
|
+
updated_at timestamptz NOT NULL DEFAULT now()
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
CREATE INDEX IF NOT EXISTS calendar_events_window_idx
|
|
432
|
+
ON calendar_events (calendar_id, start_dt);
|
|
433
|
+
CREATE INDEX IF NOT EXISTS calendar_events_series_idx
|
|
434
|
+
ON calendar_events (series_master_id);
|
|
435
|
+
CREATE INDEX IF NOT EXISTS calendar_events_generation_idx
|
|
436
|
+
ON calendar_events (calendar_id, scan_generation);
|
|
437
|
+
|
|
438
|
+
-- ───────── scheduled_subagents ─────────
|
|
439
|
+
--
|
|
440
|
+
-- One-off agent-scheduled wake-ups. Each row is upserted by the
|
|
441
|
+
-- container's \`schedule_subagent\` tool and read by the host-side
|
|
442
|
+
-- \`ScheduledSubagentScheduler\`, which installs a Croner job per row
|
|
443
|
+
-- and atomically deletes the row when it fires. \`key\` is the
|
|
444
|
+
-- agent-supplied unique id used for replace-on-conflict semantics
|
|
445
|
+
-- ("rescheduling a key overwrites the previous timing"). \`fire_at\`
|
|
446
|
+
-- is the UTC firing time; the agent passes wall-clock-in-\`core.timezone\`
|
|
447
|
+
-- and the container projects to UTC before insert. On daemon startup,
|
|
448
|
+
-- past-due rows are deleted with a warn log; future rows are
|
|
449
|
+
-- reinstalled. See [[feedback_agent_facing_format]] — UTC storage,
|
|
450
|
+
-- local-tz projection at every agent-facing edge.
|
|
451
|
+
CREATE TABLE IF NOT EXISTS scheduled_subagents (
|
|
452
|
+
key text PRIMARY KEY,
|
|
453
|
+
fire_at timestamptz NOT NULL,
|
|
454
|
+
topic text NOT NULL,
|
|
455
|
+
handler text NOT NULL,
|
|
456
|
+
prompt text,
|
|
457
|
+
payload jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
458
|
+
priority smallint NOT NULL DEFAULT 50,
|
|
459
|
+
privileged boolean NOT NULL DEFAULT false,
|
|
460
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
461
|
+
CONSTRAINT scheduled_subagents_topic_format CHECK (topic ~ '${TOPIC_PATTERN}')
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
CREATE INDEX IF NOT EXISTS scheduled_subagents_fire_at_idx
|
|
465
|
+
ON scheduled_subagents (fire_at);
|
|
466
|
+
|
|
467
|
+
CREATE OR REPLACE FUNCTION scheduled_subagents_notify_changed() RETURNS trigger AS $$
|
|
468
|
+
DECLARE
|
|
469
|
+
payload_key text;
|
|
470
|
+
op text;
|
|
471
|
+
BEGIN
|
|
472
|
+
IF TG_OP = 'DELETE' THEN
|
|
473
|
+
payload_key := OLD.key;
|
|
474
|
+
op := 'd';
|
|
475
|
+
ELSE
|
|
476
|
+
payload_key := NEW.key;
|
|
477
|
+
op := 'u';
|
|
478
|
+
END IF;
|
|
479
|
+
PERFORM pg_notify('${SCHEDULED_SUBAGENTS_CHANNEL}', payload_key || ':' || op);
|
|
480
|
+
RETURN COALESCE(NEW, OLD);
|
|
481
|
+
END;
|
|
482
|
+
$$ LANGUAGE plpgsql;
|
|
483
|
+
|
|
484
|
+
DROP TRIGGER IF EXISTS scheduled_subagents_notify_changed_trg ON scheduled_subagents;
|
|
485
|
+
CREATE TRIGGER scheduled_subagents_notify_changed_trg
|
|
486
|
+
AFTER INSERT OR UPDATE OR DELETE ON scheduled_subagents
|
|
487
|
+
FOR EACH ROW EXECUTE FUNCTION scheduled_subagents_notify_changed();
|
|
488
|
+
|
|
489
|
+
-- ───────── inference_events ─────────
|
|
490
|
+
--
|
|
491
|
+
-- Append-only audit trail of every upstream model HTTP call attempt.
|
|
492
|
+
-- Written by AgentRunner on each generate() outcome (success / retryable /
|
|
493
|
+
-- fatal). Read by the host-side \`inference_status\` reflection tool to
|
|
494
|
+
-- answer "is this model misbehaving right now?". One row per attempt;
|
|
495
|
+
-- retries write multiple rows. NULL agent_run_id is kept for runs whose
|
|
496
|
+
-- row was later deleted (parent event cascade) so the historical signal
|
|
497
|
+
-- survives.
|
|
498
|
+
CREATE TABLE IF NOT EXISTS inference_events (
|
|
499
|
+
id bigserial PRIMARY KEY,
|
|
500
|
+
provider text NOT NULL,
|
|
501
|
+
model text NOT NULL,
|
|
502
|
+
agent_run_id bigint REFERENCES agentruns(id) ON DELETE SET NULL,
|
|
503
|
+
outcome text NOT NULL CHECK (outcome IN ('success','retryable','fatal')),
|
|
504
|
+
status_code int,
|
|
505
|
+
error_excerpt text,
|
|
506
|
+
occurred_at timestamptz NOT NULL DEFAULT now()
|
|
507
|
+
);
|
|
508
|
+
|
|
509
|
+
CREATE INDEX IF NOT EXISTS inference_events_model_time_idx
|
|
510
|
+
ON inference_events (model, occurred_at DESC);
|
|
511
|
+
CREATE INDEX IF NOT EXISTS inference_events_occurred_at_idx
|
|
512
|
+
ON inference_events (occurred_at DESC);
|
|
513
|
+
|
|
514
|
+
-- ───────── tool_calls ─────────
|
|
515
|
+
--
|
|
516
|
+
-- Append-only record of every tool invocation an agentrun attempted,
|
|
517
|
+
-- tagged with the resolved handler markdown path and whether the call
|
|
518
|
+
-- threw. Written by the tool wrapper in the container's ToolsFactory on
|
|
519
|
+
-- each execute() outcome. Read by the heuristic tool-preloader
|
|
520
|
+
-- (ToolCallBus.topToolsForHandler): the tools a handler successfully
|
|
521
|
+
-- used across its recent non-error runs get loaded up front on the next
|
|
522
|
+
-- run, so warm handlers rarely need a tool_list / tool_call round-trip.
|
|
523
|
+
-- NULL agent_run_id survives a parent-event cascade so the historical
|
|
524
|
+
-- usage signal outlives the run row.
|
|
525
|
+
CREATE TABLE IF NOT EXISTS tool_calls (
|
|
526
|
+
id bigserial PRIMARY KEY,
|
|
527
|
+
agent_run_id bigint REFERENCES agentruns(id) ON DELETE SET NULL,
|
|
528
|
+
handler_path text NOT NULL,
|
|
529
|
+
tool_name text NOT NULL,
|
|
530
|
+
successful boolean NOT NULL,
|
|
531
|
+
created_at timestamptz NOT NULL DEFAULT now()
|
|
532
|
+
);
|
|
533
|
+
|
|
534
|
+
CREATE INDEX IF NOT EXISTS tool_calls_handler_run_idx
|
|
535
|
+
ON tool_calls (handler_path, agent_run_id DESC);
|
|
536
|
+
CREATE INDEX IF NOT EXISTS tool_calls_created_at_idx
|
|
537
|
+
ON tool_calls (created_at DESC);
|
|
538
|
+
`;
|
|
539
|
+
/**
|
|
540
|
+
* SQL fragment that flips the parent event's terminal state when the
|
|
541
|
+
* **root** agentrun settles. Run inside the same transaction as the
|
|
542
|
+
* agentrun's terminal UPDATE.
|
|
543
|
+
*
|
|
544
|
+
* The event's outcome mirrors the root agentrun's outcome. Non-root
|
|
545
|
+
* agentruns — `start_subagent` children that surface their result back
|
|
546
|
+
* to the parent, and `schedule_subagent` immediate-mode children that
|
|
547
|
+
* the parent fired and forgot — are out-of-band side effects. They
|
|
548
|
+
* never flip the event on their own; the root decides.
|
|
549
|
+
*
|
|
550
|
+
* Settling a non-root agentrun is therefore a no-op for this update
|
|
551
|
+
* (the EXISTS guard fails). Settling the root writes the event's
|
|
552
|
+
* terminal state to the root's terminal state directly.
|
|
553
|
+
*
|
|
554
|
+
* Parameters (in order):
|
|
555
|
+
* $1 = event_id (bigint, as string)
|
|
556
|
+
* $2 = id of the just-settled agentrun (bigint, as string)
|
|
557
|
+
* $3 = state of the just-settled agentrun ('done' | 'failed')
|
|
558
|
+
*/
|
|
559
|
+
export const EVENT_TERMINAL_UPDATE_SQL = `
|
|
560
|
+
UPDATE events
|
|
561
|
+
SET state = $3, updated_at = now()
|
|
562
|
+
WHERE id = $1
|
|
563
|
+
AND state = 'running'
|
|
564
|
+
AND EXISTS (
|
|
565
|
+
SELECT 1 FROM agentruns
|
|
566
|
+
WHERE id = $2
|
|
567
|
+
AND event_id = $1
|
|
568
|
+
AND parent_agentrun_id IS NULL
|
|
569
|
+
)
|
|
570
|
+
`;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One row in the `stepresults` audit table — a single step inside an
|
|
3
|
+
* {@link AgentRunRow}'s tool-loop.
|
|
4
|
+
*
|
|
5
|
+
* Steps are immutable: the row is INSERTed once when the AI SDK's
|
|
6
|
+
* `onStepFinish` callback fires, and never updated.
|
|
7
|
+
*
|
|
8
|
+
* `id`, `agentRunId`, and `eventId` are kept as strings because
|
|
9
|
+
* postgres `bigint` exceeds JavaScript's safe integer range.
|
|
10
|
+
*/
|
|
11
|
+
export interface StepResultRow {
|
|
12
|
+
/** Database primary key. */
|
|
13
|
+
readonly id: string;
|
|
14
|
+
/** FK to {@link AgentRunRow.id} — the agentrun this step belongs to. */
|
|
15
|
+
readonly agentRunId: string;
|
|
16
|
+
/**
|
|
17
|
+
* FK to {@link EventRow.id} — denormalized from the agentrun so
|
|
18
|
+
* subscribers can route on the channel payload without a JOIN.
|
|
19
|
+
*/
|
|
20
|
+
readonly eventId: string;
|
|
21
|
+
/** Zero-based index of this step within its agentrun. */
|
|
22
|
+
readonly stepNumber: number;
|
|
23
|
+
/**
|
|
24
|
+
* `'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error'
|
|
25
|
+
* | 'other'` per the AI SDK's `FinishReason`. Stored as text so
|
|
26
|
+
* forward-compatible with new SDK values.
|
|
27
|
+
*/
|
|
28
|
+
readonly finishReason: string;
|
|
29
|
+
/** The assistant text generated in this step (may be empty when only tool calls were made). */
|
|
30
|
+
readonly resultText: string | null;
|
|
31
|
+
/** Text-form chain-of-thought from extended-thinking models, when emitted. */
|
|
32
|
+
readonly reasoningText: string | null;
|
|
33
|
+
/** Prompt / input tokens for this step. SDK marks this `number | undefined`. */
|
|
34
|
+
readonly inputTokens: number | null;
|
|
35
|
+
/** Completion / output tokens for this step. SDK marks this `number | undefined`. */
|
|
36
|
+
readonly outputTokens: number | null;
|
|
37
|
+
/** `inputTokens + outputTokens` when both are present; `null` otherwise. Denormalized for cheap SUM(). */
|
|
38
|
+
readonly totalTokens: number | null;
|
|
39
|
+
/**
|
|
40
|
+
* Per-step input-token breakdown — mirrors the AI SDK's
|
|
41
|
+
* `LanguageModelUsage.inputTokenDetails`. `noCacheTokens` are
|
|
42
|
+
* input tokens billed at the full rate; `cacheReadTokens` are
|
|
43
|
+
* cache hits (cheap); `cacheWriteTokens` are cache writes
|
|
44
|
+
* (sometimes premium-priced, e.g. Anthropic). All `null` when
|
|
45
|
+
* the provider doesn't report the detail.
|
|
46
|
+
*/
|
|
47
|
+
readonly inputTokensNoCache: number | null;
|
|
48
|
+
readonly inputTokensCacheRead: number | null;
|
|
49
|
+
readonly inputTokensCacheWrite: number | null;
|
|
50
|
+
/**
|
|
51
|
+
* Per-step output-token breakdown — mirrors the AI SDK's
|
|
52
|
+
* `LanguageModelUsage.outputTokenDetails`. `textTokens` are
|
|
53
|
+
* normal completion tokens; `reasoningTokens` are extended-
|
|
54
|
+
* thinking tokens (charged separately by some providers).
|
|
55
|
+
* Both `null` when the provider doesn't report the detail.
|
|
56
|
+
*/
|
|
57
|
+
readonly outputTokensText: number | null;
|
|
58
|
+
readonly outputTokensReasoning: number | null;
|
|
59
|
+
/** Cached `length(toolCalls)` for indexable filtering. */
|
|
60
|
+
readonly toolCallCount: number;
|
|
61
|
+
/** Full `StepResult.toolCalls` array. Opaque to us; the SDK types it as `TypedToolCall<TOOLS>[]`. */
|
|
62
|
+
readonly toolCalls: unknown;
|
|
63
|
+
/** Full `StepResult.toolResults` array. Opaque to us. */
|
|
64
|
+
readonly toolResults: unknown;
|
|
65
|
+
/**
|
|
66
|
+
* Full SDK step object captured as JSON for diagnosis. `null`
|
|
67
|
+
* unless `inference.captureRawStepResultToDatabase: true` is set
|
|
68
|
+
* in `config.yml`. Useful when chasing provider-specific fields
|
|
69
|
+
* (Anthropic cache stats, OpenAI logprobs, …) that don't have
|
|
70
|
+
* dedicated columns.
|
|
71
|
+
*/
|
|
72
|
+
readonly rawResult: unknown;
|
|
73
|
+
/** Insert timestamp. */
|
|
74
|
+
readonly createdAt: Date;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Input shape for {@link StepResultBus.add}. `totalTokens` and
|
|
78
|
+
* `toolCallCount` are derived at insert time and not provided here.
|
|
79
|
+
*/
|
|
80
|
+
export interface NewStepResult {
|
|
81
|
+
readonly agentRunId: string;
|
|
82
|
+
readonly eventId: string;
|
|
83
|
+
readonly stepNumber: number;
|
|
84
|
+
readonly finishReason: string;
|
|
85
|
+
readonly resultText?: string | null;
|
|
86
|
+
readonly reasoningText?: string | null;
|
|
87
|
+
readonly inputTokens?: number | null;
|
|
88
|
+
readonly outputTokens?: number | null;
|
|
89
|
+
readonly inputTokensNoCache?: number | null;
|
|
90
|
+
readonly inputTokensCacheRead?: number | null;
|
|
91
|
+
readonly inputTokensCacheWrite?: number | null;
|
|
92
|
+
readonly outputTokensText?: number | null;
|
|
93
|
+
readonly outputTokensReasoning?: number | null;
|
|
94
|
+
readonly toolCalls?: unknown;
|
|
95
|
+
readonly toolResults?: unknown;
|
|
96
|
+
/**
|
|
97
|
+
* Full SDK step object to capture as JSON. Pass through only
|
|
98
|
+
* when the operator has opted into raw capture; pass `undefined`
|
|
99
|
+
* (or omit) for the common case where the column should stay
|
|
100
|
+
* NULL.
|
|
101
|
+
*/
|
|
102
|
+
readonly rawResult?: unknown;
|
|
103
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|