talon-agent 3.0.5 → 3.1.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.
Files changed (46) hide show
  1. package/package.json +6 -1
  2. package/src/backend/kilo/models/index.ts +4 -1
  3. package/src/backend/kilo/server.ts +17 -4
  4. package/src/backend/openai-agents/session.ts +1 -1
  5. package/src/backend/opencode/models/index.ts +4 -1
  6. package/src/backend/opencode/server.ts +17 -4
  7. package/src/backend/shared/system-prompt.ts +5 -0
  8. package/src/cli/events.ts +1 -1
  9. package/src/cli/tasks.ts +5 -1
  10. package/src/core/background/triggers/pid.ts +28 -0
  11. package/src/core/background/triggers/resume.ts +4 -24
  12. package/src/core/background/triggers/spawn.ts +1 -1
  13. package/src/core/engine/gateway-actions/plugins.ts +3 -3
  14. package/src/core/prompt/invalidation.ts +23 -0
  15. package/src/core/types.ts +2 -2
  16. package/src/frontend/discord/callbacks/components.ts +1 -1
  17. package/src/frontend/discord/commands/settings.ts +1 -1
  18. package/src/frontend/native/extensions.ts +2 -2
  19. package/src/frontend/telegram/callbacks/model.ts +1 -1
  20. package/src/frontend/telegram/commands/settings.ts +1 -1
  21. package/src/frontend/terminal/commands.ts +1 -1
  22. package/src/plugins/github/index.ts +1 -1
  23. package/src/plugins/mem0/index.ts +1 -1
  24. package/src/plugins/mempalace/index.ts +1 -1
  25. package/src/plugins/playwright/index.ts +1 -1
  26. package/src/storage/chat-settings.ts +3 -60
  27. package/src/storage/cron-store.ts +6 -80
  28. package/src/storage/goal-store.ts +10 -19
  29. package/src/storage/history.ts +2 -13
  30. package/src/storage/journal.ts +20 -9
  31. package/src/storage/media-index.ts +2 -22
  32. package/src/storage/repositories/chat-settings-repo.ts +55 -1
  33. package/src/storage/repositories/cron-repo.ts +82 -6
  34. package/src/storage/repositories/goals-repo.ts +20 -1
  35. package/src/storage/repositories/history-repo.ts +14 -1
  36. package/src/storage/repositories/media-index-repo.ts +23 -1
  37. package/src/storage/repositories/scripts-repo.ts +16 -1
  38. package/src/storage/repositories/sessions-repo.ts +1 -1
  39. package/src/storage/repositories/triggers-repo.ts +60 -5
  40. package/src/storage/script-store.ts +2 -15
  41. package/src/storage/session-record.ts +220 -0
  42. package/src/storage/sessions.ts +22 -210
  43. package/src/storage/trigger-store.ts +6 -60
  44. package/src/types/assets.d.ts +9 -0
  45. package/src/types/effort.ts +12 -0
  46. package/tsconfig.json +6 -0
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Session record shapes + their constructors and normalisers — the
3
+ * vocabulary both the public store (storage/sessions.ts) and the
4
+ * repository (repositories/sessions-repo.ts) speak, extracted so the
5
+ * repo never has to import the store (which imports the repo).
6
+ *
7
+ * Mutation logic (fold/add/bucket) stays in the store; this module owns
8
+ * only "what a well-formed record looks like" and "how a raw JSON blob
9
+ * becomes one".
10
+ */
11
+
12
+ export type SessionUsage = {
13
+ totalInputTokens: number;
14
+ totalOutputTokens: number;
15
+ totalCacheRead: number;
16
+ totalCacheWrite: number;
17
+ /** Last turn's total prompt tokens (cumulative across all API calls in the turn, including tool-use loops). */
18
+ lastPromptTokens: number;
19
+ /** Actual context window fill from the last API call (last iteration's prompt tokens). */
20
+ contextTokens: number;
21
+ /** Model's context window size in tokens (from SDK modelUsage). */
22
+ contextWindow: number;
23
+ /** Number of API round-trips in the last turn (tool-use steps). */
24
+ numApiCalls: number;
25
+ /** Estimated cost in USD. */
26
+ estimatedCostUsd: number;
27
+ /** Total response time in ms (for averaging). */
28
+ totalResponseMs: number;
29
+ /** Last response time in ms. */
30
+ lastResponseMs: number;
31
+ /** Fastest response time in ms. */
32
+ fastestResponseMs: number;
33
+ };
34
+
35
+ export type MetricsCounterSet = {
36
+ queries: number;
37
+ toolCalls: number;
38
+ turnsWithTools: number;
39
+ apiCalls: number;
40
+ inputTokens: number;
41
+ outputTokens: number;
42
+ cacheReadTokens: number;
43
+ cacheWriteTokens: number;
44
+ failedTurns: number;
45
+ flowViolationRetries: number;
46
+ flowViolationCapExhausted: number;
47
+ trailingTextDropped: number;
48
+ };
49
+
50
+ export type MetricsLatencyAgg = {
51
+ count: number;
52
+ sumMs: number;
53
+ minMs: number;
54
+ maxMs: number;
55
+ };
56
+
57
+ export type MetricsGrain = {
58
+ counters: MetricsCounterSet;
59
+ latency: MetricsLatencyAgg;
60
+ toolCallsByName: Record<string, number>;
61
+ backend: Record<string, MetricsCounterSet & { latency: MetricsLatencyAgg }>;
62
+ cacheHitPercent: MetricsLatencyAgg;
63
+ toolCallsPerTurn: MetricsLatencyAgg;
64
+ apiCallsPerTurn: MetricsLatencyAgg;
65
+ };
66
+
67
+ export type SessionMetrics = {
68
+ lifetime: MetricsGrain;
69
+ buckets: Record<string, MetricsGrain>;
70
+ };
71
+
72
+ export type SessionState = {
73
+ /** Backend server-side session ID. */
74
+ sessionId: string | undefined;
75
+ /** Turn count. */
76
+ turns: number;
77
+ /** Last activity timestamp. */
78
+ lastActive: number;
79
+ /** Created timestamp. */
80
+ createdAt: number;
81
+ /** Cumulative usage stats. */
82
+ usage: SessionUsage;
83
+ /** Persistent per-session metrics, bucketed by UTC day. */
84
+ metrics: SessionMetrics;
85
+ /** ID of the last message sent by the bot in this chat. */
86
+ lastBotMessageId?: number;
87
+ /** Descriptive session name derived from first message. */
88
+ sessionName?: string;
89
+ /** Model used for this session's cost tracking. */
90
+ lastModel?: string;
91
+ };
92
+
93
+ export const emptyUsage = (): SessionUsage => ({
94
+ totalInputTokens: 0,
95
+ totalOutputTokens: 0,
96
+ totalCacheRead: 0,
97
+ totalCacheWrite: 0,
98
+ lastPromptTokens: 0,
99
+ contextTokens: 0,
100
+ contextWindow: 0,
101
+ numApiCalls: 0,
102
+ estimatedCostUsd: 0,
103
+ totalResponseMs: 0,
104
+ lastResponseMs: 0,
105
+ fastestResponseMs: Infinity,
106
+ });
107
+
108
+ export const emptyCounters = (): MetricsCounterSet => ({
109
+ queries: 0,
110
+ toolCalls: 0,
111
+ turnsWithTools: 0,
112
+ apiCalls: 0,
113
+ inputTokens: 0,
114
+ outputTokens: 0,
115
+ cacheReadTokens: 0,
116
+ cacheWriteTokens: 0,
117
+ failedTurns: 0,
118
+ flowViolationRetries: 0,
119
+ flowViolationCapExhausted: 0,
120
+ trailingTextDropped: 0,
121
+ });
122
+
123
+ export const emptyLatency = (): MetricsLatencyAgg => ({
124
+ count: 0,
125
+ sumMs: 0,
126
+ minMs: Infinity,
127
+ maxMs: 0,
128
+ });
129
+
130
+ export const emptyGrain = (): MetricsGrain => ({
131
+ counters: emptyCounters(),
132
+ latency: emptyLatency(),
133
+ toolCallsByName: {},
134
+ backend: {},
135
+ cacheHitPercent: emptyLatency(),
136
+ toolCallsPerTurn: emptyLatency(),
137
+ apiCallsPerTurn: emptyLatency(),
138
+ });
139
+
140
+ export const emptyMetrics = (): SessionMetrics => ({
141
+ lifetime: emptyGrain(),
142
+ buckets: {},
143
+ });
144
+
145
+ function normaliseLatency(raw: unknown): MetricsLatencyAgg {
146
+ const r = raw as Partial<MetricsLatencyAgg> | undefined;
147
+ const count =
148
+ typeof r?.count === "number" && Number.isFinite(r.count) ? r.count : 0;
149
+ return {
150
+ count,
151
+ sumMs:
152
+ typeof r?.sumMs === "number" && Number.isFinite(r.sumMs) ? r.sumMs : 0,
153
+ minMs:
154
+ typeof r?.minMs === "number" && Number.isFinite(r.minMs)
155
+ ? r.minMs
156
+ : count > 0
157
+ ? 0
158
+ : Infinity,
159
+ maxMs:
160
+ typeof r?.maxMs === "number" && Number.isFinite(r.maxMs) ? r.maxMs : 0,
161
+ };
162
+ }
163
+
164
+ function normaliseCounters(raw: unknown): MetricsCounterSet {
165
+ const base = emptyCounters();
166
+ const r = raw as Partial<MetricsCounterSet> | undefined;
167
+ for (const key of Object.keys(base) as Array<keyof MetricsCounterSet>) {
168
+ const value = r?.[key];
169
+ if (typeof value === "number" && Number.isFinite(value)) base[key] = value;
170
+ }
171
+ return base;
172
+ }
173
+
174
+ function normaliseGrain(raw: unknown): MetricsGrain {
175
+ const r = raw as Partial<MetricsGrain> | undefined;
176
+ const grain = emptyGrain();
177
+ grain.counters = normaliseCounters(r?.counters);
178
+ grain.latency = normaliseLatency(r?.latency);
179
+ grain.cacheHitPercent = normaliseLatency(r?.cacheHitPercent);
180
+ grain.toolCallsPerTurn = normaliseLatency(r?.toolCallsPerTurn);
181
+ grain.apiCallsPerTurn = normaliseLatency(r?.apiCallsPerTurn);
182
+ if (r?.toolCallsByName && typeof r.toolCallsByName === "object") {
183
+ for (const [key, value] of Object.entries(r.toolCallsByName)) {
184
+ if (typeof value === "number" && Number.isFinite(value)) {
185
+ grain.toolCallsByName[key] = value;
186
+ }
187
+ }
188
+ }
189
+ if (r?.backend && typeof r.backend === "object") {
190
+ for (const [key, value] of Object.entries(r.backend)) {
191
+ grain.backend[key] = {
192
+ ...normaliseCounters(value),
193
+ latency: normaliseLatency((value as { latency?: unknown }).latency),
194
+ };
195
+ }
196
+ }
197
+ return grain;
198
+ }
199
+
200
+ /**
201
+ * Coerce an untrusted/persisted metrics blob into a well-formed
202
+ * SessionMetrics: missing fields backfilled, non-finite numbers dropped,
203
+ * JSON's `minMs: null` (Infinity doesn't survive JSON.stringify) restored
204
+ * to the domain sentinel. Runs once at the load boundary (sessions-repo);
205
+ * in-memory metrics stay well-formed by construction after that.
206
+ */
207
+ export function normaliseMetrics(metrics: unknown): SessionMetrics {
208
+ const raw = metrics as Partial<SessionMetrics> | undefined;
209
+ const result: SessionMetrics = {
210
+ lifetime: normaliseGrain(raw?.lifetime),
211
+ buckets: {},
212
+ };
213
+ if (raw?.buckets && typeof raw.buckets === "object") {
214
+ for (const [day, grain] of Object.entries(raw.buckets)) {
215
+ if (/^\d{4}-\d{2}-\d{2}$/.test(day))
216
+ result.buckets[day] = normaliseGrain(grain);
217
+ }
218
+ }
219
+ return result;
220
+ }
@@ -24,87 +24,28 @@ import { files } from "../util/paths.js";
24
24
  import { importLegacyJson } from "./legacy-import.js";
