@opengeni/core 0.12.7 → 0.14.3
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/access/index.d.ts +22 -0
- package/dist/application/new-session-drafts.d.ts +14 -0
- package/dist/application/session-commands.d.ts +107 -0
- package/dist/billing/limits.d.ts +29 -0
- package/dist/dependencies.d.ts +137 -0
- package/dist/domain/capabilities.d.ts +62 -0
- package/dist/domain/environments.d.ts +33 -0
- package/dist/domain/insights.d.ts +11 -0
- package/dist/domain/packs.d.ts +27 -0
- package/dist/domain/resources.d.ts +32 -0
- package/dist/domain/scheduled-tasks.d.ts +72 -0
- package/dist/domain/session-tool-policy.d.ts +31 -0
- package/dist/domain/sessions.d.ts +256 -0
- package/dist/domain/slack-bot.d.ts +19 -0
- package/dist/domain/workspace-members.d.ts +34 -0
- package/dist/index.d.ts +23 -1189
- package/dist/index.js +758 -115
- package/dist/index.js.map +1 -1
- package/dist/managed-auth-type.d.ts +2 -0
- package/dist/rigs/index.d.ts +57 -0
- package/dist/sandbox/fleet.d.ts +197 -0
- package/dist/sandbox/routing.d.ts +55 -0
- package/dist/sandbox-types.d.ts +52 -0
- package/dist/session-authorization.d.ts +36 -0
- package/dist/transcription.d.ts +71 -0
- package/dist/workflow-wake-contract.d.ts +4 -0
- package/package.json +11 -11
- package/src/access/index.ts +73 -2
- package/src/application/new-session-drafts.ts +3 -0
- package/src/application/session-commands.ts +22 -9
- package/src/dependencies.ts +5 -0
- package/src/domain/insights.ts +480 -0
- package/src/domain/session-tool-policy.ts +22 -39
- package/src/domain/sessions.ts +140 -72
- package/src/domain/slack-bot.ts +2 -4
- package/src/index.ts +2 -0
- package/src/sandbox/fleet.ts +96 -33
- package/src/sandbox/routing.ts +29 -7
- package/src/transcription.ts +142 -0
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
AccessGrant,
|
|
8
8
|
SessionAuthorizationOperation,
|
|
9
9
|
SessionAuthorizationPort,
|
|
10
|
+
SessionAuthorizationSurface,
|
|
10
11
|
SessionCommandReceipt,
|
|
11
12
|
SessionControlRequest,
|
|
12
13
|
SessionControlResponse,
|
|
@@ -15,7 +16,7 @@ import type {
|
|
|
15
16
|
WorkspaceInferenceControlRequest,
|
|
16
17
|
WorkspaceInferenceControlResponse,
|
|
17
18
|
} from "@opengeni/contracts";
|
|
18
|
-
import { reasoningEffortForMetadata } from "@opengeni/contracts";
|
|
19
|
+
import { latencyModeForMetadata, reasoningEffortForMetadata } from "@opengeni/contracts";
|
|
19
20
|
import {
|
|
20
21
|
deleteSessionQueueItemInTransaction,
|
|
21
22
|
editQueuedTurnInTransaction,
|
|
@@ -55,6 +56,8 @@ export type HumanSessionCommandContext = {
|
|
|
55
56
|
workspaceId: string;
|
|
56
57
|
sessionId: string;
|
|
57
58
|
subjectId: string;
|
|
59
|
+
/** See AgentSessionCommandContext.authorizationSurface. */
|
|
60
|
+
authorizationSurface?: SessionAuthorizationSurface;
|
|
58
61
|
};
|
|
59
62
|
|
|
60
63
|
export type AgentSessionCommandContext = {
|
|
@@ -65,6 +68,13 @@ export type AgentSessionCommandContext = {
|
|
|
65
68
|
callerTurnId: string;
|
|
66
69
|
callerAttemptId: string;
|
|
67
70
|
callerExecutionGeneration: number;
|
|
71
|
+
/**
|
|
72
|
+
* The trusted adapter surface that owns this command's one authorization
|
|
73
|
+
* decision. Direct core callers omit it and retain the canonical `core`
|
|
74
|
+
* surface; adapters that delegate the complete command set it explicitly so
|
|
75
|
+
* they do not authorize once at the edge and then repeat the host call here.
|
|
76
|
+
*/
|
|
77
|
+
authorizationSurface?: SessionAuthorizationSurface;
|
|
68
78
|
};
|
|
69
79
|
|
|
70
80
|
type SessionAuthorizationCommandDeps = {
|
|
@@ -104,7 +114,7 @@ async function authorizeHumanSessionCommand(
|
|
|
104
114
|
return await requireSessionAuthorization(deps, humanAccessGrant(context), {
|
|
105
115
|
sessionId: context.sessionId,
|
|
106
116
|
operation,
|
|
107
|
-
surface: "core",
|
|
117
|
+
surface: context.authorizationSurface ?? "core",
|
|
108
118
|
});
|
|
109
119
|
}
|
|
110
120
|
|
|
@@ -117,7 +127,7 @@ async function authorizeAgentSessionCommand(
|
|
|
117
127
|
return await requireSessionAuthorization(deps, agentAccessGrant(context), {
|
|
118
128
|
sessionId: targetSessionId,
|
|
119
129
|
operation,
|
|
120
|
-
surface: "core",
|
|
130
|
+
surface: context.authorizationSurface ?? "core",
|
|
121
131
|
});
|
|
122
132
|
}
|
|
123
133
|
|
|
@@ -339,7 +349,12 @@ export async function controlAgentSessionWorkstream(
|
|
|
339
349
|
reason?: string | null;
|
|
340
350
|
},
|
|
341
351
|
) {
|
|
342
|
-
await authorizeAgentSessionCommand(
|
|
352
|
+
const authorization = await authorizeAgentSessionCommand(
|
|
353
|
+
deps,
|
|
354
|
+
context,
|
|
355
|
+
input.targetSessionId,
|
|
356
|
+
"session.control",
|
|
357
|
+
);
|
|
343
358
|
const result = await withWorkspaceRls(deps.db, context.workspaceId, (scoped) =>
|
|
344
359
|
scoped.transaction((tx) =>
|
|
345
360
|
mutateSessionControlInTransaction(tx as unknown as Database, {
|
|
@@ -358,7 +373,7 @@ export async function controlAgentSessionWorkstream(
|
|
|
358
373
|
]);
|
|
359
374
|
await publishWorkspaceControlEvent(deps, context.workspaceId, result.workspaceControlEventId);
|
|
360
375
|
await requestControlWakeDispatch(deps, result.wakeCount);
|
|
361
|
-
return result;
|
|
376
|
+
return { ...result, authorization };
|
|
362
377
|
}
|
|
363
378
|
|
|
364
379
|
function receipt(row: SessionCommandReceiptRow): SessionCommandReceipt {
|
|
@@ -384,10 +399,9 @@ function composerDraft(
|
|
|
384
399
|
revision: row.revision,
|
|
385
400
|
text: row.text,
|
|
386
401
|
resources: row.resources as ComposerDraft["resources"],
|
|
387
|
-
tools: row.tools as ComposerDraft["tools"],
|
|
388
|
-
toolsProvided: row.toolsProvided,
|
|
389
402
|
model: row.model,
|
|
390
403
|
reasoningEffort: row.reasoningEffort as ComposerDraft["reasoningEffort"],
|
|
404
|
+
latencyMode: row.latencyMode as ComposerDraft["latencyMode"],
|
|
391
405
|
sourceTurnId: row.sourceTurnId,
|
|
392
406
|
sourceTurnVersion: row.sourceTurnVersion,
|
|
393
407
|
updatedAt: row.updatedAt.toISOString(),
|
|
@@ -648,10 +662,9 @@ export async function getHumanComposerDraft(
|
|
|
648
662
|
revision: 0,
|
|
649
663
|
text: "",
|
|
650
664
|
resources: [],
|
|
651
|
-
tools: [],
|
|
652
|
-
toolsProvided: false,
|
|
653
665
|
model: session.model,
|
|
654
666
|
reasoningEffort: reasoningEffortForMetadata(session.metadata, "medium"),
|
|
667
|
+
latencyMode: latencyModeForMetadata(session.metadata, "standard"),
|
|
655
668
|
sourceTurnId: null,
|
|
656
669
|
sourceTurnVersion: null,
|
|
657
670
|
updatedAt: null,
|
package/src/dependencies.ts
CHANGED
|
@@ -14,6 +14,7 @@ import type { Observability } from "@opengeni/observability";
|
|
|
14
14
|
import type { createObjectStorage } from "@opengeni/storage";
|
|
15
15
|
import type { ManagedAuth } from "./managed-auth-type";
|
|
16
16
|
import type { ApiSandboxClient, ResumeBoxByIdInput, ResumedSandboxSession } from "./sandbox-types";
|
|
17
|
+
import type { TranscriptionService } from "./transcription";
|
|
17
18
|
|
|
18
19
|
export type SessionWorkflowClient = {
|
|
19
20
|
signalUserMessage: (input: {
|
|
@@ -110,6 +111,10 @@ export type AppDependencies = {
|
|
|
110
111
|
codexFetch?: typeof fetch;
|
|
111
112
|
/** Injectable Slack Web API transport for deterministic bot-connection tests. */
|
|
112
113
|
slackFetch?: typeof fetch;
|
|
114
|
+
/** Injectable Google OAuth/Drive transport for deterministic connector tests. */
|
|
115
|
+
googleDriveFetch?: typeof fetch;
|
|
116
|
+
/** Optional host-owned voice-input transcription service. */
|
|
117
|
+
transcription?: TranscriptionService | null;
|
|
113
118
|
// The API process's OWN agent-loop-free sandbox client (constructed from
|
|
114
119
|
// settings via @opengeni/runtime/sandbox). Undefined when sandboxBackend=none.
|
|
115
120
|
// This is the foundation of the API-direct control plane: the API resumes
|
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
import { configuredStaticUsageLimits, type Settings } from "@opengeni/config";
|
|
2
|
+
import {
|
|
3
|
+
WorkspaceInsightsSnapshot,
|
|
4
|
+
type InsightsRange,
|
|
5
|
+
type WorkspaceInsightsResponse,
|
|
6
|
+
} from "@opengeni/contracts";
|
|
7
|
+
import {
|
|
8
|
+
aggregateModelCallFacts,
|
|
9
|
+
aggregateModelCallFactsByDay,
|
|
10
|
+
aggregateRootSessionDrivers,
|
|
11
|
+
aggregateScheduleFacts,
|
|
12
|
+
aggregateSessionDepth,
|
|
13
|
+
aggregateWarmSecondsByGroup,
|
|
14
|
+
countOnlineMachines,
|
|
15
|
+
countScheduledTaskFires,
|
|
16
|
+
countSessionsAttachedToGroups,
|
|
17
|
+
enumerateUtcDays,
|
|
18
|
+
listFloorSessions,
|
|
19
|
+
listLiveWarmLeases,
|
|
20
|
+
listModelCallFacets,
|
|
21
|
+
listScheduledTasks,
|
|
22
|
+
requireWorkspace,
|
|
23
|
+
sumUsageQuantity,
|
|
24
|
+
sumUsageQuantityByDay,
|
|
25
|
+
sumUsageQuantityInRange,
|
|
26
|
+
type Database,
|
|
27
|
+
} from "@opengeni/db";
|
|
28
|
+
|
|
29
|
+
const MACHINE_HEARTBEAT_FRESH_MS = 120_000;
|
|
30
|
+
|
|
31
|
+
export type GetWorkspaceInsightsInput = {
|
|
32
|
+
workspaceId: string;
|
|
33
|
+
range: InsightsRange;
|
|
34
|
+
provider?: string | null;
|
|
35
|
+
model?: string | null;
|
|
36
|
+
now?: Date;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
function microsToUsd(micros: number): number {
|
|
40
|
+
return Math.round((micros / 1_000_000) * 100) / 100;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function cacheHitPct(cached: number, input: number): number {
|
|
44
|
+
if (input <= 0) return 0;
|
|
45
|
+
return Math.min(100, Math.max(0, Math.round((cached / input) * 100)));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function startOfUtcDay(date: Date): Date {
|
|
49
|
+
return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function startOfUtcMonth(date: Date): Date {
|
|
53
|
+
return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function startOfUtcYear(date: Date): Date {
|
|
57
|
+
return new Date(Date.UTC(date.getUTCFullYear(), 0, 1));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function resolveRangeWindow(
|
|
61
|
+
range: InsightsRange,
|
|
62
|
+
now: Date,
|
|
63
|
+
): {
|
|
64
|
+
since: Date;
|
|
65
|
+
until: Date;
|
|
66
|
+
priorSince: Date;
|
|
67
|
+
priorUntil: Date;
|
|
68
|
+
rangeLabel: string;
|
|
69
|
+
priorLabel: string;
|
|
70
|
+
seriesLabel: string;
|
|
71
|
+
cacheSeriesLabel: string;
|
|
72
|
+
} {
|
|
73
|
+
const until = now;
|
|
74
|
+
let since: Date;
|
|
75
|
+
let rangeLabel: string;
|
|
76
|
+
let priorLabel: string;
|
|
77
|
+
let seriesLabel: string;
|
|
78
|
+
let cacheSeriesLabel: string;
|
|
79
|
+
switch (range) {
|
|
80
|
+
case "today":
|
|
81
|
+
since = startOfUtcDay(now);
|
|
82
|
+
rangeLabel = "Today (UTC)";
|
|
83
|
+
priorLabel = "Prior equal window";
|
|
84
|
+
seriesLabel = "Credit $ (UTC day)";
|
|
85
|
+
cacheSeriesLabel = "Cache hit %";
|
|
86
|
+
break;
|
|
87
|
+
case "week":
|
|
88
|
+
since = new Date(startOfUtcDay(now).getTime() - 6 * 24 * 60 * 60 * 1000);
|
|
89
|
+
rangeLabel = "Last 7 days (UTC)";
|
|
90
|
+
priorLabel = "Prior 7 days";
|
|
91
|
+
seriesLabel = "Credit $ / day";
|
|
92
|
+
cacheSeriesLabel = "Cache hit % / day";
|
|
93
|
+
break;
|
|
94
|
+
case "month":
|
|
95
|
+
since = startOfUtcMonth(now);
|
|
96
|
+
rangeLabel = "This month (UTC)";
|
|
97
|
+
priorLabel = "Prior equal window";
|
|
98
|
+
seriesLabel = "Credit $ / day";
|
|
99
|
+
cacheSeriesLabel = "Cache hit % / day";
|
|
100
|
+
break;
|
|
101
|
+
case "ytd":
|
|
102
|
+
since = startOfUtcYear(now);
|
|
103
|
+
rangeLabel = "Year to date (UTC)";
|
|
104
|
+
priorLabel = "Prior equal window";
|
|
105
|
+
seriesLabel = "Credit $ / day";
|
|
106
|
+
cacheSeriesLabel = "Cache hit % / day";
|
|
107
|
+
break;
|
|
108
|
+
default: {
|
|
109
|
+
const _exhaustive: never = range;
|
|
110
|
+
throw new Error(`Unknown insights range: ${_exhaustive}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const durationMs = until.getTime() - since.getTime();
|
|
114
|
+
const priorUntil = since;
|
|
115
|
+
const priorSince = new Date(priorUntil.getTime() - Math.max(durationMs, 1));
|
|
116
|
+
return {
|
|
117
|
+
since,
|
|
118
|
+
until,
|
|
119
|
+
priorSince,
|
|
120
|
+
priorUntil,
|
|
121
|
+
rangeLabel,
|
|
122
|
+
priorLabel,
|
|
123
|
+
seriesLabel,
|
|
124
|
+
cacheSeriesLabel,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function ageLabel(updatedAt: Date, now: Date): string {
|
|
129
|
+
const ms = Math.max(0, now.getTime() - updatedAt.getTime());
|
|
130
|
+
const minutes = Math.floor(ms / 60_000);
|
|
131
|
+
if (minutes < 1) return "just now";
|
|
132
|
+
if (minutes < 60) return `${minutes}m`;
|
|
133
|
+
const hours = Math.floor(minutes / 60);
|
|
134
|
+
if (hours < 48) return `${hours}h`;
|
|
135
|
+
return `${Math.floor(hours / 24)}d`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function floorState(input: {
|
|
139
|
+
status: string;
|
|
140
|
+
directControlState: string;
|
|
141
|
+
}): "running" | "paused" | "failed" | "idle" | "compacting" | "waiting" {
|
|
142
|
+
if (input.directControlState === "paused") return "paused";
|
|
143
|
+
switch (input.status) {
|
|
144
|
+
case "running":
|
|
145
|
+
return "running";
|
|
146
|
+
case "failed":
|
|
147
|
+
return "failed";
|
|
148
|
+
case "requires_action":
|
|
149
|
+
case "waiting":
|
|
150
|
+
return "waiting";
|
|
151
|
+
case "compacting":
|
|
152
|
+
return "compacting";
|
|
153
|
+
case "completed":
|
|
154
|
+
case "idle":
|
|
155
|
+
case "queued":
|
|
156
|
+
return "idle";
|
|
157
|
+
default:
|
|
158
|
+
return "idle";
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function billingPathOf(value: string): "opengeni_credits" | "external" {
|
|
163
|
+
return value === "external" ? "external" : "opengeni_credits";
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export async function getWorkspaceInsights(
|
|
167
|
+
db: Database,
|
|
168
|
+
settings: Settings,
|
|
169
|
+
input: GetWorkspaceInsightsInput,
|
|
170
|
+
): Promise<WorkspaceInsightsResponse> {
|
|
171
|
+
await requireWorkspace(db, input.workspaceId);
|
|
172
|
+
const now = input.now ?? new Date();
|
|
173
|
+
const window = resolveRangeWindow(input.range, now);
|
|
174
|
+
const provider = input.provider?.trim() || null;
|
|
175
|
+
const model = input.model?.trim() || null;
|
|
176
|
+
const modelFilterActive = Boolean(provider || model);
|
|
177
|
+
const filter = { provider, model };
|
|
178
|
+
|
|
179
|
+
const [
|
|
180
|
+
workspaceCreditMicros,
|
|
181
|
+
priorWorkspaceCreditMicros,
|
|
182
|
+
warmSeconds,
|
|
183
|
+
priorWarmSeconds,
|
|
184
|
+
modelRows,
|
|
185
|
+
priorModelRows,
|
|
186
|
+
factDays,
|
|
187
|
+
warmDays,
|
|
188
|
+
costDays,
|
|
189
|
+
warmGroups,
|
|
190
|
+
liveWarm,
|
|
191
|
+
rootDrivers,
|
|
192
|
+
scheduleFacts,
|
|
193
|
+
tasks,
|
|
194
|
+
depth,
|
|
195
|
+
floorRows,
|
|
196
|
+
machinesOnline,
|
|
197
|
+
billableTokensUsed,
|
|
198
|
+
agentRunsUsed,
|
|
199
|
+
facets,
|
|
200
|
+
] = await Promise.all([
|
|
201
|
+
sumUsageQuantityInRange(db, {
|
|
202
|
+
workspaceId: input.workspaceId,
|
|
203
|
+
eventType: "model.cost",
|
|
204
|
+
since: window.since,
|
|
205
|
+
until: window.until,
|
|
206
|
+
}),
|
|
207
|
+
sumUsageQuantityInRange(db, {
|
|
208
|
+
workspaceId: input.workspaceId,
|
|
209
|
+
eventType: "model.cost",
|
|
210
|
+
since: window.priorSince,
|
|
211
|
+
until: window.priorUntil,
|
|
212
|
+
}),
|
|
213
|
+
sumUsageQuantityInRange(db, {
|
|
214
|
+
workspaceId: input.workspaceId,
|
|
215
|
+
eventType: "sandbox.warm_seconds",
|
|
216
|
+
since: window.since,
|
|
217
|
+
until: window.until,
|
|
218
|
+
}),
|
|
219
|
+
sumUsageQuantityInRange(db, {
|
|
220
|
+
workspaceId: input.workspaceId,
|
|
221
|
+
eventType: "sandbox.warm_seconds",
|
|
222
|
+
since: window.priorSince,
|
|
223
|
+
until: window.priorUntil,
|
|
224
|
+
}),
|
|
225
|
+
aggregateModelCallFacts(db, {
|
|
226
|
+
workspaceId: input.workspaceId,
|
|
227
|
+
since: window.since,
|
|
228
|
+
until: window.until,
|
|
229
|
+
...filter,
|
|
230
|
+
}),
|
|
231
|
+
aggregateModelCallFacts(db, {
|
|
232
|
+
workspaceId: input.workspaceId,
|
|
233
|
+
since: window.priorSince,
|
|
234
|
+
until: window.priorUntil,
|
|
235
|
+
...filter,
|
|
236
|
+
}),
|
|
237
|
+
aggregateModelCallFactsByDay(db, {
|
|
238
|
+
workspaceId: input.workspaceId,
|
|
239
|
+
since: window.since,
|
|
240
|
+
until: window.until,
|
|
241
|
+
...filter,
|
|
242
|
+
}),
|
|
243
|
+
sumUsageQuantityByDay(db, {
|
|
244
|
+
workspaceId: input.workspaceId,
|
|
245
|
+
eventType: "sandbox.warm_seconds",
|
|
246
|
+
since: window.since,
|
|
247
|
+
until: window.until,
|
|
248
|
+
}),
|
|
249
|
+
sumUsageQuantityByDay(db, {
|
|
250
|
+
workspaceId: input.workspaceId,
|
|
251
|
+
eventType: "model.cost",
|
|
252
|
+
since: window.since,
|
|
253
|
+
until: window.until,
|
|
254
|
+
}),
|
|
255
|
+
aggregateWarmSecondsByGroup(db, {
|
|
256
|
+
workspaceId: input.workspaceId,
|
|
257
|
+
since: window.since,
|
|
258
|
+
until: window.until,
|
|
259
|
+
limit: 24,
|
|
260
|
+
}),
|
|
261
|
+
listLiveWarmLeases(db, input.workspaceId),
|
|
262
|
+
aggregateRootSessionDrivers(db, {
|
|
263
|
+
workspaceId: input.workspaceId,
|
|
264
|
+
since: window.since,
|
|
265
|
+
until: window.until,
|
|
266
|
+
...filter,
|
|
267
|
+
limit: 8,
|
|
268
|
+
}),
|
|
269
|
+
aggregateScheduleFacts(db, {
|
|
270
|
+
workspaceId: input.workspaceId,
|
|
271
|
+
since: window.since,
|
|
272
|
+
until: window.until,
|
|
273
|
+
...filter,
|
|
274
|
+
}),
|
|
275
|
+
listScheduledTasks(db, input.workspaceId, 100),
|
|
276
|
+
aggregateSessionDepth(db, input.workspaceId),
|
|
277
|
+
listFloorSessions(db, input.workspaceId, 24),
|
|
278
|
+
settings.sandboxSelfhostedEnabled
|
|
279
|
+
? countOnlineMachines(db, input.workspaceId, MACHINE_HEARTBEAT_FRESH_MS)
|
|
280
|
+
: Promise.resolve(0),
|
|
281
|
+
sumUsageQuantity(db, {
|
|
282
|
+
workspaceId: input.workspaceId,
|
|
283
|
+
eventType: "model.tokens",
|
|
284
|
+
since: startOfUtcMonth(now),
|
|
285
|
+
}),
|
|
286
|
+
sumUsageQuantity(db, {
|
|
287
|
+
workspaceId: input.workspaceId,
|
|
288
|
+
eventType: "agent_run.created",
|
|
289
|
+
since: startOfUtcMonth(now),
|
|
290
|
+
}),
|
|
291
|
+
listModelCallFacets(db, {
|
|
292
|
+
workspaceId: input.workspaceId,
|
|
293
|
+
since: window.since,
|
|
294
|
+
until: window.until,
|
|
295
|
+
}),
|
|
296
|
+
]);
|
|
297
|
+
|
|
298
|
+
// Exact prior costs for the current top drivers — never a separate top-N page that
|
|
299
|
+
// drops roots and invents +$full as "new" spend.
|
|
300
|
+
const priorRootDrivers = await aggregateRootSessionDrivers(db, {
|
|
301
|
+
workspaceId: input.workspaceId,
|
|
302
|
+
since: window.priorSince,
|
|
303
|
+
until: window.priorUntil,
|
|
304
|
+
...filter,
|
|
305
|
+
rootSessionIds: rootDrivers.map((row) => row.rootSessionId),
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
const attached = await countSessionsAttachedToGroups(
|
|
309
|
+
db,
|
|
310
|
+
input.workspaceId,
|
|
311
|
+
warmGroups.map((group) => group.groupId),
|
|
312
|
+
);
|
|
313
|
+
const backendByGroup = new Map(liveWarm.map((lease) => [lease.groupId, lease.backend]));
|
|
314
|
+
const warmSecondsByGroup = new Map(warmGroups.map((group) => [group.groupId, group.warmSeconds]));
|
|
315
|
+
|
|
316
|
+
const models = modelRows
|
|
317
|
+
.map((row) => ({
|
|
318
|
+
id: `${row.provider}:${row.model}:${row.billingPath}`,
|
|
319
|
+
model: row.model,
|
|
320
|
+
provider: row.provider,
|
|
321
|
+
billing: billingPathOf(row.billingPath),
|
|
322
|
+
calls: row.calls,
|
|
323
|
+
inputTokens: row.inputTokens,
|
|
324
|
+
outputTokens: row.outputTokens,
|
|
325
|
+
cachedTokens: row.cachedTokens,
|
|
326
|
+
cacheWriteTokens: row.cacheWriteTokens,
|
|
327
|
+
reasoningTokens: row.reasoningTokens,
|
|
328
|
+
creditUsd: microsToUsd(row.pricedCostMicros),
|
|
329
|
+
}))
|
|
330
|
+
.sort((a, b) => b.inputTokens - a.inputTokens);
|
|
331
|
+
|
|
332
|
+
const creditMicros = modelRows.reduce((sum, row) => sum + row.pricedCostMicros, 0);
|
|
333
|
+
const priorCreditMicros = priorModelRows.reduce((sum, row) => sum + row.pricedCostMicros, 0);
|
|
334
|
+
const priorInputTokens = priorModelRows.reduce((sum, row) => sum + row.inputTokens, 0);
|
|
335
|
+
const priorCachedTokens = priorModelRows.reduce((sum, row) => sum + row.cachedTokens, 0);
|
|
336
|
+
const priorCalls = priorModelRows.reduce((sum, row) => sum + row.calls, 0);
|
|
337
|
+
|
|
338
|
+
const days = enumerateUtcDays(window.since, window.until);
|
|
339
|
+
const series = days.map((day) => {
|
|
340
|
+
const facts = factDays.get(day) ?? {
|
|
341
|
+
costMicros: 0,
|
|
342
|
+
inputTokens: 0,
|
|
343
|
+
cachedTokens: 0,
|
|
344
|
+
calls: 0,
|
|
345
|
+
};
|
|
346
|
+
const modelCostMicros = modelFilterActive
|
|
347
|
+
? facts.costMicros
|
|
348
|
+
: (costDays.get(day) ?? facts.costMicros);
|
|
349
|
+
return {
|
|
350
|
+
label: day.slice(5),
|
|
351
|
+
modelCostUsd: microsToUsd(modelCostMicros),
|
|
352
|
+
warmSeconds: warmDays.get(day) ?? 0,
|
|
353
|
+
inputTokens: facts.inputTokens,
|
|
354
|
+
cachedTokens: facts.cachedTokens,
|
|
355
|
+
cacheHitPct: cacheHitPct(facts.cachedTokens, facts.inputTokens),
|
|
356
|
+
calls: facts.calls,
|
|
357
|
+
};
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
const priorDriverByRoot = new Map(
|
|
361
|
+
priorRootDrivers.map((row) => [row.rootSessionId, row.pricedCostMicros]),
|
|
362
|
+
);
|
|
363
|
+
const creditUsdForPct = Math.max(microsToUsd(creditMicros), 0.01);
|
|
364
|
+
const drivers = rootDrivers.map((row) => {
|
|
365
|
+
const creditUsd = microsToUsd(row.pricedCostMicros);
|
|
366
|
+
const priorUsd = microsToUsd(priorDriverByRoot.get(row.rootSessionId) ?? 0);
|
|
367
|
+
return {
|
|
368
|
+
id: `root:${row.rootSessionId}`,
|
|
369
|
+
groupBy: "root_session" as const,
|
|
370
|
+
label: row.title?.trim() || row.rootSessionId.slice(0, 8),
|
|
371
|
+
creditUsd,
|
|
372
|
+
tokens: row.inputTokens,
|
|
373
|
+
cacheHitPct: cacheHitPct(row.cachedTokens, row.inputTokens),
|
|
374
|
+
pctOfCreditUsd: Math.min(100, Math.round((creditUsd / creditUsdForPct) * 100)),
|
|
375
|
+
deltaUsdVsPrior: Math.round((creditUsd - priorUsd) * 100) / 100,
|
|
376
|
+
};
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
const fireCounts = await countScheduledTaskFires(db, {
|
|
380
|
+
workspaceId: input.workspaceId,
|
|
381
|
+
since: window.since,
|
|
382
|
+
until: window.until,
|
|
383
|
+
taskIds: tasks.map((task) => task.id),
|
|
384
|
+
});
|
|
385
|
+
const scheduleFactById = new Map(scheduleFacts.map((row) => [row.scheduledTaskId, row] as const));
|
|
386
|
+
const schedules = tasks.map((task) => {
|
|
387
|
+
const fact = scheduleFactById.get(task.id);
|
|
388
|
+
return {
|
|
389
|
+
id: task.id,
|
|
390
|
+
name: task.name,
|
|
391
|
+
fires: fireCounts.get(task.id) ?? 0,
|
|
392
|
+
creditUsd: fact ? microsToUsd(fact.pricedCostMicros) : null,
|
|
393
|
+
tokens: fact ? fact.inputTokens : null,
|
|
394
|
+
cacheHitPct: fact ? cacheHitPct(fact.cachedTokens, fact.inputTokens) : null,
|
|
395
|
+
billing: fact ? billingPathOf(fact.billingPath) : null,
|
|
396
|
+
};
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
const limits = configuredStaticUsageLimits(settings);
|
|
400
|
+
// Floor sessions only carry product model, not provider. Filter when an exact
|
|
401
|
+
// model is selected; otherwise leave the list workspace-wide (captioned in UI).
|
|
402
|
+
const floor = floorRows
|
|
403
|
+
.filter((row) => {
|
|
404
|
+
if (!model) return true;
|
|
405
|
+
return row.model === model;
|
|
406
|
+
})
|
|
407
|
+
.map((row) => ({
|
|
408
|
+
id: row.id,
|
|
409
|
+
title: row.title?.trim() || "Untitled session",
|
|
410
|
+
state: floorState(row),
|
|
411
|
+
depth: row.nestedAgentDepth,
|
|
412
|
+
model: row.model,
|
|
413
|
+
provider: null,
|
|
414
|
+
ageLabel: ageLabel(row.updatedAt, now),
|
|
415
|
+
cacheHitPct: null,
|
|
416
|
+
route: row.sandboxBackend,
|
|
417
|
+
}));
|
|
418
|
+
|
|
419
|
+
const snapshot = WorkspaceInsightsSnapshot.parse({
|
|
420
|
+
range: input.range,
|
|
421
|
+
rangeLabel: window.rangeLabel,
|
|
422
|
+
priorLabel: window.priorLabel,
|
|
423
|
+
seriesLabel: window.seriesLabel,
|
|
424
|
+
cacheSeriesLabel: window.cacheSeriesLabel,
|
|
425
|
+
timezone: "UTC",
|
|
426
|
+
models,
|
|
427
|
+
facets,
|
|
428
|
+
series,
|
|
429
|
+
depth: depth.buckets.map((bucket) => ({
|
|
430
|
+
depth: bucket.depth,
|
|
431
|
+
sessions: bucket.sessions,
|
|
432
|
+
})),
|
|
433
|
+
drivers,
|
|
434
|
+
schedules,
|
|
435
|
+
warmSeconds,
|
|
436
|
+
priorWarmSeconds,
|
|
437
|
+
warmGroups: warmGroups.map((group) => ({
|
|
438
|
+
id: group.groupId,
|
|
439
|
+
groupId: group.groupId,
|
|
440
|
+
label: group.groupId.slice(0, 8),
|
|
441
|
+
backend: backendByGroup.get(group.groupId) ?? null,
|
|
442
|
+
warmSeconds: group.warmSeconds,
|
|
443
|
+
sessionsAttached: attached.get(group.groupId) ?? 0,
|
|
444
|
+
})),
|
|
445
|
+
liveWarm: liveWarm.map((lease) => ({
|
|
446
|
+
id: lease.id,
|
|
447
|
+
groupId: lease.groupId,
|
|
448
|
+
backend: lease.backend,
|
|
449
|
+
turnHolders: lease.turnHolders,
|
|
450
|
+
viewerHolders: lease.viewerHolders,
|
|
451
|
+
warmForLabel: lease.turnHolders > 0 ? "in use" : "idle warm",
|
|
452
|
+
warmSeconds: warmSecondsByGroup.get(lease.groupId) ?? 0,
|
|
453
|
+
})),
|
|
454
|
+
floor,
|
|
455
|
+
selfhostedEnabled: settings.sandboxSelfhostedEnabled,
|
|
456
|
+
machinesOnline,
|
|
457
|
+
workspaceCreditUsd: microsToUsd(workspaceCreditMicros),
|
|
458
|
+
priorWorkspaceCreditUsd: microsToUsd(priorWorkspaceCreditMicros),
|
|
459
|
+
creditUsd: microsToUsd(creditMicros),
|
|
460
|
+
priorCreditUsd: microsToUsd(priorCreditMicros),
|
|
461
|
+
priorInputTokens,
|
|
462
|
+
priorCacheHitPct: cacheHitPct(priorCachedTokens, priorInputTokens),
|
|
463
|
+
priorCalls,
|
|
464
|
+
goalsActive: depth.goalsActive,
|
|
465
|
+
goalsCompleted: depth.goalsCompleted,
|
|
466
|
+
sessionsTouched: depth.sessionsTouched,
|
|
467
|
+
rootSessions: depth.rootSessions,
|
|
468
|
+
deepestDepth: depth.deepestDepth,
|
|
469
|
+
deepestSessionTitle: depth.deepestSessionTitle || "",
|
|
470
|
+
avgDepth: Math.round(depth.avgDepth * 10) / 10,
|
|
471
|
+
warmIdleNow: liveWarm.filter((lease) => lease.turnHolders === 0).length,
|
|
472
|
+
billableTokensUsed,
|
|
473
|
+
billableTokenCap: limits.maxMonthlyTokensPerWorkspace ?? null,
|
|
474
|
+
agentRunsUsed,
|
|
475
|
+
agentRunCap: limits.maxMonthlyAgentRunsPerWorkspace ?? null,
|
|
476
|
+
modelFilterActive,
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
return { snapshot };
|
|
480
|
+
}
|