@cat-factory/node-server 0.159.0 → 0.161.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/dist/config.d.ts.map +1 -1
- package/dist/config.js +10 -1
- package/dist/config.js.map +1 -1
- package/dist/container-core-deps.d.ts +3 -1
- package/dist/container-core-deps.d.ts.map +1 -1
- package/dist/container-core-deps.js +52 -23
- package/dist/container-core-deps.js.map +1 -1
- package/dist/container-foundation.d.ts +3 -0
- package/dist/container-foundation.d.ts.map +1 -1
- package/dist/container-foundation.js +10 -2
- package/dist/container-foundation.js.map +1 -1
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +55 -25
- package/dist/container.js.map +1 -1
- package/dist/db/schema.d.ts +16 -787
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +6 -100
- package/dist/db/schema.js.map +1 -1
- package/dist/db/tables/observability.d.ts +1132 -0
- package/dist/db/tables/observability.d.ts.map +1 -0
- package/dist/db/tables/observability.js +173 -0
- package/dist/db/tables/observability.js.map +1 -0
- package/dist/repositories/drizzle/execution.d.ts +18 -1
- package/dist/repositories/drizzle/execution.d.ts.map +1 -1
- package/dist/repositories/drizzle/execution.js +215 -3
- package/dist/repositories/drizzle/execution.js.map +1 -1
- package/dist/repositories/drizzle/settings.d.ts.map +1 -1
- package/dist/repositories/drizzle/settings.js +5 -1
- package/dist/repositories/drizzle/settings.js.map +1 -1
- package/dist/repositories/drizzle.d.ts +2 -1
- package/dist/repositories/drizzle.d.ts.map +1 -1
- package/dist/repositories/drizzle.js +2 -1
- package/dist/repositories/drizzle.js.map +1 -1
- package/dist/retention.d.ts +7 -1
- package/dist/retention.d.ts.map +1 -1
- package/dist/retention.js +8 -1
- package/dist/retention.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +3 -0
- package/dist/server.js.map +1 -1
- package/drizzle/20260803220705_model_preset_provider_preference/migration.sql +1 -0
- package/drizzle/20260803220705_model_preset_provider_preference/snapshot.json +19091 -0
- package/drizzle/20260803222354_salty_albert_cleary/migration.sql +27 -0
- package/drizzle/20260803222354_salty_albert_cleary/snapshot.json +19417 -0
- package/drizzle/20260804081137_mean_sasquatch/migration.sql +5 -0
- package/drizzle/20260804081137_mean_sasquatch/snapshot.json +19472 -0
- package/package.json +20 -20
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observability.d.ts","sourceRoot":"","sources":["../../../src/db/tables/observability.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,SAAS,qDAAwB,CAAA;AAM9C,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgD1B,CAAA;AAQD,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuBjC,CAAA;AAMD,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiB9B,CAAA;AAOD;;;;;GAKG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsBxB,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAc3B,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAI9B,CAAA"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { bigint, index, integer, pgSchema, pgTable, primaryKey, text } from 'drizzle-orm/pg-core';
|
|
2
|
+
// The observability schema: the three append-heavy TELEMETRY sinks (one row per model call,
|
|
3
|
+
// per dispatched agent context, per web search a container agent performed) plus the two
|
|
4
|
+
// deployment-level PROJECTIONS the operator dashboard aggregates (settled gates, and the daily
|
|
5
|
+
// run rollup behind the long windows).
|
|
6
|
+
//
|
|
7
|
+
// Split out of `schema.ts` as a cohesive module (the `tables/` pattern the identity, settings
|
|
8
|
+
// and VCS groups already follow) when the platform projections pushed that file over its
|
|
9
|
+
// size budget: budgets are split triggers, never numbers to raise. Re-exported from
|
|
10
|
+
// `schema.ts`, so every `from '../db/schema.js'` importer is unchanged.
|
|
11
|
+
// Telemetry has a very different write profile from the transactional domain
|
|
12
|
+
// (append-heavy, high-volume, write-and-rarely-read, short retention), so it lives in
|
|
13
|
+
// its own `telemetry` Postgres schema rather than `public`. This is the Node analogue
|
|
14
|
+
// of the Cloudflare worker's separate TELEMETRY_DB D1 database. The schema is purely a
|
|
15
|
+
// namespace served by the same connection/pool; `migrate()` creates it on boot. The
|
|
16
|
+
// `llm_call_metrics` table and `agent_context_snapshots` table live here.
|
|
17
|
+
export const telemetry = pgSchema('telemetry');
|
|
18
|
+
// LLM observability sink (mirror of D1 migration 0026). One row per proxied
|
|
19
|
+
// container-agent model call: full prompt/response, output-limit headroom and the
|
|
20
|
+
// transport-vs-execution latency split. Pruned aggressively by retention (the full
|
|
21
|
+
// bodies make it heavy); booleans are integer 0/1 to match the SQLite store.
|
|
22
|
+
export const llmCallMetrics = telemetry.table('llm_call_metrics', {
|
|
23
|
+
id: text('id').primaryKey(),
|
|
24
|
+
workspace_id: text('workspace_id').notNull(),
|
|
25
|
+
execution_id: text('execution_id'),
|
|
26
|
+
agent_kind: text('agent_kind').notNull(),
|
|
27
|
+
provider: text('provider').notNull(),
|
|
28
|
+
model: text('model').notNull(),
|
|
29
|
+
created_at: bigint('created_at', { mode: 'number' }).notNull(),
|
|
30
|
+
streaming: integer('streaming').notNull().default(0),
|
|
31
|
+
// WHICH slice of the run spent the call (`agent` / `validation-repair` / … ), stamped by
|
|
32
|
+
// the harness that owns the phase boundary; '' is the unattributed slice, a real group in
|
|
33
|
+
// the rollup rather than a dropped row. `turn_index` is the harness's job-scoped `seq`,
|
|
34
|
+
// NULL where the producing channel has no turn concept (the proxy). Mirrors D1 migration
|
|
35
|
+
// 0004_llm_call_phase_turn. See docs/initiatives/token-burn-instrumentation.md.
|
|
36
|
+
phase: text('phase').notNull().default(''),
|
|
37
|
+
turn_index: integer('turn_index'),
|
|
38
|
+
message_count: integer('message_count').notNull().default(0),
|
|
39
|
+
tool_count: integer('tool_count').notNull().default(0),
|
|
40
|
+
request_max_tokens: integer('request_max_tokens'),
|
|
41
|
+
prompt_tokens: integer('prompt_tokens').notNull().default(0),
|
|
42
|
+
cache_read_tokens: integer('cache_read_tokens').notNull().default(0),
|
|
43
|
+
cache_write_tokens: integer('cache_write_tokens').notNull().default(0),
|
|
44
|
+
completion_tokens: integer('completion_tokens').notNull().default(0),
|
|
45
|
+
total_tokens: integer('total_tokens').notNull().default(0),
|
|
46
|
+
finish_reason: text('finish_reason'),
|
|
47
|
+
upstream_ms: integer('upstream_ms').notNull().default(0),
|
|
48
|
+
overhead_ms: integer('overhead_ms').notNull().default(0),
|
|
49
|
+
total_ms: integer('total_ms').notNull().default(0),
|
|
50
|
+
ok: integer('ok').notNull().default(1),
|
|
51
|
+
http_status: integer('http_status'),
|
|
52
|
+
error_message: text('error_message'),
|
|
53
|
+
// prompt_text is stored as a DELTA (only the messages this call appended beyond
|
|
54
|
+
// prompt_prefix_count); the full prompt is rebuilt on export. See D1 migration 0027.
|
|
55
|
+
prompt_text: text('prompt_text').notNull().default(''),
|
|
56
|
+
prompt_prefix_count: integer('prompt_prefix_count').notNull().default(0),
|
|
57
|
+
prompt_hash: text('prompt_hash').notNull().default(''),
|
|
58
|
+
response_text: text('response_text').notNull().default(''),
|
|
59
|
+
// The model's reasoning/"thinking" trace on a separate channel, when emitted (a
|
|
60
|
+
// reasoning model can spend its whole output budget here and return empty
|
|
61
|
+
// response_text). Mirrors D1 migration 0002_llm_reasoning_text.
|
|
62
|
+
reasoning_text: text('reasoning_text').notNull().default(''),
|
|
63
|
+
}, (t) => [
|
|
64
|
+
index('idx_llm_call_metrics_execution').on(t.workspace_id, t.execution_id, t.created_at),
|
|
65
|
+
index('idx_llm_call_metrics_created').on(t.created_at),
|
|
66
|
+
]);
|
|
67
|
+
// The complete, redacted context provided to one container-agent dispatch (per step
|
|
68
|
+
// attempt): the fully fragment-composed system + user prompts, the fragment bodies
|
|
69
|
+
// folded in, and the full content of the files injected into the container. Captures
|
|
70
|
+
// what proxy telemetry can't (the injected `.cat-context/*` files the agent reads via
|
|
71
|
+
// tools). JSON-shaped columns are text; pruned on the same retention window as
|
|
72
|
+
// llm_call_metrics. Mirrors the D1 agent_context_snapshots table column-for-column.
|
|
73
|
+
export const agentContextSnapshots = telemetry.table('agent_context_snapshots', {
|
|
74
|
+
id: text('id').primaryKey(),
|
|
75
|
+
workspace_id: text('workspace_id').notNull(),
|
|
76
|
+
execution_id: text('execution_id').notNull(),
|
|
77
|
+
agent_kind: text('agent_kind').notNull(),
|
|
78
|
+
step_index: integer('step_index').notNull(),
|
|
79
|
+
created_at: bigint('created_at', { mode: 'number' }).notNull(),
|
|
80
|
+
model: text('model'),
|
|
81
|
+
harness: text('harness'),
|
|
82
|
+
system_prompt: text('system_prompt').notNull().default(''),
|
|
83
|
+
user_prompt: text('user_prompt').notNull().default(''),
|
|
84
|
+
// JSON arrays: [{id, body}] and [{path, title, url, content}].
|
|
85
|
+
fragments: text('fragments').notNull().default('[]'),
|
|
86
|
+
context_files: text('context_files').notNull().default('[]'),
|
|
87
|
+
// Redacted structural bits (repo/branch, webSearch, infra, decisions, revision).
|
|
88
|
+
extras: text('extras').notNull().default('{}'),
|
|
89
|
+
}, (t) => [
|
|
90
|
+
index('idx_agent_context_snapshots_execution').on(t.workspace_id, t.execution_id, t.created_at),
|
|
91
|
+
index('idx_agent_context_snapshots_created').on(t.created_at),
|
|
92
|
+
]);
|
|
93
|
+
// One web search a container agent performed through the backend search proxy. Recorded
|
|
94
|
+
// best-effort (gated by the same LLM_RECORD_PROMPTS + storeAgentContext double switch as
|
|
95
|
+
// agent_context_snapshots) and pruned on the same retention window. Mirrors the D1
|
|
96
|
+
// agent_search_queries table column-for-column.
|
|
97
|
+
export const agentSearchQueries = telemetry.table('agent_search_queries', {
|
|
98
|
+
id: text('id').primaryKey(),
|
|
99
|
+
workspace_id: text('workspace_id').notNull(),
|
|
100
|
+
execution_id: text('execution_id').notNull(),
|
|
101
|
+
agent_kind: text('agent_kind').notNull(),
|
|
102
|
+
// The upstream backend that served the search (`brave` | `searxng`), or null.
|
|
103
|
+
provider: text('provider'),
|
|
104
|
+
query: text('query').notNull().default(''),
|
|
105
|
+
result_count: integer('result_count').notNull().default(0),
|
|
106
|
+
created_at: bigint('created_at', { mode: 'number' }).notNull(),
|
|
107
|
+
}, (t) => [
|
|
108
|
+
index('idx_agent_search_queries_execution').on(t.workspace_id, t.execution_id, t.created_at),
|
|
109
|
+
index('idx_agent_search_queries_created').on(t.created_at),
|
|
110
|
+
]);
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// Platform-operator observability projections (mirrors D1 migration 0079). Both are read by
|
|
113
|
+
// the account-scoped dashboard rollups; see the D1 migration for the full rationale.
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
/**
|
|
116
|
+
* One flat row per SETTLED polling gate, so the gate / CI-fixer attempt statistics are an
|
|
117
|
+
* ordinary aggregate over columns rather than a `jsonb_array_elements` expansion of the run's
|
|
118
|
+
* internal `steps[].gate.*` shape. The id is derived by the writer (`<runId>:<stepIndex>:
|
|
119
|
+
* <outcome>`) so a driver replay collapses onto one row.
|
|
120
|
+
*/
|
|
121
|
+
export const gateOutcomes = pgTable('gate_outcomes', {
|
|
122
|
+
id: text('id').primaryKey(),
|
|
123
|
+
workspace_id: text('workspace_id').notNull(),
|
|
124
|
+
execution_id: text('execution_id').notNull(),
|
|
125
|
+
block_id: text('block_id').notNull(),
|
|
126
|
+
gate_kind: text('gate_kind').notNull(),
|
|
127
|
+
helper_kind: text('helper_kind'),
|
|
128
|
+
outcome: text('outcome').notNull(),
|
|
129
|
+
attempts: integer('attempts').notNull().default(0),
|
|
130
|
+
max_attempts: integer('max_attempts').notNull().default(0),
|
|
131
|
+
helper_failures: integer('helper_failures').notNull().default(0),
|
|
132
|
+
duration_ms: bigint('duration_ms', { mode: 'number' }),
|
|
133
|
+
created_at: bigint('created_at', { mode: 'number' }).notNull(),
|
|
134
|
+
}, (t) => [
|
|
135
|
+
// The rollup's access path: this account's workspaces, settled since T.
|
|
136
|
+
index('idx_gate_outcomes_workspace_created').on(t.workspace_id, t.created_at),
|
|
137
|
+
// The retention prune's access path (a global range delete, not workspace-scoped).
|
|
138
|
+
index('idx_gate_outcomes_created').on(t.created_at),
|
|
139
|
+
]);
|
|
140
|
+
/**
|
|
141
|
+
* The daily rollup of `agent_runs` behind the dashboard's `30d` / `90d` windows. `failure_kind`
|
|
142
|
+
* carries '' (never NULL) for a non-failed status because it is part of the primary key, and a
|
|
143
|
+
* NULL there would not deduplicate a re-run bucket; the repository maps it back to null at the
|
|
144
|
+
* read boundary. Rewritten in place on each sweep, never appended: the current day's counts are
|
|
145
|
+
* not final, so they must be corrected rather than frozen.
|
|
146
|
+
*/
|
|
147
|
+
export const platformRunDays = pgTable('platform_run_days', {
|
|
148
|
+
workspace_id: text('workspace_id').notNull(),
|
|
149
|
+
day_start: bigint('day_start', { mode: 'number' }).notNull(),
|
|
150
|
+
status: text('status').notNull(),
|
|
151
|
+
failure_kind: text('failure_kind').notNull().default(''),
|
|
152
|
+
run_count: integer('run_count').notNull(),
|
|
153
|
+
}, (t) => [
|
|
154
|
+
primaryKey({ columns: [t.workspace_id, t.day_start, t.status, t.failure_kind] }),
|
|
155
|
+
// The prune's access path; the account-scoped read rides the primary key's leading columns.
|
|
156
|
+
index('idx_platform_run_days_day').on(t.day_start),
|
|
157
|
+
]);
|
|
158
|
+
/**
|
|
159
|
+
* How far the daily-rollup SWEEP has covered. A fact about the sweep, so it cannot be derived
|
|
160
|
+
* from the rolled-up rows: `MAX(day_start)` cannot tell an idle account from a wedged pass, nor
|
|
161
|
+
* a brand-new account from a rollup that has never run, and those call for opposite responses.
|
|
162
|
+
*
|
|
163
|
+
* DEPLOYMENT-scoped (one row per rollup, no tenant dimension) because one pass covers every
|
|
164
|
+
* workspace, and carrying only a day boundary and a timestamp it holds no tenant data. Written
|
|
165
|
+
* in the same transaction as the rewrite it describes. NOT workspace-scoped, hence absent from
|
|
166
|
+
* `WORKSPACE_SCOPED_TABLES` and from the retention prune.
|
|
167
|
+
*/
|
|
168
|
+
export const platformRollupState = pgTable('platform_rollup_state', {
|
|
169
|
+
rollup: text('rollup').primaryKey(),
|
|
170
|
+
through_day: bigint('through_day', { mode: 'number' }).notNull(),
|
|
171
|
+
updated_at: bigint('updated_at', { mode: 'number' }).notNull(),
|
|
172
|
+
});
|
|
173
|
+
//# sourceMappingURL=observability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observability.js","sourceRoot":"","sources":["../../../src/db/tables/observability.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentFailure, AgentRunRef, AgentRunRepository, Clock, DueSchedule, ExecutionInstance, ExecutionRepository, ExecutionStatus, LiveRunSummary, Pipeline, PipelineRepository, PipelineSchedule, PipelineScheduleRepository, PlatformDurationStats, PlatformFailureCount, PlatformLiveCounts, PlatformMetricsRepository, PlatformRunOutcome, PlatformRunTrendPoint, RunRef, ScheduleRun, StaleAgentRun } from '@cat-factory/kernel';
|
|
1
|
+
import type { AgentFailure, AgentRunRef, AgentRunRepository, Clock, DueSchedule, ExecutionInstance, ExecutionRepository, ExecutionStatus, GateOutcomeRecord, GateOutcomeRepository, LiveRunSummary, Pipeline, PipelineRepository, PipelineSchedule, PipelineScheduleRepository, PlatformDailyRunCount, PlatformDurationStats, PlatformFailedRunRef, PlatformFailureCount, PlatformGateOutcomeCount, PlatformLiveCounts, PlatformMetricsRepository, PlatformRunOutcome, PlatformRunTrendPoint, RunRef, ScheduleRun, StaleAgentRun } from '@cat-factory/kernel';
|
|
2
2
|
import type { DrizzleDb } from '../../db/client.js';
|
|
3
3
|
export declare class DrizzlePipelineRepository implements PipelineRepository {
|
|
4
4
|
private readonly db;
|
|
@@ -76,6 +76,23 @@ export declare class DrizzlePlatformMetricsRepository implements PlatformMetrics
|
|
|
76
76
|
failureKindBreakdown(accountId: string, sinceEpochMs: number): Promise<PlatformFailureCount[]>;
|
|
77
77
|
activeAndParkedCounts(accountId: string): Promise<PlatformLiveCounts>;
|
|
78
78
|
durationStatsSince(accountId: string, sinceEpochMs: number): Promise<PlatformDurationStats>;
|
|
79
|
+
rollupRunDays(fromEpochMs: number, toEpochMs: number): Promise<number>;
|
|
80
|
+
dailyRunTotalsSince(accountId: string, sinceEpochMs: number): Promise<PlatformDailyRunCount[]>;
|
|
81
|
+
dailyRollupWatermark(): Promise<number | null>;
|
|
82
|
+
deleteRunDaysOlderThan(cutoff: number): Promise<number>;
|
|
83
|
+
recentFailedRuns(accountId: string, sinceEpochMs: number, perWorkspaceLimit: number): Promise<PlatformFailedRunRef[]>;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The settled-gate projection on Postgres: the engine's write and the ONE aggregate behind the
|
|
87
|
+
* dashboard's gate / CI-fixer attempt statistics. Mirrors `D1GateOutcomeRepository`; the
|
|
88
|
+
* cross-runtime conformance suite asserts the two agree.
|
|
89
|
+
*/
|
|
90
|
+
export declare class DrizzleGateOutcomeRepository implements GateOutcomeRepository {
|
|
91
|
+
private readonly db;
|
|
92
|
+
constructor(db: DrizzleDb);
|
|
93
|
+
record(row: GateOutcomeRecord): Promise<void>;
|
|
94
|
+
statsSince(accountId: string, sinceEpochMs: number): Promise<PlatformGateOutcomeCount[]>;
|
|
95
|
+
deleteOlderThan(cutoff: number): Promise<number>;
|
|
79
96
|
}
|
|
80
97
|
export declare class DrizzlePipelineScheduleRepository implements PipelineScheduleRepository {
|
|
81
98
|
private readonly db;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execution.d.ts","sourceRoot":"","sources":["../../../src/repositories/drizzle/execution.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,KAAK,EACL,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,EACf,cAAc,EACd,QAAQ,EACR,kBAAkB,EAClB,gBAAgB,EAChB,0BAA0B,EAC1B,qBAAqB,EACrB,oBAAoB,EACpB,kBAAkB,EAClB,yBAAyB,EACzB,kBAAkB,EAClB,qBAAqB,EAErB,MAAM,EACN,WAAW,EAEX,aAAa,EACd,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"execution.d.ts","sourceRoot":"","sources":["../../../src/repositories/drizzle/execution.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
// is the SAME shared mapping the D1 repos use (@cat-factory/server), so behaviour
|
|
4
4
|
// matches across stores; this layer only owns the Drizzle queries. Assembled into the
|
|
5
5
|
// CoreRepositories set by ./drizzle.ts (the barrel).
|
|
6
|
-
import { LIVE_EXECUTION_STATUSES } from '@cat-factory/kernel';
|
|
6
|
+
import { LIVE_EXECUTION_STATUSES, RUN_DAYS_ROLLUP } from '@cat-factory/kernel';
|
|
7
|
+
import { DAY_MS } from '@cat-factory/orchestration';
|
|
7
8
|
import { agentRunKindSchema } from '@cat-factory/contracts';
|
|
8
9
|
import { adoptCreatedAt, decodeEnum, executionToDetail, parseIssueIntakeColumn, rowToExecution, rowToPipeline, serializeIssueIntakeColumn, tryDecodeRows, } from '@cat-factory/server';
|
|
9
|
-
import { and, desc, eq, gte, inArray, lt, notInArray, or, sql } from 'drizzle-orm';
|
|
10
|
-
import { agentRuns, blocks, pipelineScheduleRuns, pipelineSchedules, pipelines, workspaces, } from '../../db/schema.js';
|
|
10
|
+
import { and, desc, eq, gte, inArray, lt, lte, notInArray, or, sql } from 'drizzle-orm';
|
|
11
|
+
import { agentRuns, blocks, gateOutcomes, pipelineScheduleRuns, pipelineSchedules, pipelines, platformRollupState, platformRunDays, workspaces, } from '../../db/schema.js';
|
|
11
12
|
export class DrizzlePipelineRepository {
|
|
12
13
|
db;
|
|
13
14
|
constructor(db) {
|
|
@@ -573,6 +574,217 @@ export class DrizzlePlatformMetricsRepository {
|
|
|
573
574
|
p99Ms: at(row?.p99Ms),
|
|
574
575
|
};
|
|
575
576
|
}
|
|
577
|
+
async rollupRunDays(fromEpochMs, toEpochMs) {
|
|
578
|
+
// The aggregation happens in Postgres and no run row is loaded. Bounds snap to day edges so
|
|
579
|
+
// a partially-covered day is never written with only the covered part's counts, which would
|
|
580
|
+
// then read as a complete day.
|
|
581
|
+
const from = Math.floor(fromEpochMs / DAY_MS) * DAY_MS;
|
|
582
|
+
const to = Math.ceil(toEpochMs / DAY_MS) * DAY_MS;
|
|
583
|
+
if (to <= from)
|
|
584
|
+
return 0;
|
|
585
|
+
// DELETE the window, then INSERT it: an upsert alone is NOT a rewrite. A run's `status`
|
|
586
|
+
// MUTATES in place (`running` → `done`) while its `created_at` stays put, so a pass that ran
|
|
587
|
+
// while the day was in flight wrote a `(day, 'running')` bucket that a later pass's SELECT
|
|
588
|
+
// no longer produces — `DO UPDATE` never touches a row the new result set omits, and the
|
|
589
|
+
// orphan then sits in the table until retention, inflating every long-window total and trend
|
|
590
|
+
// by every run that happened to be mid-flight at a sweep. The delete is what makes "rewritten
|
|
591
|
+
// in place" true rather than "corrected where it still overlaps".
|
|
592
|
+
//
|
|
593
|
+
// Both statements ride ONE transaction so a concurrent dashboard read never observes the
|
|
594
|
+
// window mid-rewrite (it would see an empty stretch and render it as an idle period). The
|
|
595
|
+
// `ON CONFLICT` is kept as a belt on top of the braces: the `GROUP BY` makes the incoming
|
|
596
|
+
// keys unique, so it can only fire against a pass racing this one.
|
|
597
|
+
return await this.db.transaction(async (tx) => {
|
|
598
|
+
await tx
|
|
599
|
+
.delete(platformRunDays)
|
|
600
|
+
.where(and(gte(platformRunDays.day_start, from), lt(platformRunDays.day_start, to)));
|
|
601
|
+
// `failure_kind` is '' for a non-failed status because it is in the primary key (see
|
|
602
|
+
// migration 0079 for why a nullable key column would not deduplicate).
|
|
603
|
+
const res = await tx.execute(sql `
|
|
604
|
+
INSERT INTO platform_run_days (workspace_id, day_start, status, failure_kind, run_count)
|
|
605
|
+
SELECT ${agentRuns.workspace_id},
|
|
606
|
+
(${agentRuns.created_at} / ${DAY_MS}::bigint) * ${DAY_MS}::bigint AS day_start,
|
|
607
|
+
${agentRuns.status},
|
|
608
|
+
CASE WHEN ${agentRuns.status} = 'failed'
|
|
609
|
+
THEN coalesce((${agentRuns.failure}::jsonb ->> 'kind'), 'unknown')
|
|
610
|
+
ELSE '' END AS failure_kind,
|
|
611
|
+
count(*)::int AS run_count
|
|
612
|
+
FROM ${agentRuns}
|
|
613
|
+
WHERE ${agentRuns.created_at} >= ${from} AND ${agentRuns.created_at} < ${to}
|
|
614
|
+
GROUP BY 1, 2, 3, 4
|
|
615
|
+
ON CONFLICT (workspace_id, day_start, status, failure_kind)
|
|
616
|
+
DO UPDATE SET run_count = excluded.run_count
|
|
617
|
+
`);
|
|
618
|
+
// The sweep's coverage, recorded INSIDE the same transaction as the rewrite it describes,
|
|
619
|
+
// so the watermark can never claim a day the rows do not have. `through_day` is the last
|
|
620
|
+
// day the pass actually covered (`to` is the exclusive end) and only ever moves FORWARD:
|
|
621
|
+
// a pass recomputing an OLD window (a backfill, a replay) must not walk it backwards and
|
|
622
|
+
// make a current rollup read as behind. `updated_at` is the pass's own upper bound, which
|
|
623
|
+
// is the caller's `now` before it was snapped to a day edge.
|
|
624
|
+
await tx
|
|
625
|
+
.insert(platformRollupState)
|
|
626
|
+
.values({ rollup: RUN_DAYS_ROLLUP, through_day: to - DAY_MS, updated_at: toEpochMs })
|
|
627
|
+
.onConflictDoUpdate({
|
|
628
|
+
target: platformRollupState.rollup,
|
|
629
|
+
set: {
|
|
630
|
+
through_day: sql `greatest(${platformRollupState.through_day}, ${to - DAY_MS})`,
|
|
631
|
+
updated_at: sql `greatest(${platformRollupState.updated_at}, ${toEpochMs})`,
|
|
632
|
+
},
|
|
633
|
+
});
|
|
634
|
+
return Number(res.rowCount ?? 0);
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
async dailyRunTotalsSince(accountId, sinceEpochMs) {
|
|
638
|
+
const rows = await this.db
|
|
639
|
+
.select({
|
|
640
|
+
dayStart: platformRunDays.day_start,
|
|
641
|
+
status: platformRunDays.status,
|
|
642
|
+
failureKind: platformRunDays.failure_kind,
|
|
643
|
+
count: sql `sum(${platformRunDays.run_count})::int`,
|
|
644
|
+
})
|
|
645
|
+
.from(platformRunDays)
|
|
646
|
+
.where(and(inArray(platformRunDays.workspace_id, this.db
|
|
647
|
+
.select({ id: workspaces.id })
|
|
648
|
+
.from(workspaces)
|
|
649
|
+
.where(eq(workspaces.account_id, accountId))),
|
|
650
|
+
// Snap to the day the window starts IN, so a window beginning mid-day still includes
|
|
651
|
+
// that day's bucket rather than dropping the oldest day entirely.
|
|
652
|
+
gte(platformRunDays.day_start, Math.floor(sinceEpochMs / DAY_MS) * DAY_MS)))
|
|
653
|
+
.groupBy(platformRunDays.day_start, platformRunDays.status, platformRunDays.failure_kind)
|
|
654
|
+
.orderBy(platformRunDays.day_start);
|
|
655
|
+
return rows.map((r) => ({
|
|
656
|
+
dayStart: Number(r.dayStart),
|
|
657
|
+
status: r.status,
|
|
658
|
+
failureKind: r.failureKind === '' ? null : r.failureKind,
|
|
659
|
+
count: Number(r.count),
|
|
660
|
+
}));
|
|
661
|
+
}
|
|
662
|
+
async dailyRollupWatermark() {
|
|
663
|
+
const [row] = await this.db
|
|
664
|
+
.select({ throughDay: platformRollupState.through_day })
|
|
665
|
+
.from(platformRollupState)
|
|
666
|
+
.where(eq(platformRollupState.rollup, RUN_DAYS_ROLLUP));
|
|
667
|
+
return row?.throughDay != null ? Number(row.throughDay) : null;
|
|
668
|
+
}
|
|
669
|
+
async deleteRunDaysOlderThan(cutoff) {
|
|
670
|
+
const rows = await this.db
|
|
671
|
+
.delete(platformRunDays)
|
|
672
|
+
.where(lt(platformRunDays.day_start, cutoff))
|
|
673
|
+
.returning({ dayStart: platformRunDays.day_start });
|
|
674
|
+
return rows.length;
|
|
675
|
+
}
|
|
676
|
+
async recentFailedRuns(accountId, sinceEpochMs, perWorkspaceLimit) {
|
|
677
|
+
if (perWorkspaceLimit <= 0)
|
|
678
|
+
return [];
|
|
679
|
+
// Capped PER WORKSPACE by a window function rather than one global LIMIT: a card belongs to
|
|
680
|
+
// a single workspace, and a noisy neighbour must not starve another's card of its evidence.
|
|
681
|
+
// The partition COUNT rides along so the cap can state what it left out without a second
|
|
682
|
+
// query that could disagree with the sample it accompanies.
|
|
683
|
+
const ranked = this.db
|
|
684
|
+
.select({
|
|
685
|
+
workspaceId: agentRuns.workspace_id,
|
|
686
|
+
executionId: agentRuns.id,
|
|
687
|
+
blockId: agentRuns.block_id,
|
|
688
|
+
createdAt: agentRuns.created_at,
|
|
689
|
+
failureKind: sql `coalesce((${agentRuns.failure}::jsonb ->> 'kind'), 'unknown')`.as('failure_kind'),
|
|
690
|
+
rn: sql `row_number() over (partition by ${agentRuns.workspace_id}
|
|
691
|
+
order by ${agentRuns.created_at} desc, ${agentRuns.id} desc)`.as('rn'),
|
|
692
|
+
workspaceFailedTotal: sql `(count(*) over (partition by ${agentRuns.workspace_id}))::int`.as('workspace_failed_total'),
|
|
693
|
+
})
|
|
694
|
+
.from(agentRuns)
|
|
695
|
+
.where(and(this.accountScope(accountId), gte(agentRuns.created_at, sinceEpochMs), eq(agentRuns.status, 'failed'), eq(agentRuns.kind, 'execution')))
|
|
696
|
+
.as('ranked');
|
|
697
|
+
const rows = await this.db
|
|
698
|
+
.select({
|
|
699
|
+
workspaceId: ranked.workspaceId,
|
|
700
|
+
executionId: ranked.executionId,
|
|
701
|
+
blockId: ranked.blockId,
|
|
702
|
+
failureKind: ranked.failureKind,
|
|
703
|
+
createdAt: ranked.createdAt,
|
|
704
|
+
workspaceFailedTotal: ranked.workspaceFailedTotal,
|
|
705
|
+
})
|
|
706
|
+
.from(ranked)
|
|
707
|
+
.where(lte(ranked.rn, perWorkspaceLimit))
|
|
708
|
+
.orderBy(ranked.workspaceId, desc(ranked.createdAt));
|
|
709
|
+
return rows.map((r) => ({
|
|
710
|
+
workspaceId: r.workspaceId,
|
|
711
|
+
executionId: r.executionId,
|
|
712
|
+
blockId: r.blockId ?? null,
|
|
713
|
+
failureKind: r.failureKind,
|
|
714
|
+
createdAt: Number(r.createdAt),
|
|
715
|
+
workspaceFailedTotal: Number(r.workspaceFailedTotal),
|
|
716
|
+
}));
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* The settled-gate projection on Postgres: the engine's write and the ONE aggregate behind the
|
|
721
|
+
* dashboard's gate / CI-fixer attempt statistics. Mirrors `D1GateOutcomeRepository`; the
|
|
722
|
+
* cross-runtime conformance suite asserts the two agree.
|
|
723
|
+
*/
|
|
724
|
+
export class DrizzleGateOutcomeRepository {
|
|
725
|
+
db;
|
|
726
|
+
constructor(db) {
|
|
727
|
+
this.db = db;
|
|
728
|
+
}
|
|
729
|
+
async record(row) {
|
|
730
|
+
// FIRST WRITE WINS on the derived id: the durable drivers replay, and re-inserting a settle
|
|
731
|
+
// would inflate every count this table exists to report.
|
|
732
|
+
await this.db
|
|
733
|
+
.insert(gateOutcomes)
|
|
734
|
+
.values({
|
|
735
|
+
id: row.id,
|
|
736
|
+
workspace_id: row.workspaceId,
|
|
737
|
+
execution_id: row.executionId,
|
|
738
|
+
block_id: row.blockId,
|
|
739
|
+
gate_kind: row.gateKind,
|
|
740
|
+
helper_kind: row.helperKind,
|
|
741
|
+
outcome: row.outcome,
|
|
742
|
+
attempts: row.attempts,
|
|
743
|
+
max_attempts: row.maxAttempts,
|
|
744
|
+
helper_failures: row.helperFailures,
|
|
745
|
+
duration_ms: row.durationMs,
|
|
746
|
+
created_at: row.createdAt,
|
|
747
|
+
})
|
|
748
|
+
.onConflictDoNothing({ target: gateOutcomes.id });
|
|
749
|
+
}
|
|
750
|
+
async statsSince(accountId, sinceEpochMs) {
|
|
751
|
+
const gates = sql `count(*)::int`;
|
|
752
|
+
const rows = await this.db
|
|
753
|
+
.select({
|
|
754
|
+
gateKind: gateOutcomes.gate_kind,
|
|
755
|
+
helperKind: gateOutcomes.helper_kind,
|
|
756
|
+
outcome: gateOutcomes.outcome,
|
|
757
|
+
gates,
|
|
758
|
+
attempts: sql `coalesce(sum(${gateOutcomes.attempts}), 0)::int`,
|
|
759
|
+
helperFailures: sql `coalesce(sum(${gateOutcomes.helper_failures}), 0)::int`,
|
|
760
|
+
cleanGates: sql `count(*) filter (where ${gateOutcomes.attempts} = 0)::int`,
|
|
761
|
+
})
|
|
762
|
+
.from(gateOutcomes)
|
|
763
|
+
.where(and(inArray(gateOutcomes.workspace_id, this.db
|
|
764
|
+
.select({ id: workspaces.id })
|
|
765
|
+
.from(workspaces)
|
|
766
|
+
.where(eq(workspaces.account_id, accountId))), gte(gateOutcomes.created_at, sinceEpochMs)))
|
|
767
|
+
.groupBy(gateOutcomes.gate_kind, gateOutcomes.helper_kind, gateOutcomes.outcome)
|
|
768
|
+
.orderBy(desc(sql `count(*)`));
|
|
769
|
+
return rows.map((r) => ({
|
|
770
|
+
gateKind: r.gateKind,
|
|
771
|
+
helperKind: r.helperKind ?? null,
|
|
772
|
+
// Guard the stored string into the port's union; anything unrecognised reads as the
|
|
773
|
+
// non-passing outcome rather than being dropped from the statistic entirely.
|
|
774
|
+
outcome: r.outcome === 'passed' ? 'passed' : 'exhausted',
|
|
775
|
+
gates: Number(r.gates),
|
|
776
|
+
attempts: Number(r.attempts),
|
|
777
|
+
helperFailures: Number(r.helperFailures),
|
|
778
|
+
cleanGates: Number(r.cleanGates),
|
|
779
|
+
}));
|
|
780
|
+
}
|
|
781
|
+
async deleteOlderThan(cutoff) {
|
|
782
|
+
const rows = await this.db
|
|
783
|
+
.delete(gateOutcomes)
|
|
784
|
+
.where(lt(gateOutcomes.created_at, cutoff))
|
|
785
|
+
.returning({ id: gateOutcomes.id });
|
|
786
|
+
return rows.length;
|
|
787
|
+
}
|
|
576
788
|
}
|
|
577
789
|
function rowToSchedule(row) {
|
|
578
790
|
const recurrence = {
|