25
25
  import * as repo from "./repositories/sessions-repo.js";
26
26
 
27
- export type SessionUsage = {
28
- totalInputTokens: number;
29
- totalOutputTokens: number;
30
- totalCacheRead: number;
31
- totalCacheWrite: number;
32
- /** Last turn's total prompt tokens (cumulative across all API calls in the turn, including tool-use loops). */
33
- lastPromptTokens: number;
34
- /** Actual context window fill from the last API call (last iteration's prompt tokens). */
35
- contextTokens: number;
36
- /** Model's context window size in tokens (from SDK modelUsage). */
37
- contextWindow: number;
38
- /** Number of API round-trips in the last turn (tool-use steps). */
39
- numApiCalls: number;
40
- /** Estimated cost in USD. */
41
- estimatedCostUsd: number;
42
- /** Total response time in ms (for averaging). */
43
- totalResponseMs: number;
44
- /** Last response time in ms. */
45
- lastResponseMs: number;
46
- /** Fastest response time in ms. */
47
- fastestResponseMs: number;
48
- };
49
-
50
- export type MetricsCounterSet = {
51
- queries: number;
52
- toolCalls: number;
53
- turnsWithTools: number;
54
- apiCalls: number;
55
- inputTokens: number;
56
- outputTokens: number;
57
- cacheReadTokens: number;
58
- cacheWriteTokens: number;
59
- failedTurns: number;
60
- flowViolationRetries: number;
61
- flowViolationCapExhausted: number;
62
- trailingTextDropped: number;
63
- };
64
-
65
- export type MetricsLatencyAgg = {
66
- count: number;
67
- sumMs: number;
68
- minMs: number;
69
- maxMs: number;
70
- };
71
-
72
- export type MetricsGrain = {
73
- counters: MetricsCounterSet;
74
- latency: MetricsLatencyAgg;
75
- toolCallsByName: Record<string, number>;
76
- backend: Record<string, MetricsCounterSet & { latency: MetricsLatencyAgg }>;
77
- cacheHitPercent: MetricsLatencyAgg;
78
- toolCallsPerTurn: MetricsLatencyAgg;
79
- apiCallsPerTurn: MetricsLatencyAgg;
80
- };
81
-
82
- export type SessionMetrics = {
83
- lifetime: MetricsGrain;
84
- buckets: Record<string, MetricsGrain>;
85
- };
86
-
87
- export type SessionState = {
88
- /** Backend server-side session ID. */
89
- sessionId: string | undefined;
90
- /** Turn count. */
91
- turns: number;
92
- /** Last activity timestamp. */
93
- lastActive: number;
94
- /** Created timestamp. */
95
- createdAt: number;
96
- /** Cumulative usage stats. */
97
- usage: SessionUsage;
98
- /** Persistent per-session metrics, bucketed by UTC day. */
99
- metrics: SessionMetrics;
100
- /** ID of the last message sent by the bot in this chat. */
101
- lastBotMessageId?: number;
102
- /** Descriptive session name derived from first message. */
103
- sessionName?: string;
104
- /** Model used for this session's cost tracking. */
105
- lastModel?: string;
106
- };
107
-
27
+ export type {
28
+ MetricsCounterSet,
29
+ MetricsGrain,
30
+ MetricsLatencyAgg,
31
+ SessionMetrics,
32
+ SessionState,
33
+ SessionUsage,
34
+ } from "./session-record.js";
35
+ export { emptyMetrics, normaliseMetrics } from "./session-record.js";
36
+ import {
37
+ emptyCounters,
38
+ emptyGrain,
39
+ emptyLatency,
40
+ emptyMetrics,
41
+ emptyUsage,
42
+ type MetricsCounterSet,
43
+ type MetricsGrain,
44
+ type MetricsLatencyAgg,
45
+ type SessionMetrics,
46
+ type SessionState,
47
+ type SessionUsage,
48
+ } from "./session-record.js";
108
49
  // In-memory cache over the sessions table. Reads serve live references
