@opengeni/worker-bundle 2.2.0 → 2.2.1-canary.36927100590001
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/activities/agent-run-admission.d.ts +3 -1
- package/dist/activities/agent-turn/admission.d.ts +24 -3
- package/dist/activities/agent-turn/agent-build.d.ts +1 -0
- package/dist/activities/agent-turn/claude-usage-observer.d.ts +13 -1
- package/dist/activities/agent-turn/compaction-prep.d.ts +20 -0
- package/dist/activities/agent-turn/credential-restriction.d.ts +3 -0
- package/dist/activities/agent-turn/final-reply.d.ts +18 -0
- package/dist/activities/agent-turn/governance-model.d.ts +6 -0
- package/dist/activities/agent-turn/model-call-admission.d.ts +15 -0
- package/dist/activities/agent-turn/run-credentials.d.ts +4 -0
- package/dist/activities/agent-turn/sandbox-establish.d.ts +1 -0
- package/dist/activities/agent-turn/session-title.d.ts +3 -1
- package/dist/activities/agent-turn/stream-attempt.d.ts +1 -0
- package/dist/activities/agent-turn/turn-context.d.ts +1 -0
- package/dist/activities/automations.d.ts +3 -1
- package/dist/activities/browser-deadline-checkpoint.d.ts +29 -0
- package/dist/activities/goals.d.ts +8 -0
- package/dist/activities/run-credentials.d.ts +4 -2
- package/dist/activities/scheduled-tasks.d.ts +4 -35
- package/dist/activities/types.d.ts +4 -1
- package/dist/activities/workspace-capture.d.ts +2 -0
- package/dist/{activities-control-MHNA3CYV.js → activities-control-4F2A734B.js} +502 -133
- package/dist/activities-control-4F2A734B.js.map +1 -0
- package/dist/activities-control.d.ts +30 -0
- package/dist/{activities-turn-FQ5OO2WW.js → activities-turn-TESOIOEX.js} +696 -251
- package/dist/activities-turn-TESOIOEX.js.map +1 -0
- package/dist/activities.d.ts +30 -0
- package/dist/browser-deadline-checkpoint-contract.d.ts +2 -0
- package/dist/{chunk-RZAAGRFQ.js → chunk-4PJTUBWQ.js} +220 -88
- package/dist/chunk-4PJTUBWQ.js.map +1 -0
- package/dist/{chunk-IJZU752L.js → chunk-YG27NEMX.js} +34 -1
- package/dist/chunk-YG27NEMX.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +46 -24
- package/dist/index.js.map +1 -1
- package/dist/observability-metrics.d.ts +16 -1
- package/dist/sandbox-resume.d.ts +4 -1
- package/dist/workflow-bundle.js +89 -21
- package/dist/workflows/browser-deadline-checkpoint.d.ts +9 -0
- package/dist/workflows/scheduled-tasks.d.ts +4 -0
- package/dist/workflows.d.ts +1 -0
- package/package.json +21 -20
- package/src/activities/agent-run-admission.ts +17 -1
- package/src/activities/agent-turn/admission.ts +64 -1
- package/src/activities/agent-turn/agent-build.ts +24 -3
- package/src/activities/agent-turn/claim.ts +69 -15
- package/src/activities/agent-turn/claude-usage-observer.ts +33 -1
- package/src/activities/agent-turn/compaction-prep.ts +67 -16
- package/src/activities/agent-turn/credential-restriction.ts +14 -0
- package/src/activities/agent-turn/errors.ts +55 -0
- package/src/activities/agent-turn/failure-settlement.ts +26 -6
- package/src/activities/agent-turn/final-reply.ts +46 -0
- package/src/activities/agent-turn/governance-model.ts +12 -0
- package/src/activities/agent-turn/model-call-admission.ts +94 -0
- package/src/activities/agent-turn/run-credentials.ts +20 -3
- package/src/activities/agent-turn/run.ts +44 -5
- package/src/activities/agent-turn/sandbox-establish.ts +9 -5
- package/src/activities/agent-turn/session-title.ts +8 -2
- package/src/activities/agent-turn/stream-attempt.ts +144 -38
- package/src/activities/agent-turn/tool-environment.ts +55 -39
- package/src/activities/agent-turn/turn-context.ts +1 -0
- package/src/activities/automations.ts +78 -4
- package/src/activities/browser-deadline-checkpoint.ts +279 -0
- package/src/activities/capabilities.ts +52 -14
- package/src/activities/goals.ts +95 -98
- package/src/activities/knowledge-indexing.ts +29 -0
- package/src/activities/open-suffix-resume.ts +2 -1
- package/src/activities/run-credentials.ts +22 -16
- package/src/activities/sandbox-lease.ts +22 -0
- package/src/activities/scheduled-tasks.ts +172 -38
- package/src/activities/site-auth-maintenance.ts +1 -0
- package/src/activities/types.ts +4 -0
- package/src/activities/workspace-capture.ts +13 -2
- package/src/activities/workspace-credential-provider.ts +12 -0
- package/src/activities-control.ts +2 -0
- package/src/activities.ts +3 -0
- package/src/browser-deadline-checkpoint-contract.ts +5 -0
- package/src/index.ts +53 -25
- package/src/observability-metrics.ts +55 -1
- package/src/sandbox-resume.ts +23 -8
- package/src/sandbox-routing.ts +4 -2
- package/src/workflows/browser-deadline-checkpoint.ts +50 -0
- package/src/workflows/scheduled-tasks.ts +26 -13
- package/src/workflows.ts +4 -0
- package/dist/activities-control-MHNA3CYV.js.map +0 -1
- package/dist/activities-turn-FQ5OO2WW.js.map +0 -1
- package/dist/chunk-IJZU752L.js.map +0 -1
- package/dist/chunk-RZAAGRFQ.js.map +0 -1
package/src/activities/goals.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
allowedFirstPartyMcpToolsForSession,
|
|
3
3
|
configuredStaticUsageLimits,
|
|
4
|
+
isModelAvailableForNewSelection,
|
|
4
5
|
policyProviderIdForModel,
|
|
5
6
|
resolveModelProvider,
|
|
6
7
|
resolveTurnExecutionPolicyV1,
|
|
@@ -13,20 +14,18 @@ import {
|
|
|
13
14
|
import {
|
|
14
15
|
evaluateWorkspaceModelPolicy,
|
|
15
16
|
mergeToolRefs,
|
|
17
|
+
readTurnExecutionPolicyV1,
|
|
16
18
|
type SessionGoal,
|
|
17
19
|
type ToolRef,
|
|
18
20
|
} from "@opengeni/contracts";
|
|
19
21
|
import { isCodexBilledModel } from "@opengeni/codex";
|
|
20
22
|
import {
|
|
21
23
|
enqueueSessionWorkflowWakeIfRunnable,
|
|
22
|
-
getBillingBalance,
|
|
23
24
|
getWorkspaceModelPolicy,
|
|
24
25
|
getSessionGoal,
|
|
25
|
-
|
|
26
|
+
getSessionTurn,
|
|
26
27
|
materializeGoalContinuation,
|
|
27
28
|
requireSession,
|
|
28
|
-
sumUsageQuantity,
|
|
29
|
-
type Database,
|
|
30
29
|
} from "@opengeni/db";
|
|
31
30
|
import type {
|
|
32
31
|
ControlActivityServices,
|
|
@@ -38,6 +37,8 @@ import {
|
|
|
38
37
|
resolveCatalogSettings,
|
|
39
38
|
resolveWorkspaceCatalogSettings,
|
|
40
39
|
} from "@opengeni/core";
|
|
40
|
+
import { agentRunAdmissionDenial } from "./agent-run-admission";
|
|
41
|
+
import { turnCredentialRestriction } from "./agent-turn/credential-restriction";
|
|
41
42
|
|
|
42
43
|
export function createGoalActivities(services: () => Promise<ControlActivityServices>) {
|
|
43
44
|
async function enqueueGoalRetryWake(input: MaybeContinueGoalInput): Promise<void> {
|
|
@@ -65,7 +66,6 @@ export function createGoalActivities(services: () => Promise<ControlActivityServ
|
|
|
65
66
|
if (!existingGoal || existingGoal.status !== "active") {
|
|
66
67
|
return { action: "none" };
|
|
67
68
|
}
|
|
68
|
-
let settings = (await resolveCatalogSettings(db, catalogSourceSettings)).settings;
|
|
69
69
|
// Loaded before the budget check so the codex-billed predicate and the
|
|
70
70
|
// synthesized turn use the SAME effective policy. An explicit per-turn
|
|
71
71
|
// model can differ from the persisted session default; follow-up goal work
|
|
@@ -74,6 +74,13 @@ export function createGoalActivities(services: () => Promise<ControlActivityServ
|
|
|
74
74
|
// Kept below the goal-less fast path so a non-goal session still skips the
|
|
75
75
|
// reads entirely.
|
|
76
76
|
const session = await requireSession(db, input.workspaceId, input.sessionId);
|
|
77
|
+
// Terminal sessions retain their goal for human recovery, but cannot
|
|
78
|
+
// continue. Do not validate an obsolete model before the locked guard gets
|
|
79
|
+
// the chance to reject that work; otherwise a deterministic error retries.
|
|
80
|
+
if (session.status === "failed" || session.status === "cancelled") {
|
|
81
|
+
return { action: "none" };
|
|
82
|
+
}
|
|
83
|
+
let settings = (await resolveCatalogSettings(db, catalogSourceSettings)).settings;
|
|
77
84
|
const inheritedContinuationModel = session.model;
|
|
78
85
|
let continuationModel = inheritedContinuationModel;
|
|
79
86
|
const continuationReasoningEffort = session.reasoningEffort;
|
|
@@ -109,39 +116,27 @@ export function createGoalActivities(services: () => Promise<ControlActivityServ
|
|
|
109
116
|
) {
|
|
110
117
|
modelPolicyBlocked = `session is locked to Codex remote compaction v2; model "${continuationModel}" is not a Codex subscription model`;
|
|
111
118
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
+
const turnExecutionPolicy = modelPolicyBlocked
|
|
120
|
+
? undefined
|
|
121
|
+
: resolveTurnExecutionPolicyV1(settings, {
|
|
122
|
+
modelId: continuationModel,
|
|
123
|
+
requestedModelId: null,
|
|
124
|
+
modelSource: "continuation",
|
|
125
|
+
reasoningEffort: continuationReasoningEffort,
|
|
126
|
+
reasoningSource: "continuation",
|
|
127
|
+
latencyMode: continuationLatencyMode,
|
|
128
|
+
latencyModeSource: "continuation",
|
|
129
|
+
});
|
|
130
|
+
const continuationPolicy: NonNullable<
|
|
131
|
+
Parameters<typeof materializeGoalContinuation>[1]["policy"]
|
|
132
|
+
> = {
|
|
119
133
|
model: continuationModel,
|
|
120
|
-
});
|
|
121
|
-
const fundedWithoutCredits = goalContinuationFundedWithoutCredits(
|
|
122
|
-
settings,
|
|
123
|
-
continuationModel,
|
|
124
|
-
isCodexRun,
|
|
125
|
-
);
|
|
126
|
-
// Budget exhaustion pauses the goal visibly instead of failing the
|
|
127
|
-
// session. Computed up front and applied inside the locked decision so a
|
|
128
|
-
// limits pause never consumes continuation budget.
|
|
129
|
-
const budgetBlocked = await goalRunBudgetBlocked(
|
|
130
|
-
settings,
|
|
131
|
-
db,
|
|
132
|
-
input.accountId,
|
|
133
|
-
input.workspaceId,
|
|
134
|
-
fundedWithoutCredits,
|
|
135
|
-
);
|
|
136
|
-
const turnExecutionPolicy = resolveTurnExecutionPolicyV1(settings, {
|
|
137
|
-
modelId: continuationModel,
|
|
138
|
-
requestedModelId: null,
|
|
139
|
-
modelSource: "continuation",
|
|
140
134
|
reasoningEffort: continuationReasoningEffort,
|
|
141
|
-
reasoningSource: "continuation",
|
|
142
135
|
latencyMode: continuationLatencyMode,
|
|
143
|
-
|
|
144
|
-
|
|
136
|
+
...(turnExecutionPolicy ? { turnExecutionPolicy } : {}),
|
|
137
|
+
tools: withFirstPartyTools(settings, session.tools),
|
|
138
|
+
sandboxBackend: session.sandboxBackend,
|
|
139
|
+
};
|
|
145
140
|
const decision = await materializeGoalContinuation(db, {
|
|
146
141
|
accountId: input.accountId,
|
|
147
142
|
workspaceId: input.workspaceId,
|
|
@@ -157,15 +152,45 @@ export function createGoalActivities(services: () => Promise<ControlActivityServ
|
|
|
157
152
|
// A model-policy block takes precedence: it is deterministic (a budget
|
|
158
153
|
// pause can clear on its own; a policy pause needs a model/policy change)
|
|
159
154
|
// and rides the same visible-pause channel.
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
155
|
+
admission: async (tx, causalTurn) => {
|
|
156
|
+
// The materializer selects this exact causal row under its session/goal
|
|
157
|
+
// locks and freezes policy only after admission returns. Never infer
|
|
158
|
+
// credential authority from the generated continuation's payload.
|
|
159
|
+
const sourceTurn = causalTurn
|
|
160
|
+
? await getSessionTurn(tx, input.workspaceId, causalTurn.id)
|
|
161
|
+
: null;
|
|
162
|
+
if (causalTurn && (!sourceTurn || sourceTurn.sessionId !== input.sessionId)) {
|
|
163
|
+
throw new Error("Goal continuation source turn is unavailable");
|
|
164
|
+
}
|
|
165
|
+
const sourcePolicy = readTurnExecutionPolicyV1(sourceTurn?.metadata);
|
|
166
|
+
if (turnExecutionPolicy) {
|
|
167
|
+
const credentialRestriction = turnCredentialRestriction(
|
|
168
|
+
sourcePolicy.kind === "valid" ? sourcePolicy.policy : turnExecutionPolicy,
|
|
169
|
+
session.metadata,
|
|
170
|
+
);
|
|
171
|
+
continuationPolicy.turnExecutionPolicy = credentialRestriction
|
|
172
|
+
? { ...turnExecutionPolicy, credentialRestriction }
|
|
173
|
+
: turnExecutionPolicy;
|
|
174
|
+
}
|
|
175
|
+
const budgetBlocked = modelPolicyBlocked
|
|
176
|
+
? null
|
|
177
|
+
: await goalRunBudgetBlocked(
|
|
178
|
+
{ ...service, settings, db: tx },
|
|
179
|
+
{
|
|
180
|
+
accountId: input.accountId,
|
|
181
|
+
workspaceId: input.workspaceId,
|
|
182
|
+
model: continuationModel,
|
|
183
|
+
initiatingHumanSubjectId: causalTurn?.initiatingHumanSubjectId ?? null,
|
|
184
|
+
},
|
|
185
|
+
);
|
|
186
|
+
return {
|
|
187
|
+
budgetBlocked: modelPolicyBlocked ?? budgetBlocked?.message ?? null,
|
|
188
|
+
budgetPausedReason: modelPolicyBlocked
|
|
189
|
+
? "limits"
|
|
190
|
+
: (budgetBlocked?.pausedReason ?? "limits"),
|
|
191
|
+
};
|
|
168
192
|
},
|
|
193
|
+
policy: continuationPolicy,
|
|
169
194
|
// Long-wait guidance is only given when `wait_for_input` is actually in this
|
|
170
195
|
// session's effective first-party selection (the same source the worker
|
|
171
196
|
// signs into the delegated token and the API uses to register tools), so
|
|
@@ -215,18 +240,21 @@ export function goalContinuationModelDecision(input: {
|
|
|
215
240
|
providerId: policyProviderIdForModel(catalogSettings, modelId),
|
|
216
241
|
modelId,
|
|
217
242
|
}).allowed;
|
|
218
|
-
if (
|
|
219
|
-
resolveModelProvider(catalogSettings, input.inheritedModel) &&
|
|
220
|
-
!policyBlocks(input.inheritedModel)
|
|
221
|
-
) {
|
|
222
|
-
return { model: input.inheritedModel, blocked: null };
|
|
223
|
-
}
|
|
224
243
|
if (!resolveModelProvider(catalogSettings, input.inheritedModel)) {
|
|
225
244
|
return {
|
|
226
245
|
model: input.inheritedModel,
|
|
227
246
|
blocked: `model "${input.inheritedModel}" is no longer in the deployment or workspace catalog; choose an available model before resuming the goal`,
|
|
228
247
|
};
|
|
229
248
|
}
|
|
249
|
+
if (!isModelAvailableForNewSelection(catalogSettings, input.inheritedModel)) {
|
|
250
|
+
return {
|
|
251
|
+
model: input.inheritedModel,
|
|
252
|
+
blocked: `model "${input.inheritedModel}" is retired from new selection; choose an available model before resuming the goal`,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
if (!policyBlocks(input.inheritedModel)) {
|
|
256
|
+
return { model: input.inheritedModel, blocked: null };
|
|
257
|
+
}
|
|
230
258
|
return {
|
|
231
259
|
model: input.inheritedModel,
|
|
232
260
|
blocked: `workspace model policy blocks model "${input.inheritedModel}"; pick an allowed model or change the workspace model policy`,
|
|
@@ -307,6 +335,8 @@ export function goalContinuationPrompt(
|
|
|
307
335
|
"- For document report deliverables (a document the user asked for, or a large report meant to be kept or shared), follow the Documents Skill: create the durable native document first, inspect its relevant final head after the last edit, and provide the returned artifact reference. Declare report requirements through the available goal tools before authoring and satisfy every persisted report requirement with verified artifact delivery evidence before completion. Sandbox paths and raw file IDs do not prove report delivery. If artifact tooling or access is unavailable, keep that deliverable incomplete and state the blocker; never invent proof or silently substitute a local report. Ordinary chat answers, short progress updates, source-code links, and explicitly requested local-file work remain outside this report contract.",
|
|
308
336
|
"",
|
|
309
337
|
"Do not rely on intent, partial progress, memory of earlier work, or a plausible final answer as proof of completion. Call opengeni__goal_complete with concrete evidence only when the full objective is actually achieved and no required work remains.",
|
|
338
|
+
"Goal evidence is a short proof for the ledger, not the deliverable. After goal_complete succeeds, finish this same turn with the requested user-facing answer, or a concise summary and retained artifact link. Goal completion stops future automatic continuations; it does not send the answer or end this turn. Never compress a report into evidence or omit the final reply.",
|
|
339
|
+
"Goal progress notes are short human-readable milestone statuses, not raw transcripts or continuation instructions. Keep normal spaces and summarize detail instead of squeezing words into a ledger field. The text and successCriteria fields each allow 8192 UTF-8 bytes, progressNote allows 8192 UTF-8 bytes, rationale allows 2048 UTF-8 bytes, and evidence allows 8192 characters.",
|
|
310
340
|
"",
|
|
311
341
|
...waitingGuidance,
|
|
312
342
|
...childNoticeGuidance,
|
|
@@ -336,55 +366,22 @@ export function withFirstPartyTools(settings: Settings, tools: ToolRef[]): ToolR
|
|
|
336
366
|
}
|
|
337
367
|
|
|
338
368
|
/**
|
|
339
|
-
*
|
|
340
|
-
* readable reason when balance or monthly caps block another agent run.
|
|
369
|
+
* Goals share scheduled admission and pause visibly without synthesizing work.
|
|
341
370
|
*/
|
|
342
|
-
async function goalRunBudgetBlocked(
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
// credit-balance gate and monthly model-cost cap. The agent-run COUNT cap
|
|
351
|
-
// below is a volume quota (not a credit/cost gate) and remains enforced.
|
|
352
|
-
if (
|
|
353
|
-
!fundedWithoutCredits &&
|
|
354
|
-
(settings.billingMode === "stripe" || settings.usageLimitsMode === "managed")
|
|
355
|
-
) {
|
|
356
|
-
const balance = await getBillingBalance(db, accountId);
|
|
357
|
-
if (balance.balanceMicros <= 0) {
|
|
358
|
-
return "insufficient OpenGeni credits";
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
if (settings.usageLimitsMode === "static" || settings.usageLimitsMode === "managed") {
|
|
362
|
-
const limits = configuredStaticUsageLimits(settings);
|
|
363
|
-
if (!fundedWithoutCredits && limits.maxMonthlyCostMicrosPerAccount) {
|
|
364
|
-
const used = await sumUsageQuantity(db, {
|
|
365
|
-
accountId,
|
|
366
|
-
eventType: "model.cost",
|
|
367
|
-
since: startOfUtcMonth(),
|
|
368
|
-
});
|
|
369
|
-
if (used >= limits.maxMonthlyCostMicrosPerAccount) {
|
|
370
|
-
return `monthly model cost limit reached (${limits.maxMonthlyCostMicrosPerAccount} micros)`;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
if (limits.maxMonthlyAgentRunsPerWorkspace) {
|
|
374
|
-
const used = await sumUsageQuantity(db, {
|
|
375
|
-
workspaceId,
|
|
376
|
-
eventType: "agent_run.created",
|
|
377
|
-
since: startOfUtcMonth(),
|
|
378
|
-
});
|
|
379
|
-
if (used + 1 > limits.maxMonthlyAgentRunsPerWorkspace) {
|
|
380
|
-
return `monthly agent run limit reached (${limits.maxMonthlyAgentRunsPerWorkspace})`;
|
|
381
|
-
}
|
|
382
|
-
}
|
|
371
|
+
export async function goalRunBudgetBlocked(
|
|
372
|
+
services: Parameters<typeof agentRunAdmissionDenial>[0],
|
|
373
|
+
input: Omit<Parameters<typeof agentRunAdmissionDenial>[1], "requestedAgentRuns">,
|
|
374
|
+
): Promise<{ pausedReason: "limits" | "allowance"; message: string } | null> {
|
|
375
|
+
const denial = await agentRunAdmissionDenial(services, { ...input, requestedAgentRuns: 1 });
|
|
376
|
+
if (denial === null) return null;
|
|
377
|
+
if (denial === "allowance_exhausted") {
|
|
378
|
+
return { pausedReason: "allowance", message: "OpenGeni usage allowance exhausted" };
|
|
383
379
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
380
|
+
const limits = configuredStaticUsageLimits(services.settings);
|
|
381
|
+
const messages = {
|
|
382
|
+
insufficient_credits: "insufficient OpenGeni credits",
|
|
383
|
+
monthly_model_cost_limit: `monthly model cost limit reached (${limits.maxMonthlyCostMicrosPerAccount} micros)`,
|
|
384
|
+
monthly_agent_run_limit: `monthly agent run limit reached (${limits.maxMonthlyAgentRunsPerWorkspace})`,
|
|
385
|
+
};
|
|
386
|
+
return { pausedReason: "limits", message: messages[denial] };
|
|
390
387
|
}
|
|
@@ -4,8 +4,10 @@ import {
|
|
|
4
4
|
applyCreditDebitAfterUse,
|
|
5
5
|
appendKnowledgeIndexChunks,
|
|
6
6
|
claimKnowledgeIndexJobs,
|
|
7
|
+
checkWorkspaceAllowance,
|
|
7
8
|
completeKnowledgeIndexJob,
|
|
8
9
|
continueKnowledgeIndexJob,
|
|
10
|
+
creditDebitAttributionMetadata,
|
|
9
11
|
deferKnowledgeIndexJob,
|
|
10
12
|
freezeKnowledgeIndexBillingMode,
|
|
11
13
|
getBillingBalance,
|
|
@@ -198,6 +200,16 @@ export function createKnowledgeIndexingActivities(
|
|
|
198
200
|
return;
|
|
199
201
|
}
|
|
200
202
|
const paid = frozenPolicy.mode === "credits";
|
|
203
|
+
if (paid && claim.billingAttribution.kind === "unknown") {
|
|
204
|
+
// Unknown legacy/source-preparation causality is not service.
|
|
205
|
+
// Keep the checkpoint without making an uncountable paid call.
|
|
206
|
+
observability.warn("Paid Knowledge indexing awaits initiating attribution", {
|
|
207
|
+
errorCode: "knowledge_index_attribution_unavailable",
|
|
208
|
+
});
|
|
209
|
+
await deferKnowledgeIndexJob(lockedDb, claim);
|
|
210
|
+
result.deferred++;
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
201
213
|
if (paid && current.nextIndex === 0) {
|
|
202
214
|
const balance = await getBillingBalance(lockedDb, claim.accountId);
|
|
203
215
|
if (balance.balanceMicros <= 0) {
|
|
@@ -206,6 +218,22 @@ export function createKnowledgeIndexingActivities(
|
|
|
206
218
|
return;
|
|
207
219
|
}
|
|
208
220
|
}
|
|
221
|
+
if (paid) {
|
|
222
|
+
const refusal = await checkWorkspaceAllowance(lockedDb, {
|
|
223
|
+
accountId: claim.accountId,
|
|
224
|
+
workspaceId: current.billingWorkspaceId,
|
|
225
|
+
subjectId:
|
|
226
|
+
claim.billingAttribution.kind === "turn" ||
|
|
227
|
+
claim.billingAttribution.kind === "human"
|
|
228
|
+
? claim.billingAttribution.initiatingHumanSubjectId
|
|
229
|
+
: null,
|
|
230
|
+
});
|
|
231
|
+
if (refusal) {
|
|
232
|
+
await deferKnowledgeIndexJob(lockedDb, claim);
|
|
233
|
+
result.deferred++;
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
209
237
|
if (settings.usageLimitsMode === "static" || settings.usageLimitsMode === "managed") {
|
|
210
238
|
const limit =
|
|
211
239
|
configuredStaticUsageLimits(settings).maxDocumentIndexedChunksPerWorkspace;
|
|
@@ -309,6 +337,7 @@ export function createKnowledgeIndexingActivities(
|
|
|
309
337
|
sourceId: claim.revisionId,
|
|
310
338
|
idempotencyKey: `knowledge.embedding:${claim.revisionId}:${claim.generation}:${current.nextIndex}`,
|
|
311
339
|
metadata: {
|
|
340
|
+
...creditDebitAttributionMetadata(claim.billingAttribution),
|
|
312
341
|
model: claim.model,
|
|
313
342
|
bytes,
|
|
314
343
|
chunks: chunks.length,
|
|
@@ -2,6 +2,7 @@ import type { Agent } from "@openai/agents";
|
|
|
2
2
|
import {
|
|
3
3
|
OPEN_SUFFIX_RUN_STATE_BLOB,
|
|
4
4
|
approvalIdentifier,
|
|
5
|
+
withPublicApprovalFields,
|
|
5
6
|
type HumanInputResponse,
|
|
6
7
|
type SessionEvent,
|
|
7
8
|
} from "@opengeni/contracts";
|
|
@@ -380,7 +381,7 @@ export async function settleOpenSuffixResumeIfNeeded(input: {
|
|
|
380
381
|
? [
|
|
381
382
|
{
|
|
382
383
|
type: "session.requiresAction" as const,
|
|
383
|
-
payload: { approvals: requiresActionApprovals },
|
|
384
|
+
payload: { approvals: requiresActionApprovals.map(withPublicApprovalFields) },
|
|
384
385
|
},
|
|
385
386
|
]
|
|
386
387
|
: []),
|
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
RunCredentialsRequest,
|
|
6
6
|
Session,
|
|
7
7
|
SessionTurn,
|
|
8
|
+
ToolRef,
|
|
8
9
|
} from "@opengeni/contracts";
|
|
9
10
|
import type { Settings } from "@opengeni/config";
|
|
10
11
|
import { getSessionRootId, type Database } from "@opengeni/db";
|
|
@@ -30,6 +31,8 @@ export type RunCredentialResolutionContext = {
|
|
|
30
31
|
initiatingHumanSubjectId?: string | null;
|
|
31
32
|
/** Installed in-process API routes must never enter a product callback. */
|
|
32
33
|
localMcpServerIds?: readonly string[];
|
|
34
|
+
/** Exact execution selection, resolved once at the turn policy boundary. */
|
|
35
|
+
effectiveTools: readonly ToolRef[];
|
|
33
36
|
};
|
|
34
37
|
|
|
35
38
|
export type BoundRunCredentialResolver = {
|
|
@@ -42,7 +45,7 @@ export type BoundRunCredentialResolver = {
|
|
|
42
45
|
export function buildRunCredentialsRequest(
|
|
43
46
|
input: Omit<
|
|
44
47
|
RunCredentialResolutionContext,
|
|
45
|
-
"db" | "connectionCredentials" | "settings" | "initiatingHumanSubjectId"
|
|
48
|
+
"db" | "connectionCredentials" | "settings" | "initiatingHumanSubjectId" | "effectiveTools"
|
|
46
49
|
> & {
|
|
47
50
|
rootSessionId: string;
|
|
48
51
|
purpose: "provision" | "renewal";
|
|
@@ -125,7 +128,7 @@ export async function bindRunCredentialResolver(
|
|
|
125
128
|
mcpServers: selectedSessionRemoteMcpTargets(
|
|
126
129
|
input.settings,
|
|
127
130
|
input.session.mcpServers ?? [],
|
|
128
|
-
input.
|
|
131
|
+
input.effectiveTools,
|
|
129
132
|
(input.localMcpServerIds ?? []).map((id) => ({ id })),
|
|
130
133
|
),
|
|
131
134
|
},
|
|
@@ -146,22 +149,25 @@ export async function bindRunCredentialResolver(
|
|
|
146
149
|
workspaceId: input.workspaceId,
|
|
147
150
|
sessionId: input.session.id,
|
|
148
151
|
};
|
|
152
|
+
// A renewable resolver belongs to one admitted turn, even if its caller
|
|
153
|
+
// subsequently replaces or mutates an in-memory session/turn projection.
|
|
154
|
+
const frozenRequest = structuredClone(
|
|
155
|
+
buildRunCredentialsRequest({
|
|
156
|
+
accountId: input.accountId,
|
|
157
|
+
workspaceId: input.workspaceId,
|
|
158
|
+
session: input.session,
|
|
159
|
+
turn: input.turn,
|
|
160
|
+
attemptId: input.attemptId,
|
|
161
|
+
effectiveSandboxBackend: input.effectiveSandboxBackend,
|
|
162
|
+
variableSet: input.variableSet,
|
|
163
|
+
rootSessionId,
|
|
164
|
+
purpose: "provision",
|
|
165
|
+
forceRefresh: false,
|
|
166
|
+
}),
|
|
167
|
+
);
|
|
149
168
|
return {
|
|
150
169
|
resolve: async ({ purpose, forceRefresh }) => {
|
|
151
|
-
const resolution = await resolver(
|
|
152
|
-
buildRunCredentialsRequest({
|
|
153
|
-
accountId: input.accountId,
|
|
154
|
-
workspaceId: input.workspaceId,
|
|
155
|
-
session: input.session,
|
|
156
|
-
turn: input.turn,
|
|
157
|
-
attemptId: input.attemptId,
|
|
158
|
-
effectiveSandboxBackend: input.effectiveSandboxBackend,
|
|
159
|
-
variableSet: input.variableSet,
|
|
160
|
-
rootSessionId,
|
|
161
|
-
purpose,
|
|
162
|
-
forceRefresh,
|
|
163
|
-
}),
|
|
164
|
-
);
|
|
170
|
+
const resolution = await resolver({ ...frozenRequest, purpose, forceRefresh });
|
|
165
171
|
return normalizeRunCredentialsResolution(resolution, scope);
|
|
166
172
|
},
|
|
167
173
|
};
|
|
@@ -41,6 +41,8 @@ import {
|
|
|
41
41
|
deferRetainedProcessReconciliation,
|
|
42
42
|
forceDrainOverLimitViewerOnlyBoxes,
|
|
43
43
|
listCreditBalancesByAccount,
|
|
44
|
+
countActiveUsers,
|
|
45
|
+
readCreditGrantTotals,
|
|
44
46
|
readVerifiedSignupTrialSwitch,
|
|
45
47
|
listLegacyModalCheckpointSlots,
|
|
46
48
|
listLiveModalSandboxLeaseAttributions,
|
|
@@ -150,7 +152,9 @@ import { assertSandboxDrainInputTiming, sandboxDrainTiming } from "../sandbox-re
|
|
|
150
152
|
import type { ControlActivityServices as ActivityServices } from "./types";
|
|
151
153
|
import { reconcilePendingParentSystemUpdates } from "./parent-wake";
|
|
152
154
|
import {
|
|
155
|
+
recordActiveUserGauges,
|
|
153
156
|
recordCreditBalanceGauges,
|
|
157
|
+
recordCreditGrantGauges,
|
|
154
158
|
recordCreditMicros,
|
|
155
159
|
recordExpiredDrainingSandboxLeaseGauges,
|
|
156
160
|
recordOpenSandboxKubernetesInventoryGauges,
|
|
@@ -2525,6 +2529,24 @@ async function refreshQueueLeaseAndCreditGauges(
|
|
|
2525
2529
|
});
|
|
2526
2530
|
}
|
|
2527
2531
|
})(),
|
|
2532
|
+
(async () => {
|
|
2533
|
+
try {
|
|
2534
|
+
recordActiveUserGauges(observability, await countActiveUsers(db));
|
|
2535
|
+
} catch (error) {
|
|
2536
|
+
observability.warn("sandbox reaper: active-user gauge refresh failed", {
|
|
2537
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2538
|
+
});
|
|
2539
|
+
}
|
|
2540
|
+
})(),
|
|
2541
|
+
(async () => {
|
|
2542
|
+
try {
|
|
2543
|
+
recordCreditGrantGauges(observability, await readCreditGrantTotals(db));
|
|
2544
|
+
} catch (error) {
|
|
2545
|
+
observability.warn("sandbox reaper: credit-grant gauge refresh failed", {
|
|
2546
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2547
|
+
});
|
|
2548
|
+
}
|
|
2549
|
+
})(),
|
|
2528
2550
|
(async () => {
|
|
2529
2551
|
recordVerifiedSignupTrialDeploymentFlagGauge(
|
|
2530
2552
|
observability,
|