@sixb/sqlite 0.1.2 → 0.1.4
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/agents/agent-storage.d.ts +5 -3
- package/dist/agents/agent-storage.d.ts.map +1 -1
- package/dist/agents/checkpoints.d.ts +18 -0
- package/dist/agents/checkpoints.d.ts.map +1 -0
- package/dist/agents/index.d.ts +1 -0
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/messages.d.ts +2 -1
- package/dist/agents/messages.d.ts.map +1 -1
- package/dist/agents/rows.d.ts +18 -1
- package/dist/agents/rows.d.ts.map +1 -1
- package/dist/ai-cost-storage.d.ts +15 -0
- package/dist/ai-cost-storage.d.ts.map +1 -0
- package/dist/ai-usage-storage.d.ts +2 -1
- package/dist/ai-usage-storage.d.ts.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +979 -74
- package/dist/index.js.map +15 -13
- package/dist/migrations.d.ts.map +1 -1
- package/dist/object-storage.d.ts +4 -3
- package/dist/object-storage.d.ts.map +1 -1
- package/dist/ontology-storage/materialization-state.d.ts.map +1 -1
- package/dist/ontology-storage/materialization-writer.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/agents/agent-storage.ts +6 -3
- package/src/agents/checkpoints.ts +205 -0
- package/src/agents/index.ts +1 -0
- package/src/agents/messages.ts +59 -0
- package/src/agents/rows.ts +44 -0
- package/src/ai-cost-storage.ts +744 -0
- package/src/ai-usage-storage.ts +19 -0
- package/src/index.ts +8 -2
- package/src/migrations/026-ai-cost-accounting.sql +57 -0
- package/src/migrations/027-agent-context-checkpoints.sql +28 -0
- package/src/migrations/028-object-override-edit-times.sql +3 -0
- package/src/migrations.ts +10 -0
- package/src/object-query-compiler.ts +50 -0
- package/src/object-storage.ts +138 -34
- package/src/ontology-storage/materialization-state.ts +2 -0
- package/src/ontology-storage/materialization-writer.ts +6 -3
package/dist/index.js
CHANGED
|
@@ -2918,6 +2918,103 @@ CREATE INDEX idx_connector_connections_status
|
|
|
2918
2918
|
ON connector_connections (project_id, connector_id, status, id);
|
|
2919
2919
|
`;
|
|
2920
2920
|
|
|
2921
|
+
// src/migrations/026-ai-cost-accounting.sql
|
|
2922
|
+
var _026_ai_cost_accounting_default = `CREATE TABLE ai_model_call_valuations (
|
|
2923
|
+
project_id TEXT NOT NULL CHECK (length(trim(project_id)) > 0),
|
|
2924
|
+
usage_record_id TEXT NOT NULL CHECK (length(trim(usage_record_id)) > 0),
|
|
2925
|
+
status TEXT NOT NULL CHECK (status IN ('rated', 'unpriceable')),
|
|
2926
|
+
provider_id TEXT CHECK (provider_id IS NULL OR length(trim(provider_id)) > 0),
|
|
2927
|
+
model_id TEXT CHECK (model_id IS NULL OR length(trim(model_id)) > 0),
|
|
2928
|
+
currency TEXT CHECK (
|
|
2929
|
+
currency IS NULL OR (length(currency) = 3 AND currency GLOB '[A-Z][A-Z][A-Z]')
|
|
2930
|
+
),
|
|
2931
|
+
amount_nanos INTEGER CHECK (
|
|
2932
|
+
amount_nanos IS NULL OR (typeof(amount_nanos) = 'integer' AND amount_nanos >= 0)
|
|
2933
|
+
),
|
|
2934
|
+
reason TEXT CHECK (
|
|
2935
|
+
reason IS NULL OR reason IN (
|
|
2936
|
+
'missingBillingIdentity',
|
|
2937
|
+
'missingCatalogEntry',
|
|
2938
|
+
'missingUsageMeter',
|
|
2939
|
+
'unsupportedPricingDimension',
|
|
2940
|
+
'invalidUsageForFormula'
|
|
2941
|
+
)
|
|
2942
|
+
),
|
|
2943
|
+
details TEXT NOT NULL CHECK (json_valid(details) AND json_type(details) = 'object'),
|
|
2944
|
+
rated_at TEXT NOT NULL CHECK (length(trim(rated_at)) > 0),
|
|
2945
|
+
PRIMARY KEY (project_id, usage_record_id),
|
|
2946
|
+
FOREIGN KEY (project_id, usage_record_id)
|
|
2947
|
+
REFERENCES ai_model_call_usage(project_id, id)
|
|
2948
|
+
ON DELETE RESTRICT,
|
|
2949
|
+
CHECK (
|
|
2950
|
+
(
|
|
2951
|
+
status = 'rated'
|
|
2952
|
+
AND provider_id IS NOT NULL
|
|
2953
|
+
AND model_id IS NOT NULL
|
|
2954
|
+
AND currency IS NOT NULL
|
|
2955
|
+
AND amount_nanos IS NOT NULL
|
|
2956
|
+
AND reason IS NULL
|
|
2957
|
+
)
|
|
2958
|
+
OR
|
|
2959
|
+
(
|
|
2960
|
+
status = 'unpriceable'
|
|
2961
|
+
AND currency IS NULL
|
|
2962
|
+
AND amount_nanos IS NULL
|
|
2963
|
+
AND reason IS NOT NULL
|
|
2964
|
+
AND (
|
|
2965
|
+
(reason = 'missingBillingIdentity' AND provider_id IS NULL AND model_id IS NULL)
|
|
2966
|
+
OR
|
|
2967
|
+
(reason <> 'missingBillingIdentity' AND provider_id IS NOT NULL AND model_id IS NOT NULL)
|
|
2968
|
+
)
|
|
2969
|
+
)
|
|
2970
|
+
)
|
|
2971
|
+
);
|
|
2972
|
+
|
|
2973
|
+
CREATE INDEX idx_ai_model_call_usage_provider_model_time
|
|
2974
|
+
ON ai_model_call_usage (project_id, provider_id, requested_model_id, occurred_at, id);
|
|
2975
|
+
CREATE INDEX idx_ai_model_call_usage_model_time
|
|
2976
|
+
ON ai_model_call_usage (project_id, requested_model_id, occurred_at, id);
|
|
2977
|
+
CREATE INDEX idx_ai_model_call_valuations_summary
|
|
2978
|
+
ON ai_model_call_valuations (project_id, status, currency, usage_record_id);
|
|
2979
|
+
`;
|
|
2980
|
+
|
|
2981
|
+
// src/migrations/027-agent-context-checkpoints.sql
|
|
2982
|
+
var _027_agent_context_checkpoints_default = `CREATE TABLE agent_context_checkpoints (
|
|
2983
|
+
project_id TEXT NOT NULL,
|
|
2984
|
+
id TEXT NOT NULL,
|
|
2985
|
+
thread_id TEXT NOT NULL,
|
|
2986
|
+
created_by_run_id TEXT NOT NULL,
|
|
2987
|
+
previous_checkpoint_id TEXT,
|
|
2988
|
+
reason TEXT NOT NULL CHECK (reason IN ('threshold', 'overflow')),
|
|
2989
|
+
summary TEXT NOT NULL CHECK (length(trim(summary)) > 0),
|
|
2990
|
+
summary_format_version INTEGER NOT NULL CHECK (summary_format_version = 1),
|
|
2991
|
+
summarized_through_seq INTEGER NOT NULL CHECK (summarized_through_seq >= 1),
|
|
2992
|
+
observed_head_seq INTEGER NOT NULL,
|
|
2993
|
+
estimated_input_tokens_before INTEGER NOT NULL CHECK (estimated_input_tokens_before >= 0),
|
|
2994
|
+
estimated_input_tokens_after INTEGER NOT NULL CHECK (estimated_input_tokens_after >= 0),
|
|
2995
|
+
summary_model_id TEXT NOT NULL CHECK (length(trim(summary_model_id)) > 0),
|
|
2996
|
+
created_at TEXT NOT NULL,
|
|
2997
|
+
PRIMARY KEY (project_id, id),
|
|
2998
|
+
UNIQUE (project_id, created_by_run_id),
|
|
2999
|
+
FOREIGN KEY (project_id, thread_id)
|
|
3000
|
+
REFERENCES agent_threads(project_id, id) ON DELETE CASCADE,
|
|
3001
|
+
FOREIGN KEY (project_id, created_by_run_id)
|
|
3002
|
+
REFERENCES agent_runs(project_id, id) ON DELETE RESTRICT,
|
|
3003
|
+
FOREIGN KEY (project_id, previous_checkpoint_id)
|
|
3004
|
+
REFERENCES agent_context_checkpoints(project_id, id) ON DELETE RESTRICT,
|
|
3005
|
+
CHECK (summarized_through_seq < observed_head_seq)
|
|
3006
|
+
);
|
|
3007
|
+
|
|
3008
|
+
CREATE INDEX idx_agent_context_checkpoints_thread_latest
|
|
3009
|
+
ON agent_context_checkpoints(project_id, thread_id, summarized_through_seq DESC, created_at DESC, id DESC);
|
|
3010
|
+
`;
|
|
3011
|
+
|
|
3012
|
+
// src/migrations/028-object-override-edit-times.sql
|
|
3013
|
+
var _028_object_override_edit_times_default = `ALTER TABLE ontology_object_overrides
|
|
3014
|
+
ADD COLUMN edited_at TEXT NOT NULL DEFAULT '{}'
|
|
3015
|
+
CHECK (json_valid(edited_at) AND json_type(edited_at) = 'object');
|
|
3016
|
+
`;
|
|
3017
|
+
|
|
2921
3018
|
// src/migrations.ts
|
|
2922
3019
|
var MIGRATIONS_TABLE_SQL = `
|
|
2923
3020
|
CREATE TABLE IF NOT EXISTS sixb_migrations (
|
|
@@ -2977,7 +3074,10 @@ var sqliteStorageMigrations = defineMigrations({
|
|
|
2977
3074
|
}
|
|
2978
3075
|
db.run(_024_ontology_commit_executions_default);
|
|
2979
3076
|
}, { checksum: checksum(_024_ontology_commit_executions_default) }),
|
|
2980
|
-
sqliteSql("025-connector-connections", _025_connector_connections_default)
|
|
3077
|
+
sqliteSql("025-connector-connections", _025_connector_connections_default),
|
|
3078
|
+
sqliteSql("026-ai-cost-accounting", _026_ai_cost_accounting_default),
|
|
3079
|
+
sqliteSql("027-agent-context-checkpoints", _027_agent_context_checkpoints_default),
|
|
3080
|
+
sqliteSql("028-object-override-edit-times", _028_object_override_edit_times_default)
|
|
2981
3081
|
]
|
|
2982
3082
|
});
|
|
2983
3083
|
function sqliteStoragePath(basePath) {
|
|
@@ -3628,17 +3728,18 @@ function isUniqueConstraintError(error) {
|
|
|
3628
3728
|
return error instanceof Error && error.message.includes("UNIQUE constraint failed");
|
|
3629
3729
|
}
|
|
3630
3730
|
|
|
3631
|
-
// src/agents/
|
|
3632
|
-
import {
|
|
3731
|
+
// src/agents/checkpoints.ts
|
|
3732
|
+
import {
|
|
3733
|
+
agentContextCheckpointMatchesCreateInput,
|
|
3734
|
+
assertAgentContextCheckpointAnchors,
|
|
3735
|
+
assertAgentContextCheckpointAuthority,
|
|
3736
|
+
assertAgentContextCheckpointReplayState,
|
|
3737
|
+
assertCreateAgentContextCheckpointInput
|
|
3738
|
+
} from "@sixb/core/internal/agent-run-storage-provider";
|
|
3633
3739
|
import {
|
|
3634
3740
|
AgentStorageError
|
|
3635
3741
|
} from "@sixb/core/storage";
|
|
3636
3742
|
|
|
3637
|
-
// src/storage-errors.ts
|
|
3638
|
-
function isUniqueConstraintError2(error) {
|
|
3639
|
-
return error instanceof Error && error.message.includes("UNIQUE constraint failed");
|
|
3640
|
-
}
|
|
3641
|
-
|
|
3642
3743
|
// src/agents/rows.ts
|
|
3643
3744
|
import { parseSixbFailure } from "@sixb/core/internal/errors";
|
|
3644
3745
|
import {
|
|
@@ -3700,6 +3801,27 @@ function rowToMessageRecord(row) {
|
|
|
3700
3801
|
completedAt: row.completed_at ? new Date(row.completed_at) : undefined
|
|
3701
3802
|
};
|
|
3702
3803
|
}
|
|
3804
|
+
function rowToContextCheckpointRecord(row) {
|
|
3805
|
+
if (row.summary_format_version !== 1) {
|
|
3806
|
+
throw new Error(`[SixbSqlite] Unsupported agent context checkpoint summary format version '${row.summary_format_version}'.`);
|
|
3807
|
+
}
|
|
3808
|
+
return {
|
|
3809
|
+
id: row.id,
|
|
3810
|
+
projectId: row.project_id,
|
|
3811
|
+
threadId: row.thread_id,
|
|
3812
|
+
createdByRunId: row.created_by_run_id,
|
|
3813
|
+
previousCheckpointId: row.previous_checkpoint_id ?? undefined,
|
|
3814
|
+
reason: row.reason,
|
|
3815
|
+
summary: row.summary,
|
|
3816
|
+
summaryFormatVersion: 1,
|
|
3817
|
+
summarizedThroughSeq: row.summarized_through_seq,
|
|
3818
|
+
observedHeadSeq: row.observed_head_seq,
|
|
3819
|
+
estimatedInputTokensBefore: row.estimated_input_tokens_before,
|
|
3820
|
+
estimatedInputTokensAfter: row.estimated_input_tokens_after,
|
|
3821
|
+
summaryModelId: row.summary_model_id,
|
|
3822
|
+
createdAt: new Date(row.created_at)
|
|
3823
|
+
};
|
|
3824
|
+
}
|
|
3703
3825
|
function principalFromColumns(type, id) {
|
|
3704
3826
|
return type && id ? { type, id } : undefined;
|
|
3705
3827
|
}
|
|
@@ -3721,6 +3843,124 @@ function queryAgentList(input) {
|
|
|
3721
3843
|
return { rows, total, hasMore: offset + rows.length < total };
|
|
3722
3844
|
}
|
|
3723
3845
|
|
|
3846
|
+
// src/agents/checkpoints.ts
|
|
3847
|
+
var SQLITE_CHECKPOINT_RUN_ID_BATCH_SIZE = 500;
|
|
3848
|
+
|
|
3849
|
+
class SqliteAgentContextCheckpointStore {
|
|
3850
|
+
db;
|
|
3851
|
+
constructor(db) {
|
|
3852
|
+
this.db = db;
|
|
3853
|
+
}
|
|
3854
|
+
async create(input) {
|
|
3855
|
+
assertCreateAgentContextCheckpointInput(input, "SixbSqlite");
|
|
3856
|
+
return this.db.transaction(() => {
|
|
3857
|
+
const runRow = this.db.query("SELECT * FROM agent_runs WHERE project_id = ? AND id = ?").get(input.projectId, input.createdByRunId);
|
|
3858
|
+
const threadRow = this.db.query("SELECT * FROM agent_threads WHERE project_id = ? AND id = ?").get(input.projectId, input.threadId);
|
|
3859
|
+
assertAgentContextCheckpointAuthority({
|
|
3860
|
+
create: input,
|
|
3861
|
+
run: runRow ? rowToRunRecord(runRow) : null,
|
|
3862
|
+
thread: threadRow ? rowToThreadRecord(threadRow) : null,
|
|
3863
|
+
prefix: "SixbSqlite"
|
|
3864
|
+
});
|
|
3865
|
+
const latest = this.findLatest(input.projectId, input.threadId);
|
|
3866
|
+
const headRow = this.db.query("SELECT COALESCE(MAX(seq), 0) AS seq FROM agent_messages WHERE project_id = ? AND thread_id = ?").get(input.projectId, input.threadId);
|
|
3867
|
+
const existingForRun = this.db.query("SELECT * FROM agent_context_checkpoints WHERE project_id = ? AND created_by_run_id = ?").get(input.projectId, input.createdByRunId);
|
|
3868
|
+
if (existingForRun) {
|
|
3869
|
+
const record = rowToContextCheckpointRecord(existingForRun);
|
|
3870
|
+
if (agentContextCheckpointMatchesCreateInput(record, input)) {
|
|
3871
|
+
assertAgentContextCheckpointReplayState({
|
|
3872
|
+
create: input,
|
|
3873
|
+
existing: record,
|
|
3874
|
+
latest,
|
|
3875
|
+
headSeq: headRow.seq,
|
|
3876
|
+
prefix: "SixbSqlite"
|
|
3877
|
+
});
|
|
3878
|
+
return record;
|
|
3879
|
+
}
|
|
3880
|
+
throw new AgentStorageError("invalid_state", `[SixbSqlite] Agent run '${input.createdByRunId}' already created a different context checkpoint.`);
|
|
3881
|
+
}
|
|
3882
|
+
const duplicateId = this.db.query("SELECT 1 FROM agent_context_checkpoints WHERE project_id = ? AND id = ?").get(input.projectId, input.id);
|
|
3883
|
+
if (duplicateId) {
|
|
3884
|
+
throw new AgentStorageError("duplicate_id", `[SixbSqlite] Agent context checkpoint '${input.id}' already exists for project '${input.projectId}'.`);
|
|
3885
|
+
}
|
|
3886
|
+
const firstRetainedRow = this.db.query("SELECT * FROM agent_messages WHERE project_id = ? AND thread_id = ? AND seq > ? ORDER BY seq ASC LIMIT 1").get(input.projectId, input.threadId, input.summarizedThroughSeq);
|
|
3887
|
+
assertAgentContextCheckpointAnchors({
|
|
3888
|
+
create: input,
|
|
3889
|
+
latest,
|
|
3890
|
+
headSeq: headRow.seq,
|
|
3891
|
+
firstRetained: firstRetainedRow ? rowToMessageRecord(firstRetainedRow) : null,
|
|
3892
|
+
prefix: "SixbSqlite"
|
|
3893
|
+
});
|
|
3894
|
+
const createdAt = input.createdAt ?? new Date;
|
|
3895
|
+
this.db.query(`
|
|
3896
|
+
INSERT INTO agent_context_checkpoints (
|
|
3897
|
+
project_id,
|
|
3898
|
+
id,
|
|
3899
|
+
thread_id,
|
|
3900
|
+
created_by_run_id,
|
|
3901
|
+
previous_checkpoint_id,
|
|
3902
|
+
reason,
|
|
3903
|
+
summary,
|
|
3904
|
+
summary_format_version,
|
|
3905
|
+
summarized_through_seq,
|
|
3906
|
+
observed_head_seq,
|
|
3907
|
+
estimated_input_tokens_before,
|
|
3908
|
+
estimated_input_tokens_after,
|
|
3909
|
+
summary_model_id,
|
|
3910
|
+
created_at
|
|
3911
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
3912
|
+
`).run(input.projectId, input.id, input.threadId, input.createdByRunId, input.expectedPreviousCheckpointId, input.reason, input.summary, input.summaryFormatVersion, input.summarizedThroughSeq, input.observedHeadSeq, input.estimatedInputTokensBefore, input.estimatedInputTokensAfter, input.summaryModelId, createdAt.toISOString());
|
|
3913
|
+
return this.requireById(input.projectId, input.id);
|
|
3914
|
+
})();
|
|
3915
|
+
}
|
|
3916
|
+
async getLatest(input) {
|
|
3917
|
+
return this.findLatest(input.projectId, input.threadId);
|
|
3918
|
+
}
|
|
3919
|
+
async getByRunIds(input) {
|
|
3920
|
+
const runIds = [...new Set(input.runIds)];
|
|
3921
|
+
if (runIds.length === 0)
|
|
3922
|
+
return [];
|
|
3923
|
+
const rows = [];
|
|
3924
|
+
for (let offset = 0;offset < runIds.length; offset += SQLITE_CHECKPOINT_RUN_ID_BATCH_SIZE) {
|
|
3925
|
+
const batch = runIds.slice(offset, offset + SQLITE_CHECKPOINT_RUN_ID_BATCH_SIZE);
|
|
3926
|
+
const placeholders = batch.map(() => "?").join(", ");
|
|
3927
|
+
rows.push(...this.db.query(`SELECT * FROM agent_context_checkpoints WHERE project_id = ? AND created_by_run_id IN (${placeholders})`).all(input.projectId, ...batch));
|
|
3928
|
+
}
|
|
3929
|
+
const byRunId = new Map(rows.map((row) => [row.created_by_run_id, rowToContextCheckpointRecord(row)]));
|
|
3930
|
+
return runIds.flatMap((runId) => {
|
|
3931
|
+
const checkpoint = byRunId.get(runId);
|
|
3932
|
+
return checkpoint ? [checkpoint] : [];
|
|
3933
|
+
});
|
|
3934
|
+
}
|
|
3935
|
+
findLatest(projectId, threadId) {
|
|
3936
|
+
const row = this.db.query(`
|
|
3937
|
+
SELECT * FROM agent_context_checkpoints
|
|
3938
|
+
WHERE project_id = ? AND thread_id = ?
|
|
3939
|
+
ORDER BY summarized_through_seq DESC, created_at DESC, id DESC
|
|
3940
|
+
LIMIT 1
|
|
3941
|
+
`).get(projectId, threadId);
|
|
3942
|
+
return row ? rowToContextCheckpointRecord(row) : null;
|
|
3943
|
+
}
|
|
3944
|
+
requireById(projectId, id) {
|
|
3945
|
+
const row = this.db.query("SELECT * FROM agent_context_checkpoints WHERE project_id = ? AND id = ?").get(projectId, id);
|
|
3946
|
+
if (!row) {
|
|
3947
|
+
throw new AgentStorageError("invalid_state", `[SixbSqlite] Failed to load agent context checkpoint '${id}' for project '${projectId}'.`);
|
|
3948
|
+
}
|
|
3949
|
+
return rowToContextCheckpointRecord(row);
|
|
3950
|
+
}
|
|
3951
|
+
}
|
|
3952
|
+
|
|
3953
|
+
// src/agents/messages.ts
|
|
3954
|
+
import { AGENT_MESSAGE_CONTENT_VERSION } from "@sixb/core/internal/agents";
|
|
3955
|
+
import {
|
|
3956
|
+
AgentStorageError as AgentStorageError2
|
|
3957
|
+
} from "@sixb/core/storage";
|
|
3958
|
+
|
|
3959
|
+
// src/storage-errors.ts
|
|
3960
|
+
function isUniqueConstraintError2(error) {
|
|
3961
|
+
return error instanceof Error && error.message.includes("UNIQUE constraint failed");
|
|
3962
|
+
}
|
|
3963
|
+
|
|
3724
3964
|
// src/agents/messages.ts
|
|
3725
3965
|
class SqliteAgentMessageStore {
|
|
3726
3966
|
db;
|
|
@@ -3732,12 +3972,12 @@ class SqliteAgentMessageStore {
|
|
|
3732
3972
|
return this.db.transaction(() => {
|
|
3733
3973
|
const thread = this.db.query("SELECT 1 AS present FROM agent_threads WHERE project_id = ? AND id = ?").get(input.projectId, input.threadId);
|
|
3734
3974
|
if (!thread) {
|
|
3735
|
-
throw new
|
|
3975
|
+
throw new AgentStorageError2("thread_not_found", `[SixbSqlite] Agent thread '${input.threadId}' not found for project '${input.projectId}'.`);
|
|
3736
3976
|
}
|
|
3737
3977
|
if (input.runId !== null) {
|
|
3738
3978
|
const run = this.db.query("SELECT 1 AS present FROM agent_runs WHERE project_id = ? AND id = ?").get(input.projectId, input.runId);
|
|
3739
3979
|
if (!run) {
|
|
3740
|
-
throw new
|
|
3980
|
+
throw new AgentStorageError2("run_not_found", `[SixbSqlite] Agent run '${input.runId}' not found for project '${input.projectId}'.`);
|
|
3741
3981
|
}
|
|
3742
3982
|
}
|
|
3743
3983
|
const seqRow = this.db.query("SELECT COALESCE(MAX(seq), 0) + 1 AS next FROM agent_messages WHERE project_id = ? AND thread_id = ?").get(input.projectId, input.threadId);
|
|
@@ -3761,7 +4001,7 @@ class SqliteAgentMessageStore {
|
|
|
3761
4001
|
`).run(input.projectId, input.id, input.threadId, input.runId, input.role, input.authorPrincipal?.type ?? null, input.authorPrincipal?.id ?? null, seqRow.next, JSON.stringify(input.parts), input.metadata === undefined ? null : JSON.stringify(input.metadata), AGENT_MESSAGE_CONTENT_VERSION, createdAt.toISOString(), input.completedAt ? input.completedAt.toISOString() : null);
|
|
3762
4002
|
} catch (error) {
|
|
3763
4003
|
if (isUniqueConstraintError2(error)) {
|
|
3764
|
-
throw new
|
|
4004
|
+
throw new AgentStorageError2("duplicate_id", `[SixbSqlite] Agent message '${input.id}' already exists for project '${input.projectId}'.`);
|
|
3765
4005
|
}
|
|
3766
4006
|
throw error;
|
|
3767
4007
|
}
|
|
@@ -3773,6 +4013,35 @@ class SqliteAgentMessageStore {
|
|
|
3773
4013
|
return this.requireMessage(input.projectId, input.id);
|
|
3774
4014
|
})();
|
|
3775
4015
|
}
|
|
4016
|
+
async deleteByRunId(input) {
|
|
4017
|
+
return this.db.transaction(() => {
|
|
4018
|
+
const thread = this.db.query("SELECT 1 AS present FROM agent_threads WHERE project_id = ? AND id = ?").get(input.projectId, input.threadId);
|
|
4019
|
+
if (!thread) {
|
|
4020
|
+
throw new AgentStorageError2("thread_not_found", `[SixbSqlite] Agent thread '${input.threadId}' not found for project '${input.projectId}'.`);
|
|
4021
|
+
}
|
|
4022
|
+
const deleted = this.db.query("DELETE FROM agent_messages WHERE project_id = ? AND thread_id = ? AND run_id = ?").run(input.projectId, input.threadId, input.runId);
|
|
4023
|
+
if (deleted.changes === 0) {
|
|
4024
|
+
return 0;
|
|
4025
|
+
}
|
|
4026
|
+
const updatedAt = new Date().toISOString();
|
|
4027
|
+
this.db.query(`
|
|
4028
|
+
UPDATE agent_threads
|
|
4029
|
+
SET
|
|
4030
|
+
message_count = (
|
|
4031
|
+
SELECT COUNT(*) FROM agent_messages WHERE project_id = ? AND thread_id = ?
|
|
4032
|
+
),
|
|
4033
|
+
last_message_at = (
|
|
4034
|
+
SELECT created_at FROM agent_messages
|
|
4035
|
+
WHERE project_id = ? AND thread_id = ?
|
|
4036
|
+
ORDER BY seq DESC
|
|
4037
|
+
LIMIT 1
|
|
4038
|
+
),
|
|
4039
|
+
updated_at = ?
|
|
4040
|
+
WHERE project_id = ? AND id = ?
|
|
4041
|
+
`).run(input.projectId, input.threadId, input.projectId, input.threadId, updatedAt, input.projectId, input.threadId);
|
|
4042
|
+
return deleted.changes;
|
|
4043
|
+
})();
|
|
4044
|
+
}
|
|
3776
4045
|
async getById(params) {
|
|
3777
4046
|
const row = this.db.query("SELECT * FROM agent_messages WHERE project_id = ? AND id = ?").get(params.projectId, params.id);
|
|
3778
4047
|
return row ? rowToMessageRecord(row) : null;
|
|
@@ -3783,6 +4052,10 @@ class SqliteAgentMessageStore {
|
|
|
3783
4052
|
}
|
|
3784
4053
|
const whereClauses = ["project_id = ?", "thread_id = ?"];
|
|
3785
4054
|
const args = [input.projectId, input.threadId];
|
|
4055
|
+
if (input.afterSeq !== undefined) {
|
|
4056
|
+
whereClauses.push("seq > ?");
|
|
4057
|
+
args.push(input.afterSeq);
|
|
4058
|
+
}
|
|
3786
4059
|
if (input.roles) {
|
|
3787
4060
|
whereClauses.push(`role IN (${input.roles.map(() => "?").join(", ")})`);
|
|
3788
4061
|
args.push(...input.roles);
|
|
@@ -3802,7 +4075,7 @@ class SqliteAgentMessageStore {
|
|
|
3802
4075
|
requireMessage(projectId, id) {
|
|
3803
4076
|
const row = this.db.query("SELECT * FROM agent_messages WHERE project_id = ? AND id = ?").get(projectId, id);
|
|
3804
4077
|
if (!row) {
|
|
3805
|
-
throw new
|
|
4078
|
+
throw new AgentStorageError2("invalid_state", `[SixbSqlite] Failed to load agent message '${id}' for project '${projectId}'.`);
|
|
3806
4079
|
}
|
|
3807
4080
|
return rowToMessageRecord(row);
|
|
3808
4081
|
}
|
|
@@ -3814,7 +4087,7 @@ import { normalizeRequesterGroupIds } from "@sixb/core/internal/auth";
|
|
|
3814
4087
|
import { serializeSixbFailure } from "@sixb/core/internal/errors";
|
|
3815
4088
|
import {
|
|
3816
4089
|
AGENT_RUN_FAILURE_CODES as AGENT_RUN_FAILURE_CODES2,
|
|
3817
|
-
AgentStorageError as
|
|
4090
|
+
AgentStorageError as AgentStorageError3
|
|
3818
4091
|
} from "@sixb/core/storage";
|
|
3819
4092
|
|
|
3820
4093
|
// src/run-list-query.ts
|
|
@@ -3890,10 +4163,10 @@ class SqliteAgentRunStore {
|
|
|
3890
4163
|
return this.db.transaction(() => {
|
|
3891
4164
|
const thread = this.db.query("SELECT active_run_id FROM agent_threads WHERE project_id = ? AND id = ?").get(input.projectId, input.threadId);
|
|
3892
4165
|
if (!thread) {
|
|
3893
|
-
throw new
|
|
4166
|
+
throw new AgentStorageError3("thread_not_found", `[SixbSqlite] Agent thread '${input.threadId}' not found for project '${input.projectId}'.`);
|
|
3894
4167
|
}
|
|
3895
4168
|
if (thread.active_run_id !== null) {
|
|
3896
|
-
throw new
|
|
4169
|
+
throw new AgentStorageError3("active_run_exists", `[SixbSqlite] Agent thread '${input.threadId}' already has an active run '${thread.active_run_id}'.`);
|
|
3897
4170
|
}
|
|
3898
4171
|
try {
|
|
3899
4172
|
this.db.query(`
|
|
@@ -3912,7 +4185,7 @@ class SqliteAgentRunStore {
|
|
|
3912
4185
|
`).run(input.projectId, input.id, input.executionId, input.threadId, input.agentId, input.triggerMessageId, JSON.stringify(normalizeRequesterGroupIds(input.requesterGroupIds)), createdAt.toISOString());
|
|
3913
4186
|
} catch (error) {
|
|
3914
4187
|
if (isUniqueConstraintError2(error)) {
|
|
3915
|
-
throw new
|
|
4188
|
+
throw new AgentStorageError3("duplicate_id", `[SixbSqlite] Agent run '${input.id}' already exists for project '${input.projectId}'.`);
|
|
3916
4189
|
}
|
|
3917
4190
|
throw error;
|
|
3918
4191
|
}
|
|
@@ -3969,7 +4242,7 @@ class SqliteAgentRunStore {
|
|
|
3969
4242
|
return this.db.transaction(() => {
|
|
3970
4243
|
const run = this.requireRunning(input.projectId, input.id);
|
|
3971
4244
|
if (run.execution_token !== input.executionToken) {
|
|
3972
|
-
throw new
|
|
4245
|
+
throw new AgentStorageError3("execution_lost", `[SixbSqlite] Execution token is no longer current on agent run '${input.id}'.`);
|
|
3973
4246
|
}
|
|
3974
4247
|
this.db.query(`
|
|
3975
4248
|
UPDATE agent_runs
|
|
@@ -3985,7 +4258,7 @@ class SqliteAgentRunStore {
|
|
|
3985
4258
|
return this.db.transaction(() => {
|
|
3986
4259
|
const run = this.requireRunning(input.projectId, input.id);
|
|
3987
4260
|
if (run.execution_token !== input.executionToken) {
|
|
3988
|
-
throw new
|
|
4261
|
+
throw new AgentStorageError3("execution_lost", `[SixbSqlite] Execution token is no longer current on agent run '${input.id}'.`);
|
|
3989
4262
|
}
|
|
3990
4263
|
this.db.query(`
|
|
3991
4264
|
UPDATE agent_runs
|
|
@@ -4057,10 +4330,10 @@ class SqliteAgentRunStore {
|
|
|
4057
4330
|
requireStatus(projectId, id, status) {
|
|
4058
4331
|
const row = this.db.query("SELECT * FROM agent_runs WHERE project_id = ? AND id = ?").get(projectId, id);
|
|
4059
4332
|
if (!row) {
|
|
4060
|
-
throw new
|
|
4333
|
+
throw new AgentStorageError3("run_not_found", `[SixbSqlite] Agent run '${id}' not found for project '${projectId}'.`);
|
|
4061
4334
|
}
|
|
4062
4335
|
if (row.status !== status) {
|
|
4063
|
-
throw new
|
|
4336
|
+
throw new AgentStorageError3("invalid_state", `[SixbSqlite] Agent run '${id}' is not ${status} (status '${row.status}').`);
|
|
4064
4337
|
}
|
|
4065
4338
|
return row;
|
|
4066
4339
|
}
|
|
@@ -4074,7 +4347,7 @@ class SqliteAgentRunStore {
|
|
|
4074
4347
|
requireRun(projectId, id) {
|
|
4075
4348
|
const row = this.db.query("SELECT * FROM agent_runs WHERE project_id = ? AND id = ?").get(projectId, id);
|
|
4076
4349
|
if (!row) {
|
|
4077
|
-
throw new
|
|
4350
|
+
throw new AgentStorageError3("run_not_found", `[SixbSqlite] Agent run '${id}' not found for project '${projectId}'.`);
|
|
4078
4351
|
}
|
|
4079
4352
|
return rowToRunRecord(row);
|
|
4080
4353
|
}
|
|
@@ -4082,7 +4355,7 @@ class SqliteAgentRunStore {
|
|
|
4082
4355
|
|
|
4083
4356
|
// src/agents/threads.ts
|
|
4084
4357
|
import {
|
|
4085
|
-
AgentStorageError as
|
|
4358
|
+
AgentStorageError as AgentStorageError4
|
|
4086
4359
|
} from "@sixb/core/storage";
|
|
4087
4360
|
class SqliteAgentThreadStore {
|
|
4088
4361
|
db;
|
|
@@ -4111,7 +4384,7 @@ class SqliteAgentThreadStore {
|
|
|
4111
4384
|
`).run(input.projectId, input.id, input.agentId, input.ownerPrincipal.type, input.ownerPrincipal.id, input.title ?? null, input.status ?? "active", createdAt.toISOString(), updatedAt.toISOString());
|
|
4112
4385
|
} catch (error) {
|
|
4113
4386
|
if (isUniqueConstraintError2(error)) {
|
|
4114
|
-
throw new
|
|
4387
|
+
throw new AgentStorageError4("duplicate_id", `[SixbSqlite] Agent thread '${input.id}' already exists for project '${input.projectId}'.`);
|
|
4115
4388
|
}
|
|
4116
4389
|
throw error;
|
|
4117
4390
|
}
|
|
@@ -4158,7 +4431,7 @@ class SqliteAgentThreadStore {
|
|
|
4158
4431
|
requireThread(projectId, id) {
|
|
4159
4432
|
const row = this.db.query("SELECT * FROM agent_threads WHERE project_id = ? AND id = ?").get(projectId, id);
|
|
4160
4433
|
if (!row) {
|
|
4161
|
-
throw new
|
|
4434
|
+
throw new AgentStorageError4("invalid_state", `[SixbSqlite] Failed to load agent thread '${id}' for project '${projectId}'.`);
|
|
4162
4435
|
}
|
|
4163
4436
|
return rowToThreadRecord(row);
|
|
4164
4437
|
}
|
|
@@ -4169,6 +4442,7 @@ class SqliteAgentStorage {
|
|
|
4169
4442
|
threads;
|
|
4170
4443
|
runs;
|
|
4171
4444
|
messages;
|
|
4445
|
+
checkpoints;
|
|
4172
4446
|
connection;
|
|
4173
4447
|
constructor(options) {
|
|
4174
4448
|
this.connection = openSqliteStoreConnection(options);
|
|
@@ -4178,11 +4452,497 @@ class SqliteAgentStorage {
|
|
|
4178
4452
|
this.threads = new SqliteAgentThreadStore(this.connection.db);
|
|
4179
4453
|
this.runs = new SqliteAgentRunStore(this.connection.db, options.executions);
|
|
4180
4454
|
this.messages = new SqliteAgentMessageStore(this.connection.db);
|
|
4455
|
+
this.checkpoints = new SqliteAgentContextCheckpointStore(this.connection.db);
|
|
4181
4456
|
}
|
|
4182
4457
|
close() {
|
|
4183
4458
|
closeSqliteStoreConnection(this.connection);
|
|
4184
4459
|
}
|
|
4185
4460
|
}
|
|
4461
|
+
// src/ai-cost-storage.ts
|
|
4462
|
+
import {
|
|
4463
|
+
aiModelCallCostDetails,
|
|
4464
|
+
aiModelCallCostMatchesUsage,
|
|
4465
|
+
buildAiAccountingOverviewFromFragments,
|
|
4466
|
+
normalizeAiAccountingQuery,
|
|
4467
|
+
normalizeAiModelCallAccountingQuery,
|
|
4468
|
+
normalizeAiModelCallCostRecord,
|
|
4469
|
+
parseAiModelCallCostDetails,
|
|
4470
|
+
toAccountingItem
|
|
4471
|
+
} from "@sixb/core/internal/ai-cost-storage-provider";
|
|
4472
|
+
import {
|
|
4473
|
+
AiCostStorageError
|
|
4474
|
+
} from "@sixb/core/storage";
|
|
4475
|
+
class SqliteAiCostStorage {
|
|
4476
|
+
connection;
|
|
4477
|
+
constructor(connection) {
|
|
4478
|
+
this.connection = connection;
|
|
4479
|
+
}
|
|
4480
|
+
async recordModelCallCost(input) {
|
|
4481
|
+
const record = normalizeAiModelCallCostRecord(input);
|
|
4482
|
+
runImmediateTransaction(this.connection.db, () => {
|
|
4483
|
+
const usage = this.findUsage(record.projectId, record.usageRecordId);
|
|
4484
|
+
if (!usage)
|
|
4485
|
+
throw missingUsage(record);
|
|
4486
|
+
assertCostMatchesUsage(record, usage);
|
|
4487
|
+
if (this.findCost(record.projectId, record.usageRecordId))
|
|
4488
|
+
return;
|
|
4489
|
+
try {
|
|
4490
|
+
this.insertCost(record);
|
|
4491
|
+
} catch (error) {
|
|
4492
|
+
if (!isUniqueConstraintError2(error))
|
|
4493
|
+
throw error;
|
|
4494
|
+
if (!this.findCost(record.projectId, record.usageRecordId))
|
|
4495
|
+
throw error;
|
|
4496
|
+
}
|
|
4497
|
+
});
|
|
4498
|
+
}
|
|
4499
|
+
async summarizeExecutions(input) {
|
|
4500
|
+
assertNonBlank(input.projectId, "cost summary projectId");
|
|
4501
|
+
for (const id of input.executionIds)
|
|
4502
|
+
assertNonBlank(id, "cost summary executionId");
|
|
4503
|
+
if (input.executionIds.length === 0)
|
|
4504
|
+
return [];
|
|
4505
|
+
const rows = this.connection.db.query(`
|
|
4506
|
+
WITH requested AS (SELECT DISTINCT value AS execution_id FROM json_each(?))
|
|
4507
|
+
SELECT usage.execution_id,
|
|
4508
|
+
cost.status,
|
|
4509
|
+
cost.currency,
|
|
4510
|
+
CAST(cost.amount_nanos AS TEXT) AS amount_nanos
|
|
4511
|
+
FROM requested
|
|
4512
|
+
JOIN ai_model_call_usage AS usage
|
|
4513
|
+
ON usage.project_id = ? AND usage.execution_id = requested.execution_id
|
|
4514
|
+
LEFT JOIN ai_model_call_valuations AS cost
|
|
4515
|
+
ON cost.project_id = usage.project_id AND cost.usage_record_id = usage.id
|
|
4516
|
+
`).all(JSON.stringify([...new Set(input.executionIds)]), input.projectId);
|
|
4517
|
+
return aggregateSummaryRows(input.executionIds, rows);
|
|
4518
|
+
}
|
|
4519
|
+
async queryProjectOverview(input) {
|
|
4520
|
+
const query = normalizeAiAccountingQuery(input);
|
|
4521
|
+
const rows = this.connection.db.query(SQLITE_ACCOUNTING_OVERVIEW_SQL).all(query.projectId, query.from.toISOString(), query.to.toISOString(), query.providerId ?? null, query.providerId ?? null, query.modelId ?? null, query.modelId ?? null, input.bucket);
|
|
4522
|
+
return buildAiAccountingOverviewFromFragments(input, rows.map(aggregateFragmentFromRow));
|
|
4523
|
+
}
|
|
4524
|
+
async listModelCalls(input) {
|
|
4525
|
+
const query = normalizeAiModelCallAccountingQuery(input);
|
|
4526
|
+
const parameters = accountingListFilterParameters(query);
|
|
4527
|
+
const count = this.connection.db.query(SQLITE_ACCOUNTING_LIST_COUNT_SQL).get(...parameters);
|
|
4528
|
+
const rows = this.connection.db.query(SQLITE_ACCOUNTING_LIST_PAGE_SQL).all(...parameters, query.limit, query.offset);
|
|
4529
|
+
const items = rows.map((row) => toAccountingItem(accountingItemFromRow(row)));
|
|
4530
|
+
const total = safeSqlCount(count.total, "model-call list total");
|
|
4531
|
+
return structuredClone({
|
|
4532
|
+
items,
|
|
4533
|
+
total,
|
|
4534
|
+
hasMore: query.offset + items.length < total
|
|
4535
|
+
});
|
|
4536
|
+
}
|
|
4537
|
+
insertCost(record) {
|
|
4538
|
+
const rated = record.status === "rated" ? record : undefined;
|
|
4539
|
+
const details = aiModelCallCostDetails(record);
|
|
4540
|
+
this.connection.db.query(`INSERT INTO ai_model_call_valuations (
|
|
4541
|
+
project_id, usage_record_id, status, provider_id, model_id,
|
|
4542
|
+
currency, amount_nanos, reason, details, rated_at
|
|
4543
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(record.projectId, record.usageRecordId, record.status, record.billingIdentity?.providerId ?? null, record.billingIdentity?.modelId ?? null, rated?.money.currency ?? null, rated ? BigInt(rated.money.amountNanos) : null, record.status === "unpriceable" ? record.reason : null, JSON.stringify(details), record.ratedAt.toISOString());
|
|
4544
|
+
}
|
|
4545
|
+
findCost(projectId, usageId) {
|
|
4546
|
+
const row = this.connection.db.query(`SELECT *, CAST(amount_nanos AS TEXT) AS amount_nanos
|
|
4547
|
+
FROM ai_model_call_valuations
|
|
4548
|
+
WHERE project_id = ? AND usage_record_id = ?`).get(projectId, usageId);
|
|
4549
|
+
return row ? rowToCost(row) : null;
|
|
4550
|
+
}
|
|
4551
|
+
findUsage(projectId, id) {
|
|
4552
|
+
const row = this.connection.db.query("SELECT * FROM ai_model_call_usage WHERE project_id = ? AND id = ?").get(projectId, id);
|
|
4553
|
+
return row ? usageFromRow(row, []) : null;
|
|
4554
|
+
}
|
|
4555
|
+
}
|
|
4556
|
+
var SQLITE_ACCOUNTING_LIST_FILTER = `
|
|
4557
|
+
usage.project_id = ? AND usage.occurred_at >= ? AND usage.occurred_at < ?
|
|
4558
|
+
AND (? IS NULL OR usage.provider_id = ?)
|
|
4559
|
+
AND (? IS NULL OR usage.requested_model_id = ?)
|
|
4560
|
+
AND (? IS NULL OR usage.execution_id = ?)
|
|
4561
|
+
AND (
|
|
4562
|
+
? IS NULL OR ? = CASE
|
|
4563
|
+
WHEN cost.status = 'rated' THEN 'rated'
|
|
4564
|
+
WHEN cost.status = 'unpriceable' THEN 'unpriceable'
|
|
4565
|
+
ELSE 'unvalued'
|
|
4566
|
+
END
|
|
4567
|
+
)
|
|
4568
|
+
`;
|
|
4569
|
+
var SQLITE_ACCOUNTING_LIST_COUNT_SQL = `
|
|
4570
|
+
SELECT CAST(COUNT(*) AS TEXT) AS total
|
|
4571
|
+
FROM ai_model_call_usage AS usage
|
|
4572
|
+
LEFT JOIN ai_model_call_valuations AS cost
|
|
4573
|
+
ON cost.project_id = usage.project_id AND cost.usage_record_id = usage.id
|
|
4574
|
+
WHERE ${SQLITE_ACCOUNTING_LIST_FILTER}
|
|
4575
|
+
`;
|
|
4576
|
+
var SQLITE_ACCOUNTING_LIST_PAGE_SQL = `
|
|
4577
|
+
SELECT usage.*,
|
|
4578
|
+
CASE
|
|
4579
|
+
WHEN direct_agent.id IS NOT NULL THEN 'agent'
|
|
4580
|
+
WHEN workflow_agent.node_run_id IS NOT NULL THEN 'workflowAgent'
|
|
4581
|
+
ELSE NULL
|
|
4582
|
+
END AS attribution_kind,
|
|
4583
|
+
COALESCE(direct_agent.agent_id, workflow_agent.agent_id) AS attribution_agent_id,
|
|
4584
|
+
direct_agent.id AS attribution_agent_run_id,
|
|
4585
|
+
direct_agent.thread_id AS attribution_thread_id,
|
|
4586
|
+
workflow_agent.node_run_id AS attribution_node_run_id,
|
|
4587
|
+
workflow_node.workflow_id AS attribution_workflow_id,
|
|
4588
|
+
workflow_node.workflow_run_id AS attribution_workflow_run_id,
|
|
4589
|
+
COALESCE((
|
|
4590
|
+
SELECT json_group_array(group_id) FROM (
|
|
4591
|
+
SELECT group_id FROM ai_model_call_usage_groups
|
|
4592
|
+
WHERE project_id = usage.project_id AND usage_record_id = usage.id
|
|
4593
|
+
ORDER BY group_id
|
|
4594
|
+
)
|
|
4595
|
+
), '[]') AS requester_group_ids,
|
|
4596
|
+
cost.status AS cost_status,
|
|
4597
|
+
cost.provider_id AS cost_provider_id,
|
|
4598
|
+
cost.model_id AS cost_model_id,
|
|
4599
|
+
cost.currency AS cost_currency,
|
|
4600
|
+
CAST(cost.amount_nanos AS TEXT) AS cost_amount_nanos,
|
|
4601
|
+
cost.reason AS cost_reason,
|
|
4602
|
+
cost.details AS cost_details,
|
|
4603
|
+
cost.rated_at AS cost_rated_at
|
|
4604
|
+
FROM ai_model_call_usage AS usage
|
|
4605
|
+
LEFT JOIN agent_runs AS direct_agent
|
|
4606
|
+
ON direct_agent.project_id = usage.project_id
|
|
4607
|
+
AND direct_agent.execution_id = usage.execution_id
|
|
4608
|
+
LEFT JOIN workflow_agent_node_runs AS workflow_agent
|
|
4609
|
+
ON workflow_agent.project_id = usage.project_id
|
|
4610
|
+
AND workflow_agent.execution_id = usage.execution_id
|
|
4611
|
+
LEFT JOIN workflow_node_runs AS workflow_node
|
|
4612
|
+
ON workflow_node.project_id = workflow_agent.project_id
|
|
4613
|
+
AND workflow_node.id = workflow_agent.node_run_id
|
|
4614
|
+
LEFT JOIN ai_model_call_valuations AS cost
|
|
4615
|
+
ON cost.project_id = usage.project_id AND cost.usage_record_id = usage.id
|
|
4616
|
+
WHERE ${SQLITE_ACCOUNTING_LIST_FILTER}
|
|
4617
|
+
ORDER BY usage.occurred_at DESC, usage.id ASC
|
|
4618
|
+
LIMIT ? OFFSET ?
|
|
4619
|
+
`;
|
|
4620
|
+
var SQLITE_ACCOUNTING_OVERVIEW_SQL = `
|
|
4621
|
+
WITH classified AS (
|
|
4622
|
+
SELECT usage.provider_id,
|
|
4623
|
+
usage.requested_model_id,
|
|
4624
|
+
usage.occurred_at,
|
|
4625
|
+
usage.input_tokens,
|
|
4626
|
+
usage.output_tokens,
|
|
4627
|
+
usage.uncached_input_tokens,
|
|
4628
|
+
usage.cache_read_input_tokens,
|
|
4629
|
+
usage.cache_write_input_tokens,
|
|
4630
|
+
usage.text_output_tokens,
|
|
4631
|
+
usage.reasoning_output_tokens,
|
|
4632
|
+
usage.reporting_status,
|
|
4633
|
+
COALESCE(direct_agent.agent_id, workflow_agent.agent_id) AS attribution_agent_id,
|
|
4634
|
+
workflow_node.workflow_id AS attribution_workflow_id,
|
|
4635
|
+
CASE
|
|
4636
|
+
WHEN cost.status = 'rated' THEN 'rated'
|
|
4637
|
+
WHEN cost.status = 'unpriceable' THEN 'unpriceable'
|
|
4638
|
+
ELSE 'unvalued'
|
|
4639
|
+
END AS valuation_status,
|
|
4640
|
+
CASE WHEN cost.status = 'rated' THEN cost.currency ELSE NULL END AS amount_currency,
|
|
4641
|
+
CASE WHEN cost.status = 'rated' THEN cost.amount_nanos ELSE NULL END AS amount_nanos
|
|
4642
|
+
FROM ai_model_call_usage AS usage
|
|
4643
|
+
LEFT JOIN agent_runs AS direct_agent
|
|
4644
|
+
ON direct_agent.project_id = usage.project_id
|
|
4645
|
+
AND direct_agent.execution_id = usage.execution_id
|
|
4646
|
+
LEFT JOIN workflow_agent_node_runs AS workflow_agent
|
|
4647
|
+
ON workflow_agent.project_id = usage.project_id
|
|
4648
|
+
AND workflow_agent.execution_id = usage.execution_id
|
|
4649
|
+
LEFT JOIN workflow_node_runs AS workflow_node
|
|
4650
|
+
ON workflow_node.project_id = workflow_agent.project_id
|
|
4651
|
+
AND workflow_node.id = workflow_agent.node_run_id
|
|
4652
|
+
LEFT JOIN ai_model_call_valuations AS cost
|
|
4653
|
+
ON cost.project_id = usage.project_id AND cost.usage_record_id = usage.id
|
|
4654
|
+
WHERE usage.project_id = ? AND usage.occurred_at >= ? AND usage.occurred_at < ?
|
|
4655
|
+
AND (? IS NULL OR usage.provider_id = ?)
|
|
4656
|
+
AND (? IS NULL OR usage.requested_model_id = ?)
|
|
4657
|
+
), expanded AS (
|
|
4658
|
+
SELECT 'totals' AS dimension, NULL AS key_1, NULL AS key_2, NULL AS bucket_start,
|
|
4659
|
+
classified.*
|
|
4660
|
+
FROM classified
|
|
4661
|
+
UNION ALL
|
|
4662
|
+
SELECT 'series', NULL, NULL,
|
|
4663
|
+
CASE ?
|
|
4664
|
+
WHEN 'hour' THEN strftime('%Y-%m-%dT%H:00:00.000Z', occurred_at)
|
|
4665
|
+
WHEN 'day' THEN strftime('%Y-%m-%dT00:00:00.000Z', occurred_at)
|
|
4666
|
+
ELSE strftime(
|
|
4667
|
+
'%Y-%m-%dT00:00:00.000Z',
|
|
4668
|
+
occurred_at,
|
|
4669
|
+
printf('-%d days', (CAST(strftime('%w', occurred_at) AS INTEGER) + 6) % 7)
|
|
4670
|
+
)
|
|
4671
|
+
END,
|
|
4672
|
+
classified.*
|
|
4673
|
+
FROM classified
|
|
4674
|
+
UNION ALL
|
|
4675
|
+
SELECT 'model', provider_id, requested_model_id, NULL, classified.* FROM classified
|
|
4676
|
+
UNION ALL
|
|
4677
|
+
SELECT 'agent', attribution_agent_id, NULL, NULL, classified.*
|
|
4678
|
+
FROM classified WHERE attribution_agent_id IS NOT NULL
|
|
4679
|
+
UNION ALL
|
|
4680
|
+
SELECT 'workflow', attribution_workflow_id, NULL, NULL, classified.*
|
|
4681
|
+
FROM classified WHERE attribution_workflow_id IS NOT NULL
|
|
4682
|
+
)
|
|
4683
|
+
SELECT dimension,
|
|
4684
|
+
key_1,
|
|
4685
|
+
key_2,
|
|
4686
|
+
bucket_start,
|
|
4687
|
+
amount_currency,
|
|
4688
|
+
CAST(COUNT(*) AS TEXT) AS model_call_count,
|
|
4689
|
+
CAST(SUM(CASE WHEN reporting_status <> 'unavailable' THEN 1 ELSE 0 END) AS TEXT)
|
|
4690
|
+
AS reported_usage_call_count,
|
|
4691
|
+
CAST(COUNT(input_tokens) AS TEXT) AS input_tokens_count,
|
|
4692
|
+
CAST(SUM(input_tokens) AS TEXT) AS input_tokens_sum,
|
|
4693
|
+
CAST(COUNT(output_tokens) AS TEXT) AS output_tokens_count,
|
|
4694
|
+
CAST(SUM(output_tokens) AS TEXT) AS output_tokens_sum,
|
|
4695
|
+
CAST(COUNT(uncached_input_tokens) AS TEXT) AS uncached_input_tokens_count,
|
|
4696
|
+
CAST(SUM(uncached_input_tokens) AS TEXT) AS uncached_input_tokens_sum,
|
|
4697
|
+
CAST(COUNT(cache_read_input_tokens) AS TEXT) AS cache_read_input_tokens_count,
|
|
4698
|
+
CAST(SUM(cache_read_input_tokens) AS TEXT) AS cache_read_input_tokens_sum,
|
|
4699
|
+
CAST(COUNT(cache_write_input_tokens) AS TEXT) AS cache_write_input_tokens_count,
|
|
4700
|
+
CAST(SUM(cache_write_input_tokens) AS TEXT) AS cache_write_input_tokens_sum,
|
|
4701
|
+
CAST(COUNT(text_output_tokens) AS TEXT) AS text_output_tokens_count,
|
|
4702
|
+
CAST(SUM(text_output_tokens) AS TEXT) AS text_output_tokens_sum,
|
|
4703
|
+
CAST(COUNT(reasoning_output_tokens) AS TEXT) AS reasoning_output_tokens_count,
|
|
4704
|
+
CAST(SUM(reasoning_output_tokens) AS TEXT) AS reasoning_output_tokens_sum,
|
|
4705
|
+
CAST(SUM(CASE WHEN valuation_status = 'rated' THEN 1 ELSE 0 END) AS TEXT)
|
|
4706
|
+
AS rated_call_count,
|
|
4707
|
+
CAST(SUM(CASE WHEN valuation_status = 'unpriceable' THEN 1 ELSE 0 END) AS TEXT)
|
|
4708
|
+
AS unpriceable_call_count,
|
|
4709
|
+
CAST(SUM(CASE WHEN valuation_status = 'unvalued' THEN 1 ELSE 0 END) AS TEXT)
|
|
4710
|
+
AS unvalued_call_count,
|
|
4711
|
+
CAST(SUM(amount_nanos / 1000000000000) AS TEXT) AS amount_high,
|
|
4712
|
+
CAST(SUM((amount_nanos / 1000000) % 1000000) AS TEXT) AS amount_middle,
|
|
4713
|
+
CAST(SUM(amount_nanos % 1000000) AS TEXT) AS amount_low
|
|
4714
|
+
FROM expanded
|
|
4715
|
+
GROUP BY dimension, key_1, key_2, bucket_start, amount_currency
|
|
4716
|
+
`;
|
|
4717
|
+
function accountingListFilterParameters(query) {
|
|
4718
|
+
return [
|
|
4719
|
+
query.projectId,
|
|
4720
|
+
query.from.toISOString(),
|
|
4721
|
+
query.to.toISOString(),
|
|
4722
|
+
query.providerId ?? null,
|
|
4723
|
+
query.providerId ?? null,
|
|
4724
|
+
query.modelId ?? null,
|
|
4725
|
+
query.modelId ?? null,
|
|
4726
|
+
query.executionId ?? null,
|
|
4727
|
+
query.executionId ?? null,
|
|
4728
|
+
query.valuationStatus ?? null,
|
|
4729
|
+
query.valuationStatus ?? null
|
|
4730
|
+
];
|
|
4731
|
+
}
|
|
4732
|
+
function aggregateFragmentFromRow(row) {
|
|
4733
|
+
const dimensionKeys = row.dimension === "model" ? { providerId: required(row.key_1), modelId: required(row.key_2) } : row.dimension === "agent" ? { agentId: required(row.key_1) } : row.dimension === "workflow" ? { workflowId: required(row.key_1) } : row.dimension === "series" ? { start: new Date(required(row.bucket_start)) } : {};
|
|
4734
|
+
return {
|
|
4735
|
+
dimension: row.dimension,
|
|
4736
|
+
...dimensionKeys,
|
|
4737
|
+
modelCallCount: row.model_call_count,
|
|
4738
|
+
usage: {
|
|
4739
|
+
reportedCallCount: row.reported_usage_call_count,
|
|
4740
|
+
inputTokens: meterFragment(row.input_tokens_count, row.input_tokens_sum),
|
|
4741
|
+
outputTokens: meterFragment(row.output_tokens_count, row.output_tokens_sum),
|
|
4742
|
+
uncachedInputTokens: meterFragment(row.uncached_input_tokens_count, row.uncached_input_tokens_sum),
|
|
4743
|
+
cacheReadInputTokens: meterFragment(row.cache_read_input_tokens_count, row.cache_read_input_tokens_sum),
|
|
4744
|
+
cacheWriteInputTokens: meterFragment(row.cache_write_input_tokens_count, row.cache_write_input_tokens_sum),
|
|
4745
|
+
textOutputTokens: meterFragment(row.text_output_tokens_count, row.text_output_tokens_sum),
|
|
4746
|
+
reasoningOutputTokens: meterFragment(row.reasoning_output_tokens_count, row.reasoning_output_tokens_sum)
|
|
4747
|
+
},
|
|
4748
|
+
costs: {
|
|
4749
|
+
...row.amount_currency === null || row.amount_high === null || row.amount_middle === null || row.amount_low === null ? {} : {
|
|
4750
|
+
amount: {
|
|
4751
|
+
currency: row.amount_currency,
|
|
4752
|
+
amountNanos: sqliteAggregateAmount(row)
|
|
4753
|
+
}
|
|
4754
|
+
},
|
|
4755
|
+
ratedCallCount: row.rated_call_count,
|
|
4756
|
+
unpriceableCallCount: row.unpriceable_call_count,
|
|
4757
|
+
unvaluedCallCount: row.unvalued_call_count
|
|
4758
|
+
}
|
|
4759
|
+
};
|
|
4760
|
+
}
|
|
4761
|
+
function meterFragment(presentCallCount, total) {
|
|
4762
|
+
return { presentCallCount, total };
|
|
4763
|
+
}
|
|
4764
|
+
function sqliteAggregateAmount(row) {
|
|
4765
|
+
return (BigInt(required(row.amount_high)) * 1000000000000n + BigInt(required(row.amount_middle)) * 1000000n + BigInt(required(row.amount_low))).toString();
|
|
4766
|
+
}
|
|
4767
|
+
function accountingItemFromRow(row) {
|
|
4768
|
+
const usage = usageFromRow(row, parseStringArray(row.requester_group_ids));
|
|
4769
|
+
const cost = row.cost_status === null ? undefined : costFromJoinedRow(row);
|
|
4770
|
+
const attribution = row.attribution_kind === null ? undefined : row.attribution_kind === "agent" ? {
|
|
4771
|
+
kind: "agent",
|
|
4772
|
+
agentId: required(row.attribution_agent_id),
|
|
4773
|
+
agentRunId: required(row.attribution_agent_run_id),
|
|
4774
|
+
threadId: required(row.attribution_thread_id)
|
|
4775
|
+
} : {
|
|
4776
|
+
kind: "workflowAgent",
|
|
4777
|
+
agentId: required(row.attribution_agent_id),
|
|
4778
|
+
nodeRunId: required(row.attribution_node_run_id),
|
|
4779
|
+
workflowId: required(row.attribution_workflow_id),
|
|
4780
|
+
workflowRunId: required(row.attribution_workflow_run_id)
|
|
4781
|
+
};
|
|
4782
|
+
return {
|
|
4783
|
+
usage,
|
|
4784
|
+
...attribution ? { attribution } : {},
|
|
4785
|
+
...cost ? { cost } : {}
|
|
4786
|
+
};
|
|
4787
|
+
}
|
|
4788
|
+
function usageFromRow(row, requesterGroupIds) {
|
|
4789
|
+
return {
|
|
4790
|
+
id: row.id,
|
|
4791
|
+
projectId: row.project_id,
|
|
4792
|
+
executionId: row.execution_id,
|
|
4793
|
+
attempt: row.attempt,
|
|
4794
|
+
callId: row.call_id,
|
|
4795
|
+
requesterGroupIds,
|
|
4796
|
+
providerId: row.provider_id,
|
|
4797
|
+
requestedModelId: row.requested_model_id,
|
|
4798
|
+
...row.response_model_id === null ? {} : { responseModelId: row.response_model_id },
|
|
4799
|
+
responseId: row.response_id,
|
|
4800
|
+
usage: {
|
|
4801
|
+
...row.input_tokens === null ? {} : { inputTokens: row.input_tokens },
|
|
4802
|
+
...row.output_tokens === null ? {} : { outputTokens: row.output_tokens },
|
|
4803
|
+
...row.total_tokens === null ? {} : { totalTokens: row.total_tokens },
|
|
4804
|
+
...row.uncached_input_tokens === null ? {} : { uncachedInputTokens: row.uncached_input_tokens },
|
|
4805
|
+
...row.cache_read_input_tokens === null ? {} : { cacheReadInputTokens: row.cache_read_input_tokens },
|
|
4806
|
+
...row.cache_write_input_tokens === null ? {} : { cacheWriteInputTokens: row.cache_write_input_tokens },
|
|
4807
|
+
...row.text_output_tokens === null ? {} : { textOutputTokens: row.text_output_tokens },
|
|
4808
|
+
...row.reasoning_output_tokens === null ? {} : { reasoningOutputTokens: row.reasoning_output_tokens },
|
|
4809
|
+
reportingStatus: row.reporting_status
|
|
4810
|
+
},
|
|
4811
|
+
...row.raw_usage === null ? {} : { rawUsage: JSON.parse(row.raw_usage) },
|
|
4812
|
+
occurredAt: new Date(row.occurred_at),
|
|
4813
|
+
recordedAt: new Date(row.recorded_at)
|
|
4814
|
+
};
|
|
4815
|
+
}
|
|
4816
|
+
function rowToCost(row) {
|
|
4817
|
+
return costRecord({
|
|
4818
|
+
projectId: row.project_id,
|
|
4819
|
+
usageId: row.usage_record_id,
|
|
4820
|
+
status: row.status,
|
|
4821
|
+
providerId: row.provider_id,
|
|
4822
|
+
modelId: row.model_id,
|
|
4823
|
+
currency: row.currency,
|
|
4824
|
+
amountNanos: row.amount_nanos,
|
|
4825
|
+
reason: row.reason,
|
|
4826
|
+
details: row.details,
|
|
4827
|
+
ratedAt: row.rated_at
|
|
4828
|
+
});
|
|
4829
|
+
}
|
|
4830
|
+
function costFromJoinedRow(row) {
|
|
4831
|
+
return costRecord({
|
|
4832
|
+
projectId: row.project_id,
|
|
4833
|
+
usageId: row.id,
|
|
4834
|
+
status: required(row.cost_status),
|
|
4835
|
+
providerId: row.cost_provider_id,
|
|
4836
|
+
modelId: row.cost_model_id,
|
|
4837
|
+
currency: row.cost_currency,
|
|
4838
|
+
amountNanos: row.cost_amount_nanos,
|
|
4839
|
+
reason: row.cost_reason,
|
|
4840
|
+
details: required(row.cost_details),
|
|
4841
|
+
ratedAt: required(row.cost_rated_at)
|
|
4842
|
+
});
|
|
4843
|
+
}
|
|
4844
|
+
function costRecord(input) {
|
|
4845
|
+
const details = parseAiModelCallCostDetails(input.details);
|
|
4846
|
+
const base = {
|
|
4847
|
+
projectId: input.projectId,
|
|
4848
|
+
usageRecordId: input.usageId,
|
|
4849
|
+
pricingContext: details.pricingContext,
|
|
4850
|
+
priceSource: {
|
|
4851
|
+
...details.priceSource,
|
|
4852
|
+
observedAt: new Date(details.priceSource.observedAt)
|
|
4853
|
+
},
|
|
4854
|
+
ratedAt: new Date(input.ratedAt)
|
|
4855
|
+
};
|
|
4856
|
+
if (input.status === "rated") {
|
|
4857
|
+
return normalizeAiModelCallCostRecord({
|
|
4858
|
+
...base,
|
|
4859
|
+
status: "rated",
|
|
4860
|
+
billingIdentity: { providerId: required(input.providerId), modelId: required(input.modelId) },
|
|
4861
|
+
money: { currency: required(input.currency), amountNanos: required(input.amountNanos) },
|
|
4862
|
+
components: required(details.components)
|
|
4863
|
+
});
|
|
4864
|
+
}
|
|
4865
|
+
return normalizeAiModelCallCostRecord({
|
|
4866
|
+
...base,
|
|
4867
|
+
status: "unpriceable",
|
|
4868
|
+
...input.providerId && input.modelId ? { billingIdentity: { providerId: input.providerId, modelId: input.modelId } } : {},
|
|
4869
|
+
reason: required(input.reason),
|
|
4870
|
+
...details.missingMeters ? { missingMeters: details.missingMeters } : {}
|
|
4871
|
+
});
|
|
4872
|
+
}
|
|
4873
|
+
function aggregateSummaryRows(ids, rows) {
|
|
4874
|
+
const map = new Map(ids.map((id) => [id, accumulator()]));
|
|
4875
|
+
for (const row of rows) {
|
|
4876
|
+
const value = map.get(row.execution_id);
|
|
4877
|
+
if (!value)
|
|
4878
|
+
continue;
|
|
4879
|
+
if (row.status === null)
|
|
4880
|
+
value.unvaluedCallCount += 1;
|
|
4881
|
+
else if (row.status === "unpriceable")
|
|
4882
|
+
value.unpriceableCallCount += 1;
|
|
4883
|
+
else {
|
|
4884
|
+
value.ratedCallCount += 1;
|
|
4885
|
+
add(value.amounts, required(row.currency), required(row.amount_nanos));
|
|
4886
|
+
}
|
|
4887
|
+
}
|
|
4888
|
+
return ids.map((id) => finish(map.get(id)));
|
|
4889
|
+
}
|
|
4890
|
+
function accumulator() {
|
|
4891
|
+
return {
|
|
4892
|
+
amounts: new Map,
|
|
4893
|
+
ratedCallCount: 0,
|
|
4894
|
+
unpriceableCallCount: 0,
|
|
4895
|
+
unvaluedCallCount: 0
|
|
4896
|
+
};
|
|
4897
|
+
}
|
|
4898
|
+
function finish(value) {
|
|
4899
|
+
return {
|
|
4900
|
+
amounts: [...value.amounts].sort(([left], [right]) => left.localeCompare(right)).map(([currency, amount]) => ({ currency, amountNanos: amount.toString() })),
|
|
4901
|
+
ratedCallCount: value.ratedCallCount,
|
|
4902
|
+
unpriceableCallCount: value.unpriceableCallCount,
|
|
4903
|
+
unvaluedCallCount: value.unvaluedCallCount
|
|
4904
|
+
};
|
|
4905
|
+
}
|
|
4906
|
+
function add(amounts, currency, amount) {
|
|
4907
|
+
amounts.set(currency, (amounts.get(currency) ?? 0n) + BigInt(amount));
|
|
4908
|
+
}
|
|
4909
|
+
function assertCostMatchesUsage(record, usage) {
|
|
4910
|
+
if (!aiModelCallCostMatchesUsage(record, usage)) {
|
|
4911
|
+
throw new AiCostStorageError("cost_mismatch", `[SixbSqlite] AI valuation for usage '${record.usageRecordId}' does not match its usage record.`);
|
|
4912
|
+
}
|
|
4913
|
+
}
|
|
4914
|
+
function missingUsage(record) {
|
|
4915
|
+
return new AiCostStorageError("missing_usage", `[SixbSqlite] AI usage '${record.usageRecordId}' does not exist.`);
|
|
4916
|
+
}
|
|
4917
|
+
function parseStringArray(value) {
|
|
4918
|
+
const parsed = JSON.parse(value);
|
|
4919
|
+
if (!Array.isArray(parsed) || parsed.some((item) => typeof item !== "string")) {
|
|
4920
|
+
throw new Error("[SixbSqlite] AI requester group ids are invalid.");
|
|
4921
|
+
}
|
|
4922
|
+
return parsed;
|
|
4923
|
+
}
|
|
4924
|
+
function assertNonBlank(value, field) {
|
|
4925
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
4926
|
+
throw new TypeError(`[Sixb] AI ${field} must be nonblank.`);
|
|
4927
|
+
}
|
|
4928
|
+
}
|
|
4929
|
+
function safeSqlCount(value, field) {
|
|
4930
|
+
if (!/^(0|[1-9]\d*)$/.test(value)) {
|
|
4931
|
+
throw new Error(`[SixbSqlite] AI accounting ${field} is invalid.`);
|
|
4932
|
+
}
|
|
4933
|
+
const count = Number(value);
|
|
4934
|
+
if (!Number.isSafeInteger(count)) {
|
|
4935
|
+
throw new TypeError(`[Sixb] AI accounting ${field} exceeds the safe integer range.`);
|
|
4936
|
+
}
|
|
4937
|
+
return count;
|
|
4938
|
+
}
|
|
4939
|
+
function required(value) {
|
|
4940
|
+
if (value === null || value === undefined) {
|
|
4941
|
+
throw new Error("[SixbSqlite] AI valuation column is unexpectedly null.");
|
|
4942
|
+
}
|
|
4943
|
+
return value;
|
|
4944
|
+
}
|
|
4945
|
+
|
|
4186
4946
|
// src/ai-usage-storage.ts
|
|
4187
4947
|
import {
|
|
4188
4948
|
AiUsageStorageError,
|
|
@@ -4232,6 +4992,17 @@ class SqliteAiUsageStorage {
|
|
|
4232
4992
|
});
|
|
4233
4993
|
return summary;
|
|
4234
4994
|
}
|
|
4995
|
+
async getLatestForExecution(input) {
|
|
4996
|
+
assertNonBlankProjectId(input.projectId);
|
|
4997
|
+
assertAiUsageExecutionId(input.executionId);
|
|
4998
|
+
const row = this.connection.db.query(`
|
|
4999
|
+
SELECT * FROM ai_model_call_usage
|
|
5000
|
+
WHERE project_id = ? AND execution_id = ?
|
|
5001
|
+
ORDER BY occurred_at DESC, id DESC
|
|
5002
|
+
LIMIT 1
|
|
5003
|
+
`).get(input.projectId, input.executionId);
|
|
5004
|
+
return row ? this.rowToRecord(row) : null;
|
|
5005
|
+
}
|
|
4235
5006
|
async summarizeExecutions(input) {
|
|
4236
5007
|
assertNonBlankProjectId(input.projectId);
|
|
4237
5008
|
for (const executionId of input.executionIds)
|
|
@@ -4308,7 +5079,7 @@ class SqliteAiUsageStorage {
|
|
|
4308
5079
|
...row.response_model_id === null ? {} : { responseModelId: row.response_model_id },
|
|
4309
5080
|
responseId: row.response_id,
|
|
4310
5081
|
usage: {
|
|
4311
|
-
...
|
|
5082
|
+
...usageFromRow2(row),
|
|
4312
5083
|
...row.total_tokens === null ? {} : { totalTokens: row.total_tokens },
|
|
4313
5084
|
reportingStatus: row.reporting_status
|
|
4314
5085
|
},
|
|
@@ -4342,7 +5113,7 @@ function aggregateExecutionRows(executionIds, rows) {
|
|
|
4342
5113
|
for (const executionId of executionIds)
|
|
4343
5114
|
usageByExecutionId.set(executionId, []);
|
|
4344
5115
|
for (const row of rows) {
|
|
4345
|
-
usageByExecutionId.get(row.execution_id)?.push(
|
|
5116
|
+
usageByExecutionId.get(row.execution_id)?.push(usageFromRow2(row));
|
|
4346
5117
|
}
|
|
4347
5118
|
return executionIds.map((executionId) => {
|
|
4348
5119
|
const usages = usageByExecutionId.get(executionId) ?? [];
|
|
@@ -4352,7 +5123,7 @@ function aggregateExecutionRows(executionIds, rows) {
|
|
|
4352
5123
|
};
|
|
4353
5124
|
});
|
|
4354
5125
|
}
|
|
4355
|
-
function
|
|
5126
|
+
function usageFromRow2(row) {
|
|
4356
5127
|
return {
|
|
4357
5128
|
...row.input_tokens === null ? {} : { inputTokens: row.input_tokens },
|
|
4358
5129
|
...row.output_tokens === null ? {} : { outputTokens: row.output_tokens },
|
|
@@ -6582,7 +7353,7 @@ function connectionRunFromRow(row) {
|
|
|
6582
7353
|
...base,
|
|
6583
7354
|
status: "waiting",
|
|
6584
7355
|
waitingFor: "provider_authorization",
|
|
6585
|
-
expiresAt: new Date(
|
|
7356
|
+
expiresAt: new Date(required2(row.expires_at, "run expiry"))
|
|
6586
7357
|
};
|
|
6587
7358
|
}
|
|
6588
7359
|
if (row.status === "waiting" && row.waiting_for === "account_selection") {
|
|
@@ -6590,28 +7361,28 @@ function connectionRunFromRow(row) {
|
|
|
6590
7361
|
...base,
|
|
6591
7362
|
status: "waiting",
|
|
6592
7363
|
waitingFor: "account_selection",
|
|
6593
|
-
authorizationId:
|
|
6594
|
-
expiresAt: new Date(
|
|
7364
|
+
authorizationId: required2(row.authorization_id, "authorization id"),
|
|
7365
|
+
expiresAt: new Date(required2(row.expires_at, "run expiry"))
|
|
6595
7366
|
};
|
|
6596
7367
|
}
|
|
6597
7368
|
if (row.status === "running") {
|
|
6598
7369
|
return {
|
|
6599
7370
|
...base,
|
|
6600
7371
|
status: "running",
|
|
6601
|
-
processingId:
|
|
6602
|
-
callbackStartedAt: new Date(
|
|
6603
|
-
expiresAt: new Date(
|
|
7372
|
+
processingId: required2(row.processing_id, "processing id"),
|
|
7373
|
+
callbackStartedAt: new Date(required2(row.callback_started_at, "callback start")),
|
|
7374
|
+
expiresAt: new Date(required2(row.expires_at, "run expiry")),
|
|
6604
7375
|
...row.authorization_id === null ? {} : { authorizationId: row.authorization_id }
|
|
6605
7376
|
};
|
|
6606
7377
|
}
|
|
6607
|
-
const finishedAt = new Date(
|
|
7378
|
+
const finishedAt = new Date(required2(row.finished_at, "run finish"));
|
|
6608
7379
|
if (row.status === "succeeded") {
|
|
6609
7380
|
return {
|
|
6610
7381
|
...base,
|
|
6611
7382
|
status: "succeeded",
|
|
6612
|
-
authorizationId:
|
|
7383
|
+
authorizationId: required2(row.authorization_id, "authorization id"),
|
|
6613
7384
|
...row.cleanup_authorization_id === null ? {} : { cleanupAuthorizationId: row.cleanup_authorization_id },
|
|
6614
|
-
connections: parseJson(
|
|
7385
|
+
connections: parseJson(required2(row.connections, "connections")).map(connectionFromSerialized),
|
|
6615
7386
|
finishedAt
|
|
6616
7387
|
};
|
|
6617
7388
|
}
|
|
@@ -6619,7 +7390,7 @@ function connectionRunFromRow(row) {
|
|
|
6619
7390
|
return {
|
|
6620
7391
|
...base,
|
|
6621
7392
|
status: "failed",
|
|
6622
|
-
error: parseJson(
|
|
7393
|
+
error: parseJson(required2(row.error, "run error")),
|
|
6623
7394
|
...row.authorization_id === null ? {} : { authorizationId: row.authorization_id },
|
|
6624
7395
|
finishedAt
|
|
6625
7396
|
};
|
|
@@ -6689,20 +7460,20 @@ function authorizationFromRow(row) {
|
|
|
6689
7460
|
function mutationFromRow(row) {
|
|
6690
7461
|
if (row.mutation_id === null)
|
|
6691
7462
|
return;
|
|
6692
|
-
const kind =
|
|
6693
|
-
const phase =
|
|
7463
|
+
const kind = required2(row.mutation_kind, "credential mutation kind");
|
|
7464
|
+
const phase = required2(row.mutation_phase, "credential mutation phase");
|
|
6694
7465
|
const stagedCredentials = row.staged_credentials === null ? undefined : {
|
|
6695
7466
|
credentials: parseJson(row.staged_credentials),
|
|
6696
7467
|
...row.staged_credential_expires_at === null ? {} : { credentialExpiresAt: new Date(row.staged_credential_expires_at) },
|
|
6697
|
-
scopes: parseJson(
|
|
7468
|
+
scopes: parseJson(required2(row.staged_scopes, "staged credential scopes"))
|
|
6698
7469
|
};
|
|
6699
7470
|
return {
|
|
6700
7471
|
id: row.mutation_id,
|
|
6701
7472
|
kind,
|
|
6702
7473
|
phase,
|
|
6703
|
-
holderId:
|
|
6704
|
-
expiresAt: new Date(
|
|
6705
|
-
deadlineAt: new Date(
|
|
7474
|
+
holderId: required2(row.mutation_holder_id, "credential mutation holder"),
|
|
7475
|
+
expiresAt: new Date(required2(row.mutation_expires_at, "credential mutation expiry")),
|
|
7476
|
+
deadlineAt: new Date(required2(row.mutation_deadline_at, "credential mutation deadline")),
|
|
6706
7477
|
...row.mutation_expected_connection_ids === null ? {} : {
|
|
6707
7478
|
expectedConnectionIds: parseJson(row.mutation_expected_connection_ids)
|
|
6708
7479
|
},
|
|
@@ -6745,7 +7516,7 @@ function iso(value) {
|
|
|
6745
7516
|
function optionalIso(value) {
|
|
6746
7517
|
return value?.toISOString() ?? null;
|
|
6747
7518
|
}
|
|
6748
|
-
function
|
|
7519
|
+
function required2(value, label) {
|
|
6749
7520
|
if (value !== null)
|
|
6750
7521
|
return value;
|
|
6751
7522
|
throw new Error(`[SixbSqlite] Stored ${label} is missing.`);
|
|
@@ -6850,6 +7621,9 @@ function isDuplicateExecutionError(error) {
|
|
|
6850
7621
|
return error instanceof Error && error.message.includes("UNIQUE constraint failed: executions.project_id, executions.id");
|
|
6851
7622
|
}
|
|
6852
7623
|
|
|
7624
|
+
// src/object-storage.ts
|
|
7625
|
+
import { linkBatchKey, objectBatchKey } from "@sixb/core/storage";
|
|
7626
|
+
|
|
6853
7627
|
// src/object-query-compiler.ts
|
|
6854
7628
|
import {
|
|
6855
7629
|
ObjectQueryExecutionError
|
|
@@ -6865,6 +7639,8 @@ function compileObjectQueryInternal(projectId, query, ctx) {
|
|
|
6865
7639
|
switch (query.kind) {
|
|
6866
7640
|
case "start":
|
|
6867
7641
|
return compileStart(projectId, query);
|
|
7642
|
+
case "refs":
|
|
7643
|
+
return compileRefs(projectId, query);
|
|
6868
7644
|
case "filter":
|
|
6869
7645
|
return compileFilter(projectId, query.input, query.predicate);
|
|
6870
7646
|
case "sort":
|
|
@@ -6912,6 +7688,50 @@ function compileStart(projectId, query) {
|
|
|
6912
7688
|
}
|
|
6913
7689
|
};
|
|
6914
7690
|
}
|
|
7691
|
+
function compileRefs(projectId, query) {
|
|
7692
|
+
if (query.refs.length === 0) {
|
|
7693
|
+
throw new Error("[Sixb] SQLite object storage requires at least one ref");
|
|
7694
|
+
}
|
|
7695
|
+
const order = compileOrder(identityOrderFields());
|
|
7696
|
+
const selectedOrder = compileOrder(identityOrderFields(), "selected");
|
|
7697
|
+
const refsJson = JSON.stringify(query.refs);
|
|
7698
|
+
const requested = `
|
|
7699
|
+
SELECT DISTINCT
|
|
7700
|
+
json_extract(ref.value, '$.objectTypeId') AS object_type_id,
|
|
7701
|
+
json_extract(ref.value, '$.primaryId') AS primary_id
|
|
7702
|
+
FROM json_each(?) AS ref
|
|
7703
|
+
`;
|
|
7704
|
+
const sql = `
|
|
7705
|
+
WITH requested AS (${requested})
|
|
7706
|
+
SELECT selected.*, selected.properties AS _cursor_properties
|
|
7707
|
+
FROM requested
|
|
7708
|
+
JOIN objects AS selected
|
|
7709
|
+
ON selected.project_id = ?
|
|
7710
|
+
AND selected.object_type_id = requested.object_type_id
|
|
7711
|
+
AND selected.primary_id = requested.primary_id
|
|
7712
|
+
ORDER BY ${selectedOrder.sql}
|
|
7713
|
+
`;
|
|
7714
|
+
return {
|
|
7715
|
+
sql,
|
|
7716
|
+
args: [refsJson, projectId, ...selectedOrder.args],
|
|
7717
|
+
totalSql: `
|
|
7718
|
+
WITH requested AS (${requested})
|
|
7719
|
+
SELECT COUNT(*) AS total
|
|
7720
|
+
FROM requested
|
|
7721
|
+
JOIN objects AS selected
|
|
7722
|
+
ON selected.project_id = ?
|
|
7723
|
+
AND selected.object_type_id = requested.object_type_id
|
|
7724
|
+
AND selected.primary_id = requested.primary_id
|
|
7725
|
+
`,
|
|
7726
|
+
totalArgs: [refsJson, projectId],
|
|
7727
|
+
order,
|
|
7728
|
+
hasMore: () => false,
|
|
7729
|
+
trimRows: identityRows,
|
|
7730
|
+
nextPageToken: () => {
|
|
7731
|
+
return;
|
|
7732
|
+
}
|
|
7733
|
+
};
|
|
7734
|
+
}
|
|
6915
7735
|
function compileFilter(projectId, inputQuery, predicateNode) {
|
|
6916
7736
|
const predicate = compilePredicate(predicateNode);
|
|
6917
7737
|
return compileWhere(projectId, inputQuery, predicate);
|
|
@@ -7655,6 +8475,7 @@ var SQLITE_OBJECT_QUERY_CAPABILITIES = {
|
|
|
7655
8475
|
facetObjects: true,
|
|
7656
8476
|
nodes: {
|
|
7657
8477
|
start: true,
|
|
8478
|
+
refs: true,
|
|
7658
8479
|
filter: true,
|
|
7659
8480
|
text: true,
|
|
7660
8481
|
sort: true,
|
|
@@ -7706,7 +8527,7 @@ var SQLITE_OBJECT_QUERY_CAPABILITIES = {
|
|
|
7706
8527
|
stablePageTokens: true
|
|
7707
8528
|
},
|
|
7708
8529
|
notes: [
|
|
7709
|
-
"SQLite object query pushdown supports start/filter/text/sort/limit/page/traverse/set/project/expand over JSON properties and object links.",
|
|
8530
|
+
"SQLite object query pushdown supports start/refs/filter/text/sort/limit/page/traverse/set/project/expand over JSON properties and object links.",
|
|
7710
8531
|
"expand hydrates linked objects in-database (top-N per parent via row_number() + json_group_array); core resolves each expansion's cardinality before pushdown, and a mixed/unresolved one stays on the fallback.",
|
|
7711
8532
|
"Ordered decimal predicates and sorting use the bounded core fallback because SQLite has no native exact decimal type; canonical decimal equality remains pushdown-safe.",
|
|
7712
8533
|
"Relevance sorting, vector search, and unresolved start.includeSubtypes remain planner fallback or rejection cases."
|
|
@@ -7785,12 +8606,16 @@ class SqliteObjectStorage {
|
|
|
7785
8606
|
const result = new Map;
|
|
7786
8607
|
if (params.items.length === 0)
|
|
7787
8608
|
return result;
|
|
7788
|
-
const
|
|
7789
|
-
|
|
7790
|
-
|
|
7791
|
-
|
|
7792
|
-
|
|
7793
|
-
|
|
8609
|
+
const rows = this.db.query(`
|
|
8610
|
+
SELECT object.*
|
|
8611
|
+
FROM json_each(?) AS requested
|
|
8612
|
+
JOIN objects AS object
|
|
8613
|
+
ON object.project_id = ?
|
|
8614
|
+
AND object.object_type_id = json_extract(requested.value, '$.objectTypeId')
|
|
8615
|
+
AND object.primary_id = json_extract(requested.value, '$.primaryId')
|
|
8616
|
+
`).all(JSON.stringify(params.items), params.projectId);
|
|
8617
|
+
for (const row of rows) {
|
|
8618
|
+
result.set(objectBatchKey(row.object_type_id, row.primary_id), this.rowToObject(row));
|
|
7794
8619
|
}
|
|
7795
8620
|
return result;
|
|
7796
8621
|
}
|
|
@@ -7802,25 +8627,95 @@ class SqliteObjectStorage {
|
|
|
7802
8627
|
for (const item of params.items) {
|
|
7803
8628
|
const rows = stmt.all(params.projectId, item.objectTypeId, item.objectId, item.linkId);
|
|
7804
8629
|
if (rows.length > 0) {
|
|
7805
|
-
result.set(
|
|
8630
|
+
result.set(linkBatchKey(item.objectTypeId, item.objectId, item.linkId), rows.map((r) => this.rowToLink(r)));
|
|
7806
8631
|
}
|
|
7807
8632
|
}
|
|
7808
8633
|
return result;
|
|
7809
8634
|
}
|
|
7810
|
-
async
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
|
|
7818
|
-
|
|
7819
|
-
|
|
7820
|
-
|
|
7821
|
-
|
|
8635
|
+
async queryLinks(params) {
|
|
8636
|
+
assertLinkQueryLimit(params.limit);
|
|
8637
|
+
if (params.objectRefs.length === 0 || params.endpointObjectTypeIds?.length === 0) {
|
|
8638
|
+
return { links: [], hasMore: false };
|
|
8639
|
+
}
|
|
8640
|
+
const sourceJoin = `
|
|
8641
|
+
SELECT link.*
|
|
8642
|
+
FROM links AS link
|
|
8643
|
+
JOIN requested
|
|
8644
|
+
ON requested.object_type_id = link.source_type_id
|
|
8645
|
+
AND requested.object_id = link.source_id
|
|
8646
|
+
WHERE link.project_id = ?
|
|
8647
|
+
`;
|
|
8648
|
+
const targetJoin = `
|
|
8649
|
+
SELECT link.*
|
|
8650
|
+
FROM links AS link
|
|
8651
|
+
JOIN requested
|
|
8652
|
+
ON requested.object_type_id = link.target_type_id
|
|
8653
|
+
AND requested.object_id = link.target_id
|
|
8654
|
+
WHERE link.project_id = ?
|
|
8655
|
+
`;
|
|
8656
|
+
const incidentSql = params.direction === "outgoing" ? sourceJoin : params.direction === "incoming" ? targetJoin : `${sourceJoin} UNION ${targetJoin}`;
|
|
8657
|
+
const args = [JSON.stringify(params.objectRefs), params.projectId];
|
|
8658
|
+
if (params.direction === "both")
|
|
8659
|
+
args.push(params.projectId);
|
|
8660
|
+
const predicates = [];
|
|
8661
|
+
if (params.linkId !== undefined) {
|
|
8662
|
+
predicates.push("link_id = ?");
|
|
8663
|
+
args.push(params.linkId);
|
|
8664
|
+
}
|
|
8665
|
+
if (params.endpointObjectTypeIds !== undefined) {
|
|
8666
|
+
const allowedTypes = JSON.stringify([...new Set(params.endpointObjectTypeIds)]);
|
|
8667
|
+
predicates.push("source_type_id IN (SELECT value FROM json_each(?))", "target_type_id IN (SELECT value FROM json_each(?))");
|
|
8668
|
+
args.push(allowedTypes, allowedTypes);
|
|
7822
8669
|
}
|
|
7823
|
-
|
|
8670
|
+
if (params.after) {
|
|
8671
|
+
predicates.push("(source_type_id, source_id, link_id, target_type_id, target_id) > (?, ?, ?, ?, ?)");
|
|
8672
|
+
args.push(...params.after);
|
|
8673
|
+
}
|
|
8674
|
+
args.push(params.limit + 1);
|
|
8675
|
+
const whereSql = predicates.length > 0 ? `WHERE ${predicates.join(" AND ")}` : "";
|
|
8676
|
+
const rows = this.db.query(`
|
|
8677
|
+
WITH requested AS (
|
|
8678
|
+
SELECT DISTINCT
|
|
8679
|
+
json_extract(value, '$.objectTypeId') AS object_type_id,
|
|
8680
|
+
json_extract(value, '$.primaryId') AS object_id
|
|
8681
|
+
FROM json_each(?)
|
|
8682
|
+
), incident AS (${incidentSql})
|
|
8683
|
+
SELECT *
|
|
8684
|
+
FROM incident
|
|
8685
|
+
${whereSql}
|
|
8686
|
+
ORDER BY source_type_id, source_id, link_id, target_type_id, target_id
|
|
8687
|
+
LIMIT ?
|
|
8688
|
+
`).all(...args);
|
|
8689
|
+
return {
|
|
8690
|
+
links: rows.slice(0, params.limit).map((row) => this.rowToLink(row)),
|
|
8691
|
+
hasMore: rows.length > params.limit
|
|
8692
|
+
};
|
|
8693
|
+
}
|
|
8694
|
+
async listIncidentLinksBatch(params) {
|
|
8695
|
+
if (params.items.length === 0)
|
|
8696
|
+
return [];
|
|
8697
|
+
const rows = this.db.query(`
|
|
8698
|
+
WITH requested AS (
|
|
8699
|
+
SELECT
|
|
8700
|
+
json_extract(value, '$.objectTypeId') AS object_type_id,
|
|
8701
|
+
json_extract(value, '$.objectId') AS object_id
|
|
8702
|
+
FROM json_each(?)
|
|
8703
|
+
)
|
|
8704
|
+
SELECT link.*
|
|
8705
|
+
FROM links AS link
|
|
8706
|
+
JOIN requested
|
|
8707
|
+
ON requested.object_type_id = link.source_type_id
|
|
8708
|
+
AND requested.object_id = link.source_id
|
|
8709
|
+
WHERE link.project_id = ?
|
|
8710
|
+
UNION
|
|
8711
|
+
SELECT link.*
|
|
8712
|
+
FROM links AS link
|
|
8713
|
+
JOIN requested
|
|
8714
|
+
ON requested.object_type_id = link.target_type_id
|
|
8715
|
+
AND requested.object_id = link.target_id
|
|
8716
|
+
WHERE link.project_id = ?
|
|
8717
|
+
`).all(JSON.stringify(params.items), params.projectId, params.projectId);
|
|
8718
|
+
return rows.map((row) => this.rowToLink(row));
|
|
7824
8719
|
}
|
|
7825
8720
|
async listByPrimaryIdPage(params) {
|
|
7826
8721
|
assertReconciliationPageLimit(params.limit);
|
|
@@ -7932,6 +8827,11 @@ function assertReconciliationPageLimit(limit) {
|
|
|
7932
8827
|
throw new Error("Object reconciliation page limit must be a positive safe integer.");
|
|
7933
8828
|
}
|
|
7934
8829
|
}
|
|
8830
|
+
function assertLinkQueryLimit(limit) {
|
|
8831
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) {
|
|
8832
|
+
throw new Error("Object link query limit must be a positive safe integer.");
|
|
8833
|
+
}
|
|
8834
|
+
}
|
|
7935
8835
|
function readTotal(db, compiled) {
|
|
7936
8836
|
const row = db.query(compiled.totalSql).get(...compiled.totalArgs);
|
|
7937
8837
|
return row.total;
|
|
@@ -8561,13 +9461,13 @@ class SqliteMaterializationStateReader {
|
|
|
8561
9461
|
ORDER BY requested.scope_sort_key, ${linkSortExpression("links")}
|
|
8562
9462
|
`).iterate(requestedJson, this.projectId);
|
|
8563
9463
|
for (const row of rows) {
|
|
8564
|
-
const
|
|
8565
|
-
if (!
|
|
9464
|
+
const accumulator2 = accumulators.get(row.scope_sort_key);
|
|
9465
|
+
if (!accumulator2) {
|
|
8566
9466
|
throw new MaterializationConflictError2("effective-state", `Unexpected link scope '${row.scope_sort_key}' returned by storage.`);
|
|
8567
9467
|
}
|
|
8568
9468
|
const snapshot = effectiveLinkSnapshot(row);
|
|
8569
|
-
appendScopeSnapshot(
|
|
8570
|
-
effective.set(row.scope_sort_key,
|
|
9469
|
+
appendScopeSnapshot(accumulator2, snapshot);
|
|
9470
|
+
effective.set(row.scope_sort_key, accumulator2.effectiveCount === 1 ? snapshot : null);
|
|
8571
9471
|
}
|
|
8572
9472
|
return scopes.map(({ source, linkId }) => {
|
|
8573
9473
|
const key = linkScopeSortKey(source, linkId);
|
|
@@ -8927,6 +9827,7 @@ function storedObjectOverride(row) {
|
|
|
8927
9827
|
return {
|
|
8928
9828
|
ref,
|
|
8929
9829
|
value: parseJson2(row.value),
|
|
9830
|
+
editedAt: parseJson2(row.edited_at),
|
|
8930
9831
|
lastCommitId: row.last_commit_id,
|
|
8931
9832
|
updatedAt: row.updated_at
|
|
8932
9833
|
};
|
|
@@ -9416,8 +10317,9 @@ class SqliteMaterializationWriter {
|
|
|
9416
10317
|
if (item.expectedLastCommitId === null) {
|
|
9417
10318
|
try {
|
|
9418
10319
|
this.db.query(`INSERT INTO ontology_object_overrides (
|
|
9419
|
-
project_id, object_type_id, primary_id, value,
|
|
9420
|
-
|
|
10320
|
+
project_id, object_type_id, primary_id, value, edited_at,
|
|
10321
|
+
last_commit_id, updated_at
|
|
10322
|
+
) VALUES (?, ?, ?, json(?), json(?), ?, ?)`).run(projectId, item.ref.objectTypeId, item.ref.primaryId, canonicalJson(item.value), canonicalJson(item.editedAt), item.lastCommitId, item.updatedAt);
|
|
9421
10323
|
} catch (error) {
|
|
9422
10324
|
if (isSqliteConstraintError(error)) {
|
|
9423
10325
|
throw effectiveConflict("Expected object override changed.");
|
|
@@ -9427,9 +10329,9 @@ class SqliteMaterializationWriter {
|
|
|
9427
10329
|
continue;
|
|
9428
10330
|
}
|
|
9429
10331
|
requireChanges(this.db.query(`UPDATE ontology_object_overrides
|
|
9430
|
-
SET value = json(?), last_commit_id = ?, updated_at = ?
|
|
10332
|
+
SET value = json(?), edited_at = json(?), last_commit_id = ?, updated_at = ?
|
|
9431
10333
|
WHERE project_id = ? AND object_type_id = ? AND primary_id = ?
|
|
9432
|
-
AND last_commit_id = ?`).run(canonicalJson(item.value), item.lastCommitId, item.updatedAt, projectId, item.ref.objectTypeId, item.ref.primaryId, item.expectedLastCommitId).changes, "effective-state", "Expected object override changed.");
|
|
10334
|
+
AND last_commit_id = ?`).run(canonicalJson(item.value), canonicalJson(item.editedAt), item.lastCommitId, item.updatedAt, projectId, item.ref.objectTypeId, item.ref.primaryId, item.expectedLastCommitId).changes, "effective-state", "Expected object override changed.");
|
|
9433
10335
|
}
|
|
9434
10336
|
for (const item of chunk.overrides.objects.deletes) {
|
|
9435
10337
|
requireChanges(this.db.query(`DELETE FROM ontology_object_overrides
|
|
@@ -12984,6 +13886,7 @@ class SqliteStorage {
|
|
|
12984
13886
|
executions;
|
|
12985
13887
|
agents;
|
|
12986
13888
|
aiUsage;
|
|
13889
|
+
aiCosts;
|
|
12987
13890
|
actionRuns;
|
|
12988
13891
|
pipelineRuns;
|
|
12989
13892
|
syncRuns;
|
|
@@ -13030,6 +13933,7 @@ class SqliteStorage {
|
|
|
13030
13933
|
this.executions = createOperationScopedFacade(stores.executions, scope);
|
|
13031
13934
|
this.agents = createAgentOperationScope(stores.agents, scope);
|
|
13032
13935
|
this.aiUsage = createOperationScopedFacade(stores.aiUsage, scope);
|
|
13936
|
+
this.aiCosts = createOperationScopedFacade(stores.aiCosts, scope);
|
|
13033
13937
|
this.actionRuns = createOperationScopedFacade(stores.actionRuns, scope);
|
|
13034
13938
|
this.pipelineRuns = createOperationScopedFacade(stores.pipelineRuns, scope);
|
|
13035
13939
|
this.timeseries = createOperationScopedFacade(readTimeseries, readScope);
|
|
@@ -13149,6 +14053,7 @@ function createSqliteStores(connection, options) {
|
|
|
13149
14053
|
executions,
|
|
13150
14054
|
agents: new SqliteAgentStorage({ connection, executions }),
|
|
13151
14055
|
aiUsage: new SqliteAiUsageStorage({ connection }),
|
|
14056
|
+
aiCosts: new SqliteAiCostStorage(connection),
|
|
13152
14057
|
actionRuns: new SqliteActionRunStorage({ connection, executions }),
|
|
13153
14058
|
pipelineRuns: new SqlitePipelineRunStorage({ connection, executions }),
|
|
13154
14059
|
timeseries: new SqliteTimeseriesStorage({ connection }),
|
|
@@ -13166,4 +14071,4 @@ export {
|
|
|
13166
14071
|
SqliteStorage
|
|
13167
14072
|
};
|
|
13168
14073
|
|
|
13169
|
-
//# debugId=
|
|
14074
|
+
//# debugId=2D24EFD822513C9E64756E2164756E21
|