109
50
  // from here; writes go through persist() so each mutation commits.
110
51
  const cache = new Map<string, SessionState>();
@@ -273,135 +214,6 @@ function persist(chatId: string, session: SessionState): void {
273
214
 
274
215
  // ── Core operations ─────────────────────────────────────────────────────────
275
216
 
276
- const emptyUsage = (): SessionUsage => ({
277
- totalInputTokens: 0,
278
- totalOutputTokens: 0,
279
- totalCacheRead: 0,
280
- totalCacheWrite: 0,
281
- lastPromptTokens: 0,
282
- contextTokens: 0,
283
- contextWindow: 0,
284
- numApiCalls: 0,
285
- estimatedCostUsd: 0,
286
- totalResponseMs: 0,
287
- lastResponseMs: 0,
288
- fastestResponseMs: Infinity,
289
- });
290
-
291
- const emptyCounters = (): MetricsCounterSet => ({
292
- queries: 0,
293
- toolCalls: 0,
294
- turnsWithTools: 0,
295
- apiCalls: 0,
296
- inputTokens: 0,
297
- outputTokens: 0,
298
- cacheReadTokens: 0,
299
- cacheWriteTokens: 0,
300
- failedTurns: 0,
301
- flowViolationRetries: 0,
302
- flowViolationCapExhausted: 0,
303
- trailingTextDropped: 0,
304
- });
305
-
306
- const emptyLatency = (): MetricsLatencyAgg => ({
307
- count: 0,
308
- sumMs: 0,
309
- minMs: Infinity,
310
- maxMs: 0,
311
- });
312
-
313
- const emptyGrain = (): MetricsGrain => ({
314
- counters: emptyCounters(),
315
- latency: emptyLatency(),
316
- toolCallsByName: {},
317
- backend: {},
318
- cacheHitPercent: emptyLatency(),
319
- toolCallsPerTurn: emptyLatency(),
320
- apiCallsPerTurn: emptyLatency(),
321
- });
322
-
323
- export const emptyMetrics = (): SessionMetrics => ({
324
- lifetime: emptyGrain(),
325
- buckets: {},
326
- });
327
-
328
- function normaliseLatency(raw: unknown): MetricsLatencyAgg {
329
- const r = raw as Partial<MetricsLatencyAgg> | undefined;
330
- const count =
331
- typeof r?.count === "number" && Number.isFinite(r.count) ? r.count : 0;
332
- return {
333
- count,
334
- sumMs:
335
- typeof r?.sumMs === "number" && Number.isFinite(r.sumMs) ? r.sumMs : 0,
336
- minMs:
337
- typeof r?.minMs === "number" && Number.isFinite(r.minMs)
338
- ? r.minMs
339
- : count > 0
340
- ? 0
341
- : Infinity,
342
- maxMs:
343
- typeof r?.maxMs === "number" && Number.isFinite(r.maxMs) ? r.maxMs : 0,
344
- };
345
- }
346
-
347
- function normaliseCounters(raw: unknown): MetricsCounterSet {
348
- const base = emptyCounters();
349
- const r = raw as Partial<MetricsCounterSet> | undefined;
350
- for (const key of Object.keys(base) as Array<keyof MetricsCounterSet>) {
351
- const value = r?.[key];
352
- if (typeof value === "number" && Number.isFinite(value)) base[key] = value;
353
- }
354
- return base;
355
- }
356
-
357
- function normaliseGrain(raw: unknown): MetricsGrain {
358
- const r = raw as Partial<MetricsGrain> | undefined;
359
- const grain = emptyGrain();
360
- grain.counters = normaliseCounters(r?.counters);
361
- grain.latency = normaliseLatency(r?.latency);
362
- grain.cacheHitPercent = normaliseLatency(r?.cacheHitPercent);
363
- grain.toolCallsPerTurn = normaliseLatency(r?.toolCallsPerTurn);
364
- grain.apiCallsPerTurn = normaliseLatency(r?.apiCallsPerTurn);
365
- if (r?.toolCallsByName && typeof r.toolCallsByName === "object") {
366
- for (const [key, value] of Object.entries(r.toolCallsByName)) {
367
- if (typeof value === "number" && Number.isFinite(value)) {
368
- grain.toolCallsByName[key] = value;
369
- }
370
- }
371
- }
372
- if (r?.backend && typeof r.backend === "object") {
373
- for (const [key, value] of Object.entries(r.backend)) {
374
- grain.backend[key] = {
375
- ...normaliseCounters(value),
376
- latency: normaliseLatency((value as { latency?: unknown }).latency),
377
- };
378
- }
379
- }
380
- return grain;
381
- }
382
-
383
- /**
384
- * Coerce an untrusted/persisted metrics blob into a well-formed
385
- * SessionMetrics: missing fields backfilled, non-finite numbers dropped,
386
- * JSON's `minMs: null` (Infinity doesn't survive JSON.stringify) restored
387
- * to the domain sentinel. Runs once at the load boundary (sessions-repo);
388
- * in-memory metrics stay well-formed by construction after that.
389
- */
390
- export function normaliseMetrics(metrics: unknown): SessionMetrics {
391
- const raw = metrics as Partial<SessionMetrics> | undefined;
392
- const result: SessionMetrics = {
393
- lifetime: normaliseGrain(raw?.lifetime),
394
- buckets: {},
395
- };
396
- if (raw?.buckets && typeof raw.buckets === "object") {
397
- for (const [day, grain] of Object.entries(raw.buckets)) {
398
- if (/^\d{4}-\d{2}-\d{2}$/.test(day))
399
- result.buckets[day] = normaliseGrain(grain);
400
- }
401
- }
402
- return result;
403
- }
404
-
405
217
  function addLatency(agg: MetricsLatencyAgg, value: number): void {
406
218
  if (!Number.isFinite(value)) return;
407
219
  agg.count += 1;
@@ -27,66 +27,12 @@ import * as repo from "./repositories/triggers-repo.js";
27
27
 
28
28
  // ── Types ────────────────────────────────────────────────────────────────────
29
29
 
30
- export type TriggerLanguage = "bash" | "python" | "node" | "lua";
31
-
32
- export type TriggerStatus =
33
- | "pending" // created, not yet spawned (transient)
34
- | "running" // child process alive
35
- | "fired" // exited 0 fired final wake message
36
- | "errored" // exited non-zero — fired error wake message
37
- | "cancelled" // killed by user (trigger_cancel)
38
- | "timed_out" // killed by hard timeout
39
- | "terminated"; // killed by Talon shutdown / restart
40
-
41
- export type Trigger = {
42
- id: string;
43
- chatId: string;
44
- numericChatId: number;
45
- name: string;
46
- language: TriggerLanguage;
47
- /** Absolute path to the script body on disk. */
48
- scriptPath: string;
49
- /** Absolute path to the run log (interleaved stdout+stderr). */
50
- logPath: string;
51
- description?: string;
52
- status: TriggerStatus;
53
- createdAt: number;
54
- startedAt?: number;
55
- endedAt?: number;
56
- /** PID of the child process while running (cleared on exit). */
57
- pid?: number;
58
- /** Linux /proc/<pid>/stat field 22 (start time in jiffies) captured at
59
- * spawn. Used by killOrphan to defend against PID reuse — start time is
60
- * monotonic per boot and unchanged by exec(), so a match guarantees the
61
- * current owner of the PID is the same process we spawned. Undefined on
62
- * non-Linux platforms. */
63
- pidStarttime?: number;
64
- /** Hard timeout in seconds. Default 24h, max 7d. */
65
- timeoutSeconds: number;
66
- /** Exit code on terminal status. */
67
- exitCode?: number;
68
- /** Total wake-ups fired for this trigger — sum of mid-run TALON_FIRE: lines
69
- * plus the terminal exit fire. Incremented every time fireWake() runs. */
70
- fireCount: number;
71
- lastFireAt?: number;
72
- /** Truncated tail of the most recent fire payload (for diagnostics). */
73
- lastFirePayload?: string;
74
- lastError?: string;
75
- /** If true, the trigger is respawned on Talon startup if it was still
76
- * active when Talon went down. Triggers in any terminal state
77
- * (fired/errored/cancelled) are NOT respawned — only ones interrupted
78
- * by Talon shutdown or crash. (Persistent triggers have no hard timeout,
79
- * so timed_out is unreachable for them — see spawnTrigger.) */
80
- persistent?: boolean;
81
- /**
82
- * Optional model override for the wake-up turn — a model id valid on the
83
- * chat's own backend. Unset = inherit the chat's model (preferred). When set,
84
- * the fired wake-up runs on this (typically cheaper) model instead, while
85
- * still resuming the chat session (restricted to the same backend so
86
- * continuity is preserved).
87
- */
88
- model?: string;
89
- };
30
+ export type {
31
+ Trigger,
32
+ TriggerLanguage,
33
+ TriggerStatus,
34
+ } from "./repositories/triggers-repo.js";
35
+ import type { Trigger, TriggerLanguage } from "./repositories/triggers-repo.js";
90
36
 
91
37
  // ── Constants ────────────────────────────────────────────────────────────────
92
38
 
@@ -0,0 +1,9 @@
1
+ // Ambient declaration for prompt file-asset imports. The Bun-only
2
+ // `import x from "./foo.md" with { type: "file" }` form (used only by the
3
+ // generated embedded-prompts.ts, reached through the `bun` condition of
4
+ // the `#prompt-assets` package import) resolves to the embedded file path
5
+ // as a string. tsx/node never load that module — they use disk-prompts.ts.
6
+ declare module "*.md" {
7
+ const path: string;
8
+ export default path;
9
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Reasoning-effort vocabulary — a dependency-free primitive shared by
3
+ * every layer (storage persists it, core resolves it, backends map it
4
+ * to provider knobs, frontends render pickers over it).
5
+ *
6
+ * Lives in src/types/ (a leaf: imports nothing from src/) so the layers
7
+ * below core/ can name the type without importing upward. core/types.ts
8
+ * re-exports it for the engine-side importers.
9
+ */
10
+
11
+ export type ReasoningEffortLevel =
12
+ "off" | "minimal" | "low" | "medium" | "high" | "max" | "xhigh";
package/tsconfig.json CHANGED
@@ -5,6 +5,12 @@
5
5
  "moduleResolution": "bundler",
6
6
  "resolveJsonModule": true,
7
7
  "strict": true,
8
+ // Beyond-strict gates. Two more are measured but not yet on — the fix
9
+ // waves are their own PRs: noUncheckedIndexedAccess (666 errors),
10
+ // exactOptionalPropertyTypes (303 errors).
11
+ "noImplicitOverride": true,
12
+ "noFallthroughCasesInSwitch": true,
13
+ "verbatimModuleSyntax": true,
8
14
  "esModuleInterop": true,
9
15
  "skipLibCheck": true,
10
16
  "outDir": "dist",