@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
|
@@ -10,11 +10,14 @@ import {
|
|
|
10
10
|
SessionAgentAccess,
|
|
11
11
|
SessionMemoryScope,
|
|
12
12
|
normalizeAutomaticSessionTitle,
|
|
13
|
+
metadataWithTurnExecutionPolicyV1,
|
|
13
14
|
scheduledOccurrencePayloadUtf8Bytes,
|
|
14
15
|
stableJson,
|
|
15
16
|
TurnExecutionPolicyV1,
|
|
16
17
|
type ScheduledTask,
|
|
17
18
|
type ScheduledTaskRun,
|
|
19
|
+
AgentConfigError,
|
|
20
|
+
type ResolvedAgentConfig,
|
|
18
21
|
} from "@opengeni/contracts";
|
|
19
22
|
import {
|
|
20
23
|
openGeniSlackBotMetadata,
|
|
@@ -33,6 +36,12 @@ import {
|
|
|
33
36
|
settingsWithEnabledCapabilityMcpServers,
|
|
34
37
|
settingsWithSessionMcpServerMetadata,
|
|
35
38
|
swapActiveSandbox,
|
|
39
|
+
agentConfigMayResolve,
|
|
40
|
+
applySessionAgentConfigWriteThrough,
|
|
41
|
+
resolveSessionAgentConfigForCreate,
|
|
42
|
+
recordSessionCreated,
|
|
43
|
+
recordUserMessageAccepted,
|
|
44
|
+
type ProductUsageMetricsSink,
|
|
36
45
|
} from "@opengeni/core";
|
|
37
46
|
import {
|
|
38
47
|
appendSessionEvents,
|
|
@@ -62,6 +71,7 @@ import {
|
|
|
62
71
|
getNestedAgentDepthDeploymentPolicy,
|
|
63
72
|
getSessionByCreateIdempotencyKey,
|
|
64
73
|
getSessionCreationExecutionPolicy,
|
|
74
|
+
metadataWithAgentConfigCreateIdentity,
|
|
65
75
|
getVariableSet,
|
|
66
76
|
isCodexBilledModel,
|
|
67
77
|
initializeSessionStartAtomically,
|
|
@@ -120,6 +130,30 @@ type ScheduledTemporalActivityIdentity = {
|
|
|
120
130
|
activityId: string;
|
|
121
131
|
};
|
|
122
132
|
|
|
133
|
+
export function scheduledTaskRunExecutionPolicy(
|
|
134
|
+
policy: TurnExecutionPolicyV1,
|
|
135
|
+
input: DispatchScheduledTaskRunInput,
|
|
136
|
+
creatorRestriction?: "developer_setup",
|
|
137
|
+
): TurnExecutionPolicyV1 {
|
|
138
|
+
return creatorRestriction === "developer_setup" ||
|
|
139
|
+
(input.triggerType !== "scheduled" && input.credentialRestriction === "developer_setup")
|
|
140
|
+
? { ...policy, credentialRestriction: "developer_setup" }
|
|
141
|
+
: policy;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Only a creator ceiling is standing; a manual caller ceiling is per-run. */
|
|
145
|
+
export function scheduledSessionExecutionPolicyMetadata(
|
|
146
|
+
policy: unknown,
|
|
147
|
+
creatorRestriction?: "developer_setup",
|
|
148
|
+
): Record<string, unknown> {
|
|
149
|
+
if (policy === undefined || policy === null) return {};
|
|
150
|
+
const acceptedPolicy = TurnExecutionPolicyV1.parse(policy);
|
|
151
|
+
return creatorRestriction === "developer_setup" &&
|
|
152
|
+
acceptedPolicy.credentialRestriction === "developer_setup"
|
|
153
|
+
? metadataWithTurnExecutionPolicyV1({}, acceptedPolicy)
|
|
154
|
+
: {};
|
|
155
|
+
}
|
|
156
|
+
|
|
123
157
|
export function scheduledTaskRunProducerKey(
|
|
124
158
|
input: DispatchScheduledTaskRunInput,
|
|
125
159
|
suppliedActivityIdentity?: ScheduledTemporalActivityIdentity,
|
|
@@ -189,6 +223,15 @@ export function scheduledTaskGeneratedSessionCreateIdempotencyKey(producerKey: s
|
|
|
189
223
|
* alone, with no Intl call and so no host ICU or tzdata build to make the two
|
|
190
224
|
* texts differ.
|
|
191
225
|
*/
|
|
226
|
+
/** A scheduler-generated run session: a service-created root session. */
|
|
227
|
+
function recordScheduledSessionCreated(observability: ProductUsageMetricsSink | undefined): void {
|
|
228
|
+
recordSessionCreated(observability, {
|
|
229
|
+
surface: "scheduled",
|
|
230
|
+
createdByKind: "service",
|
|
231
|
+
parentSessionId: null,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
192
235
|
export function scheduledTaskSessionTitle(taskName: string): string {
|
|
193
236
|
return normalizeAutomaticSessionTitle(taskName) ?? "Scheduled run";
|
|
194
237
|
}
|
|
@@ -328,6 +371,7 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
|
|
|
328
371
|
try {
|
|
329
372
|
return await recoverBoundScheduledTaskDispatch({
|
|
330
373
|
db,
|
|
374
|
+
observability: baseService.observability,
|
|
331
375
|
bus,
|
|
332
376
|
settings,
|
|
333
377
|
wakeSessionWorkflow,
|
|
@@ -501,7 +545,7 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
|
|
|
501
545
|
settings.openaiReasoningEffort;
|
|
502
546
|
let sandboxBackend = task.agentConfig.sandboxBackend ?? settings.sandboxBackend;
|
|
503
547
|
let sandboxOs: "linux" | "macos" | "windows" = "linux";
|
|
504
|
-
|
|
548
|
+
let taskTools = withFirstPartyTools(settings, task.agentConfig.tools);
|
|
505
549
|
// A task created by a live agent attempt froze its creator's effective
|
|
506
550
|
// first-party selection, permission set, and session access policy;
|
|
507
551
|
// its generated sessions inherit that boundary under today's
|
|
@@ -510,13 +554,55 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
|
|
|
510
554
|
const creatorPolicy = await getScheduledTaskCreatorPolicy(db, task.workspaceId, task.id);
|
|
511
555
|
// A person-chosen Slack channel adds only the two bot posting tools;
|
|
512
556
|
// they post nowhere else, whatever the creator's own selection was.
|
|
513
|
-
|
|
557
|
+
let firstPartyMcpTools = withScheduledSlackBotPostingTools(
|
|
514
558
|
creatorPolicy?.firstPartyMcpTools
|
|
515
559
|
? allowedFirstPartyMcpToolsForSession(settings, creatorPolicy.firstPartyMcpTools)
|
|
516
560
|
: resolveFirstPartyMcpToolPolicy(settings).default,
|
|
517
561
|
task.agentConfig,
|
|
518
562
|
resolveFirstPartyMcpToolPolicy(settings).allowed,
|
|
519
563
|
);
|
|
564
|
+
// Agent configuration for generated sessions (null = legacy). The
|
|
565
|
+
// scheduled baseline above (creator policy or deployment default) is
|
|
566
|
+
// what "all" reproduces; capabilities only narrow it. Frozen into the
|
|
567
|
+
// accepted execution so retries and recovery never resolve again.
|
|
568
|
+
let resolvedAgentConfig: ResolvedAgentConfig | null = null;
|
|
569
|
+
let resolvedAgentInstructions: string | undefined;
|
|
570
|
+
try {
|
|
571
|
+
const agentResolution = resolveSessionAgentConfigForCreate({
|
|
572
|
+
settings,
|
|
573
|
+
creator: "scheduled",
|
|
574
|
+
request: task.agentConfig.agent,
|
|
575
|
+
instructions: undefined,
|
|
576
|
+
workspaceSettings: agentConfigMayResolve(settings, task.agentConfig.agent)
|
|
577
|
+
? (await requireWorkspace(db, task.workspaceId)).settings
|
|
578
|
+
: {},
|
|
579
|
+
parent: null,
|
|
580
|
+
goal: task.agentConfig.goal !== undefined,
|
|
581
|
+
});
|
|
582
|
+
resolvedAgentConfig = agentResolution.config;
|
|
583
|
+
resolvedAgentInstructions = agentResolution.instructions;
|
|
584
|
+
const written = applySessionAgentConfigWriteThrough({
|
|
585
|
+
config: resolvedAgentConfig,
|
|
586
|
+
firstPartyMcpTools,
|
|
587
|
+
tools: taskTools,
|
|
588
|
+
toolPolicy: { mode: "explicit", inheritedFromSessionId: null },
|
|
589
|
+
productServerIds: task.agentConfig.tools.map((tool) => tool.id),
|
|
590
|
+
});
|
|
591
|
+
firstPartyMcpTools = written.firstPartyMcpTools;
|
|
592
|
+
taskTools = written.tools;
|
|
593
|
+
} catch (error) {
|
|
594
|
+
const status =
|
|
595
|
+
error instanceof Error ? (error as Error & { status?: unknown }).status : undefined;
|
|
596
|
+
if (!(error instanceof Error) || status !== 422) throw error;
|
|
597
|
+
const notEnabled =
|
|
598
|
+
error.cause instanceof AgentConfigError &&
|
|
599
|
+
error.cause.code === "agent_config_not_enabled";
|
|
600
|
+
return await refuseAdmission(
|
|
601
|
+
"scheduled_authority_unavailable",
|
|
602
|
+
notEnabled,
|
|
603
|
+
`scheduled agent configuration is not admissible: ${error.message}`,
|
|
604
|
+
);
|
|
605
|
+
}
|
|
520
606
|
const firstPartyMcpPermissions = creatorPolicy?.firstPartyMcpPermissions
|
|
521
607
|
? [...creatorPolicy.firstPartyMcpPermissions]
|
|
522
608
|
: [...DEFAULT_FIRST_PARTY_MCP_PERMISSIONS];
|
|
@@ -917,6 +1003,7 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
|
|
|
917
1003
|
workspaceId: task.workspaceId,
|
|
918
1004
|
model: targetSessionExecution?.model ?? model,
|
|
919
1005
|
requestedAgentRuns: input.agentRunUsageIdempotencyKey ? 0 : 1,
|
|
1006
|
+
initiatingHumanSubjectId: causalHumanSubjectId,
|
|
920
1007
|
},
|
|
921
1008
|
);
|
|
922
1009
|
if (admissionDenial) {
|
|
@@ -945,23 +1032,28 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
|
|
|
945
1032
|
}
|
|
946
1033
|
}
|
|
947
1034
|
: undefined;
|
|
948
|
-
const turnExecutionPolicy
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
1035
|
+
const turnExecutionPolicy = scheduledTaskRunExecutionPolicy(
|
|
1036
|
+
resolveTurnExecutionPolicyV1(settings, {
|
|
1037
|
+
modelId: acceptedModel,
|
|
1038
|
+
requestedModelId:
|
|
1039
|
+
generatedTarget && task.agentConfig.model ? task.agentConfig.model : null,
|
|
1040
|
+
modelSource: generatedTarget
|
|
1041
|
+
? task.agentConfig.model
|
|
1042
|
+
? "explicit"
|
|
1043
|
+
: "deployment"
|
|
1044
|
+
: "session",
|
|
1045
|
+
reasoningEffort: acceptedReasoningEffort,
|
|
1046
|
+
reasoningSource: generatedTarget
|
|
1047
|
+
? task.agentConfig.reasoningEffort
|
|
1048
|
+
? "explicit"
|
|
1049
|
+
: "deployment"
|
|
1050
|
+
: "session",
|
|
1051
|
+
latencyMode: acceptedLatencyMode,
|
|
1052
|
+
latencyModeSource: generatedTarget ? "deployment" : "session",
|
|
1053
|
+
}),
|
|
1054
|
+
input,
|
|
1055
|
+
creatorPolicy?.credentialRestriction,
|
|
1056
|
+
);
|
|
965
1057
|
const deferredEvents: Array<{
|
|
966
1058
|
sessionId: string;
|
|
967
1059
|
events: Awaited<ReturnType<typeof appendSessionEvents>>;
|
|
@@ -994,6 +1086,12 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
|
|
|
994
1086
|
resolvedTools: taskTools,
|
|
995
1087
|
resolvedFirstPartyMcpTools: firstPartyMcpTools,
|
|
996
1088
|
resolvedFirstPartyMcpPermissions: firstPartyMcpPermissions,
|
|
1089
|
+
...(resolvedAgentConfig
|
|
1090
|
+
? {
|
|
1091
|
+
resolvedAgentConfig,
|
|
1092
|
+
...(resolvedAgentInstructions !== undefined ? { resolvedAgentInstructions } : {}),
|
|
1093
|
+
}
|
|
1094
|
+
: {}),
|
|
997
1095
|
resolvedVariableSet: acceptedVariableSet
|
|
998
1096
|
? { id: acceptedVariableSet.id, generation: acceptedVariableSet.generation }
|
|
999
1097
|
: null,
|
|
@@ -1201,6 +1299,10 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
|
|
|
1201
1299
|
tools: taskTools,
|
|
1202
1300
|
firstPartyMcpTools,
|
|
1203
1301
|
firstPartyMcpPermissions,
|
|
1302
|
+
...(resolvedAgentConfig ? { agentConfig: resolvedAgentConfig } : {}),
|
|
1303
|
+
...(resolvedAgentInstructions !== undefined
|
|
1304
|
+
? { instructions: resolvedAgentInstructions }
|
|
1305
|
+
: {}),
|
|
1204
1306
|
...creatorSessionPolicy,
|
|
1205
1307
|
...(task.agentConfig.knowledgeSource?.destination.kind === "personal"
|
|
1206
1308
|
? {
|
|
@@ -1210,6 +1312,10 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
|
|
|
1210
1312
|
: {}),
|
|
1211
1313
|
metadata: {
|
|
1212
1314
|
...taskMetadata,
|
|
1315
|
+
...scheduledSessionExecutionPolicyMetadata(
|
|
1316
|
+
turnExecutionPolicy,
|
|
1317
|
+
creatorPolicy?.credentialRestriction,
|
|
1318
|
+
),
|
|
1213
1319
|
model,
|
|
1214
1320
|
reasoningEffort,
|
|
1215
1321
|
scheduledTaskId: task.id,
|
|
@@ -1305,6 +1411,7 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
|
|
|
1305
1411
|
}
|
|
1306
1412
|
session = created.session;
|
|
1307
1413
|
sessionCreated = created.created;
|
|
1414
|
+
if (sessionCreated) recordScheduledSessionCreated(baseService.observability);
|
|
1308
1415
|
if (!sessionCreated) {
|
|
1309
1416
|
await bindScheduledTaskRunSessionInTransaction(dispatchDb, {
|
|
1310
1417
|
accountId: task.accountId,
|
|
@@ -1589,6 +1696,9 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
|
|
|
1589
1696
|
run: { ...run, status: "skipped" as const, error },
|
|
1590
1697
|
};
|
|
1591
1698
|
}
|
|
1699
|
+
if (scheduledUpdate.added) {
|
|
1700
|
+
recordUserMessageAccepted(baseService.observability, { surface: "scheduled" });
|
|
1701
|
+
}
|
|
1592
1702
|
if (scheduledUpdate.added && scheduledUpdate.events.length > 0) {
|
|
1593
1703
|
if (deferPublications) {
|
|
1594
1704
|
deferredEvents.push({
|
|
@@ -1732,6 +1842,9 @@ export function createScheduledTaskActivities(services: () => Promise<ControlAct
|
|
|
1732
1842
|
run: { ...run, status: "skipped" as const, error },
|
|
1733
1843
|
};
|
|
1734
1844
|
}
|
|
1845
|
+
if (bundled.added) {
|
|
1846
|
+
recordUserMessageAccepted(baseService.observability, { surface: "scheduled" });
|
|
1847
|
+
}
|
|
1735
1848
|
if (bundled.added && bundled.events.length > 0) {
|
|
1736
1849
|
if (deferPublications) {
|
|
1737
1850
|
deferredEvents.push({
|
|
@@ -2143,6 +2256,7 @@ async function seedScheduledGeneratedSessionRoute(input: {
|
|
|
2143
2256
|
|
|
2144
2257
|
async function recoverBoundScheduledTaskDispatch(input: {
|
|
2145
2258
|
db: Database;
|
|
2259
|
+
observability?: ProductUsageMetricsSink | undefined;
|
|
2146
2260
|
bus: ControlActivityServices["bus"];
|
|
2147
2261
|
settings: ControlActivityServices["settings"];
|
|
2148
2262
|
wakeSessionWorkflow: WakeSessionWorkflowSignal | null;
|
|
@@ -2157,6 +2271,11 @@ async function recoverBoundScheduledTaskDispatch(input: {
|
|
|
2157
2271
|
const generatedSession =
|
|
2158
2272
|
task.runMode === "new_session_per_run" ||
|
|
2159
2273
|
(task.runMode === "reusable_session" && task.reusableSessionId === null);
|
|
2274
|
+
// The immutable creator policy determines standing session authority, not
|
|
2275
|
+
// a manual caller's per-run ceiling. Read it for creation and recovery checks.
|
|
2276
|
+
const recoveredCreatorPolicy = generatedSession
|
|
2277
|
+
? await getScheduledTaskCreatorPolicy(input.db, task.workspaceId, task.id)
|
|
2278
|
+
: null;
|
|
2160
2279
|
const frozenSlack = input.acceptedExecution.resolvedSlackBotConnection;
|
|
2161
2280
|
if (frozenSlack) {
|
|
2162
2281
|
const currentSlack = await requireOpenGeniSlackBotConnection(
|
|
@@ -2225,14 +2344,6 @@ async function recoverBoundScheduledTaskDispatch(input: {
|
|
|
2225
2344
|
}
|
|
2226
2345
|
const taskMetadata = { ...task.agentConfig.metadata };
|
|
2227
2346
|
delete taskMetadata[OPENGENI_SLACK_BOT_SESSION_METADATA_KEY];
|
|
2228
|
-
// Tools and permissions were frozen into the accepted execution. The
|
|
2229
|
-
// creator session policy is immutable on the task row (a tombstoned task
|
|
2230
|
-
// still answers), so re-reading it here is deterministic for the same run.
|
|
2231
|
-
const recoveredCreatorPolicy = await getScheduledTaskCreatorPolicy(
|
|
2232
|
-
input.db,
|
|
2233
|
-
task.workspaceId,
|
|
2234
|
-
task.id,
|
|
2235
|
-
);
|
|
2236
2347
|
const created = await createSessionWithIdempotencyKeyResult(input.db, {
|
|
2237
2348
|
accountId: task.accountId,
|
|
2238
2349
|
workspaceId: task.workspaceId,
|
|
@@ -2242,6 +2353,12 @@ async function recoverBoundScheduledTaskDispatch(input: {
|
|
|
2242
2353
|
tools: input.acceptedExecution.resolvedTools,
|
|
2243
2354
|
firstPartyMcpTools: input.acceptedExecution.resolvedFirstPartyMcpTools,
|
|
2244
2355
|
firstPartyMcpPermissions: input.acceptedExecution.resolvedFirstPartyMcpPermissions,
|
|
2356
|
+
...(input.acceptedExecution.resolvedAgentConfig
|
|
2357
|
+
? { agentConfig: input.acceptedExecution.resolvedAgentConfig }
|
|
2358
|
+
: {}),
|
|
2359
|
+
...(input.acceptedExecution.resolvedAgentInstructions !== undefined
|
|
2360
|
+
? { instructions: input.acceptedExecution.resolvedAgentInstructions }
|
|
2361
|
+
: {}),
|
|
2245
2362
|
...scheduledCreatorSessionPolicyInput(recoveredCreatorPolicy?.sessionPolicy ?? null),
|
|
2246
2363
|
...(task.agentConfig.knowledgeSource?.destination.kind === "personal"
|
|
2247
2364
|
? {
|
|
@@ -2251,6 +2368,10 @@ async function recoverBoundScheduledTaskDispatch(input: {
|
|
|
2251
2368
|
: {}),
|
|
2252
2369
|
metadata: {
|
|
2253
2370
|
...taskMetadata,
|
|
2371
|
+
...scheduledSessionExecutionPolicyMetadata(
|
|
2372
|
+
input.acceptedExecution.turnExecutionPolicy,
|
|
2373
|
+
recoveredCreatorPolicy?.credentialRestriction,
|
|
2374
|
+
),
|
|
2254
2375
|
model: input.acceptedExecution.resolvedModel,
|
|
2255
2376
|
reasoningEffort: input.acceptedExecution.resolvedReasoningEffort,
|
|
2256
2377
|
scheduledTaskId: task.id,
|
|
@@ -2309,6 +2430,7 @@ async function recoverBoundScheduledTaskDispatch(input: {
|
|
|
2309
2430
|
});
|
|
2310
2431
|
if (created.denied) throw new SessionSpawnDeniedDbError(created.denial);
|
|
2311
2432
|
session = created.session;
|
|
2433
|
+
if (created.created) recordScheduledSessionCreated(input.observability);
|
|
2312
2434
|
if (!created.created) {
|
|
2313
2435
|
await bindScheduledTaskRunSessionInTransaction(input.db, {
|
|
2314
2436
|
accountId: task.accountId,
|
|
@@ -2379,20 +2501,27 @@ async function recoverBoundScheduledTaskDispatch(input: {
|
|
|
2379
2501
|
);
|
|
2380
2502
|
const expectedTaskMetadata = { ...task.agentConfig.metadata };
|
|
2381
2503
|
delete expectedTaskMetadata[OPENGENI_SLACK_BOT_SESSION_METADATA_KEY];
|
|
2382
|
-
const expectedMetadata =
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2504
|
+
const expectedMetadata = metadataWithAgentConfigCreateIdentity(
|
|
2505
|
+
{
|
|
2506
|
+
...expectedTaskMetadata,
|
|
2507
|
+
...scheduledSessionExecutionPolicyMetadata(
|
|
2508
|
+
input.acceptedExecution.turnExecutionPolicy,
|
|
2509
|
+
recoveredCreatorPolicy?.credentialRestriction,
|
|
2510
|
+
),
|
|
2511
|
+
model: input.acceptedExecution.resolvedModel,
|
|
2512
|
+
reasoningEffort: input.acceptedExecution.resolvedReasoningEffort,
|
|
2513
|
+
scheduledTaskId: task.id,
|
|
2514
|
+
scheduledTaskRunMode: task.runMode,
|
|
2515
|
+
...(task.agentConfig.goal ? { scheduledTaskGoal: task.agentConfig.goal } : {}),
|
|
2516
|
+
scheduledTaskRunId: canonicalGeneratedRunId,
|
|
2517
|
+
...(frozenSlack ? { [OPENGENI_SLACK_BOT_SESSION_METADATA_KEY]: frozenSlack.id } : {}),
|
|
2518
|
+
},
|
|
2519
|
+
input.acceptedExecution.resolvedAgentConfig,
|
|
2520
|
+
);
|
|
2392
2521
|
if (
|
|
2393
2522
|
session.createIdempotencyKey !== expectedCreateKey ||
|
|
2394
2523
|
session.initialMessage !== task.agentConfig.prompt ||
|
|
2395
|
-
session.instructions !== null ||
|
|
2524
|
+
session.instructions !== (input.acceptedExecution.resolvedAgentInstructions ?? null) ||
|
|
2396
2525
|
session.policyRole !== null ||
|
|
2397
2526
|
stableJson(session.skills) !== "[]" ||
|
|
2398
2527
|
stableJson(session.toolPolicy) !==
|
|
@@ -2434,6 +2563,8 @@ async function recoverBoundScheduledTaskDispatch(input: {
|
|
|
2434
2563
|
stableJson(input.acceptedExecution.resolvedFirstPartyMcpTools) ||
|
|
2435
2564
|
stableJson(session.firstPartyMcpPermissions) !==
|
|
2436
2565
|
stableJson(input.acceptedExecution.resolvedFirstPartyMcpPermissions) ||
|
|
2566
|
+
stableJson(session.agent) !==
|
|
2567
|
+
stableJson(input.acceptedExecution.resolvedAgentConfig ?? null) ||
|
|
2437
2568
|
session.maxNestedAgentDepthOverride !== (task.agentConfig.maxNestedAgentDepth ?? null) ||
|
|
2438
2569
|
(session.variableSetId ?? null) !==
|
|
2439
2570
|
(input.acceptedExecution.resolvedVariableSet?.id ?? null) ||
|
|
@@ -2599,6 +2730,9 @@ async function recoverBoundScheduledTaskDispatch(input: {
|
|
|
2599
2730
|
) {
|
|
2600
2731
|
return { action: "blocked", reason: "scheduled_run_terminal" };
|
|
2601
2732
|
}
|
|
2733
|
+
if (scheduledUpdate.added) {
|
|
2734
|
+
recordUserMessageAccepted(input.observability, { surface: "scheduled" });
|
|
2735
|
+
}
|
|
2602
2736
|
if (scheduledUpdate.added && scheduledUpdate.events.length > 0) {
|
|
2603
2737
|
await publishDurableSessionEvents(
|
|
2604
2738
|
input.bus,
|
|
@@ -176,6 +176,7 @@ async function dispatchMaintenanceSession(
|
|
|
176
176
|
initialMessage: maintenancePrompt(maintenance),
|
|
177
177
|
// OpenGeni's own periodic check: no human or product surface entered it.
|
|
178
178
|
surface: "system",
|
|
179
|
+
metrics: service.observability,
|
|
179
180
|
resources: [],
|
|
180
181
|
tools: [{ kind: "mcp", id: "opengeni" }],
|
|
181
182
|
toolPolicy: { mode: "explicit", inheritedFromSessionId: null },
|
package/src/activities/types.ts
CHANGED
|
@@ -436,6 +436,7 @@ export type DispatchScheduledTaskRunInput = {
|
|
|
436
436
|
triggerType: Extract<ScheduledTaskTriggerType, "scheduled">;
|
|
437
437
|
agentRunUsageIdempotencyKey?: never;
|
|
438
438
|
initiator?: never;
|
|
439
|
+
credentialRestriction?: never;
|
|
439
440
|
}
|
|
440
441
|
| {
|
|
441
442
|
triggerType: Extract<
|
|
@@ -445,6 +446,8 @@ export type DispatchScheduledTaskRunInput = {
|
|
|
445
446
|
agentRunUsageIdempotencyKey: string;
|
|
446
447
|
/** Exact identity used by the API-side charge for this same trigger. */
|
|
447
448
|
initiator: TurnInitiator;
|
|
449
|
+
/** Trusted API-side caller ceiling for this occurrence, not the schedule. */
|
|
450
|
+
credentialRestriction?: "developer_setup";
|
|
448
451
|
}
|
|
449
452
|
);
|
|
450
453
|
|
|
@@ -458,6 +461,7 @@ export type DispatchScheduledTaskRunResult =
|
|
|
458
461
|
refusal?: import("@opengeni/contracts").ScheduledTaskAdmissionRefusal;
|
|
459
462
|
reason:
|
|
460
463
|
| "insufficient_credits"
|
|
464
|
+
| "allowance_exhausted"
|
|
461
465
|
| "monthly_model_cost_limit"
|
|
462
466
|
| "monthly_agent_run_limit"
|
|
463
467
|
| "malformed_manual_trigger"
|
|
@@ -947,13 +947,24 @@ async function runCapture(
|
|
|
947
947
|
}
|
|
948
948
|
|
|
949
949
|
// ── 10. observe completion ────────────────────────────────────────────────
|
|
950
|
-
|
|
950
|
+
recordWorkspaceCaptureCompleted(observability, Date.now() - startedAt);
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
/** Count one committed logical capture and observe its end-to-end duration. */
|
|
954
|
+
export function recordWorkspaceCaptureCompleted(
|
|
955
|
+
observability: Pick<Observability, "incrementCounter" | "observeHistogram">,
|
|
956
|
+
durationMs: number,
|
|
957
|
+
): void {
|
|
951
958
|
observability.incrementCounter({
|
|
952
959
|
name: "opengeni_workspace_capture_total",
|
|
953
960
|
labels: { result: "ok" },
|
|
954
961
|
});
|
|
962
|
+
// Distinct from the physical `opengeni_workspace_capture_duration_seconds`
|
|
963
|
+
// {backend,outcome} histogram the runtime hook records in this same
|
|
964
|
+
// registry: one name cannot carry two label sets.
|
|
955
965
|
observability.observeHistogram({
|
|
956
|
-
name: "
|
|
966
|
+
name: "opengeni_workspace_capture_revision_duration_seconds",
|
|
967
|
+
help: "Turn-end logical workspace revision capture duration for committed revisions.",
|
|
957
968
|
value: durationMs / 1000,
|
|
958
969
|
});
|
|
959
970
|
}
|
|
@@ -78,6 +78,8 @@ export function credentialProviderRequestBody(
|
|
|
78
78
|
mcpServers: [],
|
|
79
79
|
},
|
|
80
80
|
): CredentialProviderRequest {
|
|
81
|
+
const via = input.initiatorContext.via;
|
|
82
|
+
const latestHop = Array.isArray(via) ? via.at(-1) : null;
|
|
81
83
|
return {
|
|
82
84
|
type: "credentials.request",
|
|
83
85
|
lane: selection.lane,
|
|
@@ -92,6 +94,16 @@ export function credentialProviderRequestBody(
|
|
|
92
94
|
turnId: input.turnId,
|
|
93
95
|
attemptId: input.attemptId,
|
|
94
96
|
initiator: { kind: input.initiator.kind, subjectId: input.initiator.subjectId },
|
|
97
|
+
initiatorContext: {
|
|
98
|
+
kind:
|
|
99
|
+
latestHop?.kind === "agent"
|
|
100
|
+
? "agent"
|
|
101
|
+
: input.initiator.kind === "subject"
|
|
102
|
+
? "human"
|
|
103
|
+
: "service",
|
|
104
|
+
initiator: input.initiator,
|
|
105
|
+
context: input.initiatorContext,
|
|
106
|
+
},
|
|
95
107
|
initiatingHumanSubjectId,
|
|
96
108
|
initiatingHuman,
|
|
97
109
|
sandboxBackend: input.effectiveSandboxBackend,
|
|
@@ -2,6 +2,7 @@ import { createKnowledgeIndexingActivities } from "./activities/knowledge-indexi
|
|
|
2
2
|
import { createSharedActivityServices } from "./activity-services";
|
|
3
3
|
import { createCodexCapacityActivities } from "./activities/codex-capacity";
|
|
4
4
|
import { createBrowserStateArtifactMaintenanceActivities } from "./activities/browser-state-artifact-reaper";
|
|
5
|
+
import { createBrowserDeadlineCheckpointActivities } from "./activities/browser-deadline-checkpoint";
|
|
5
6
|
import { createAutomationActivities } from "./activities/automations";
|
|
6
7
|
import { createDocumentActivities } from "./activities/documents";
|
|
7
8
|
import { createFileUploadReaperActivities } from "./activities/file-upload-reaper";
|
|
@@ -39,6 +40,7 @@ export function createControlActivitiesFromServices(
|
|
|
39
40
|
...createCodexCapacityActivities(services),
|
|
40
41
|
...createRigVerificationActivities(services),
|
|
41
42
|
...createBrowserStateArtifactMaintenanceActivities(services),
|
|
43
|
+
...createBrowserDeadlineCheckpointActivities(services),
|
|
42
44
|
...createFileUploadReaperActivities(services),
|
|
43
45
|
...createRetainedScreenshotMaintenanceActivities(services),
|
|
44
46
|
...createWorkflowWakeActivities(services),
|
package/src/activities.ts
CHANGED
|
@@ -117,6 +117,9 @@ export const maybeContinueGoal = defaultControlActivities.maybeContinueGoal;
|
|
|
117
117
|
export const getCodexCapacityWait = defaultControlActivities.getCodexCapacityWait;
|
|
118
118
|
export const reconcileCodexCapacityWait = defaultControlActivities.reconcileCodexCapacityWait;
|
|
119
119
|
export const prepareSandboxLeaseSweep = defaultControlActivities.prepareSandboxLeaseSweep;
|
|
120
|
+
export const listDueBrowserCheckpoints = defaultControlActivities.listDueBrowserCheckpoints;
|
|
121
|
+
export const checkpointBrowserBeforeDeadline =
|
|
122
|
+
defaultControlActivities.checkpointBrowserBeforeDeadline;
|
|
120
123
|
export const drainSandboxLease = defaultControlActivities.drainSandboxLease;
|
|
121
124
|
export const maintainSandboxLeaseSweep = defaultControlActivities.maintainSandboxLeaseSweep;
|
|
122
125
|
export const reapSandboxLeases = defaultControlActivities.reapSandboxLeases;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export const BROWSER_DEADLINE_CHECKPOINT_SWEEP_ID = "opengeni-browser-deadline-checkpoints-v1";
|
|
2
|
+
const QUEUE_SUFFIX = "-browser-checkpoint-v1";
|
|
3
|
+
export function browserDeadlineCheckpointTaskQueue(base: string): string {
|
|
4
|
+
return base.endsWith(QUEUE_SUFFIX) ? base : `${base}${QUEUE_SUFFIX}`;
|
|
5
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -99,6 +99,10 @@ import {
|
|
|
99
99
|
SANDBOX_REAPER_V2_WORKFLOW_ID,
|
|
100
100
|
sandboxLifecycleTaskQueue,
|
|
101
101
|
} from "./sandbox-reaper-contract";
|
|
102
|
+
import {
|
|
103
|
+
BROWSER_DEADLINE_CHECKPOINT_SWEEP_ID,
|
|
104
|
+
browserDeadlineCheckpointTaskQueue,
|
|
105
|
+
} from "./browser-deadline-checkpoint-contract";
|
|
102
106
|
|
|
103
107
|
export {
|
|
104
108
|
createHostExportPump,
|
|
@@ -323,28 +327,39 @@ export async function createOpenGeniWorker(options: WorkerOptions): Promise<{
|
|
|
323
327
|
return { worker, connection };
|
|
324
328
|
}
|
|
325
329
|
|
|
330
|
+
const ownedWorkers = [worker];
|
|
326
331
|
try {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
332
|
+
// The new activity type has its own queue. Retain the V1 poller so old
|
|
333
|
+
// drain histories finish; old binaries never receive checkpoint work.
|
|
334
|
+
for (const taskQueue of [
|
|
335
|
+
sandboxLifecycleTaskQueue(settings.temporalTaskQueue),
|
|
336
|
+
browserDeadlineCheckpointTaskQueue(settings.temporalTaskQueue),
|
|
337
|
+
]) {
|
|
338
|
+
const lifecycleWorker = await Worker.create({
|
|
339
|
+
...sharedWorkerOptions,
|
|
340
|
+
...workflowDefinition,
|
|
341
|
+
taskQueue,
|
|
342
|
+
reuseV8Context: true,
|
|
343
|
+
workflowThreadPoolSize: 1,
|
|
344
|
+
maxCachedWorkflows: CONTROL_WORKER_MAX_CACHED_WORKFLOWS,
|
|
345
|
+
maxConcurrentWorkflowTaskExecutions: CONTROL_WORKER_MAX_CONCURRENT_WORKFLOW_TASKS,
|
|
346
|
+
maxConcurrentActivityTaskExecutions: CONTROL_WORKER_MAX_CONCURRENT_ACTIVITIES,
|
|
347
|
+
});
|
|
348
|
+
ownedWorkers.push(lifecycleWorker);
|
|
349
|
+
}
|
|
337
350
|
return {
|
|
338
|
-
worker: combineWorkerRunTargets(
|
|
351
|
+
worker: combineWorkerRunTargets(ownedWorkers),
|
|
339
352
|
connection,
|
|
340
353
|
};
|
|
341
354
|
} catch (error) {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
355
|
+
for (const ownedWorker of ownedWorkers) {
|
|
356
|
+
try {
|
|
357
|
+
ownedWorker.shutdown();
|
|
358
|
+
} catch {
|
|
359
|
+
// Preserve the lifecycle worker construction failure. The base poller
|
|
360
|
+
// still received its shutdown request; a secondary synchronous
|
|
361
|
+
// shutdown error must not replace the startup root cause.
|
|
362
|
+
}
|
|
348
363
|
}
|
|
349
364
|
throw error;
|
|
350
365
|
}
|
|
@@ -478,20 +493,33 @@ export async function createWorkerWorkflowSignaler(
|
|
|
478
493
|
return normalizeTurnTaskQueueStats(response.stats);
|
|
479
494
|
},
|
|
480
495
|
startSandboxReaperWorkflow: async () => {
|
|
481
|
-
|
|
482
|
-
|
|
496
|
+
// Same backend tick, no additional schedule. A bounded independent
|
|
497
|
+
// inventory cannot prevent the existing per-box drain from starting.
|
|
498
|
+
const starts = await Promise.allSettled([
|
|
499
|
+
temporal.workflow.start("sandboxReaperWorkflowV2", {
|
|
483
500
|
taskQueue: sandboxLifecycleTaskQueue(settings.temporalTaskQueue),
|
|
484
501
|
workflowId: SANDBOX_REAPER_V2_WORKFLOW_ID,
|
|
485
502
|
workflowIdReusePolicy: "ALLOW_DUPLICATE",
|
|
486
503
|
args: [],
|
|
487
|
-
})
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
504
|
+
}),
|
|
505
|
+
temporal.workflow.start("browserDeadlineCheckpointSweepWorkflow", {
|
|
506
|
+
taskQueue: browserDeadlineCheckpointTaskQueue(settings.temporalTaskQueue),
|
|
507
|
+
workflowId: BROWSER_DEADLINE_CHECKPOINT_SWEEP_ID,
|
|
508
|
+
workflowIdReusePolicy: "ALLOW_DUPLICATE",
|
|
509
|
+
args: [],
|
|
510
|
+
}),
|
|
511
|
+
]);
|
|
512
|
+
for (const start of starts) {
|
|
513
|
+
if (
|
|
514
|
+
start.status === "rejected" &&
|
|
515
|
+
!(start.reason instanceof WorkflowExecutionAlreadyStartedError)
|
|
516
|
+
) {
|
|
517
|
+
// The other start already settled. Retry the router without losing
|
|
518
|
+
// ordinary draining or silently omitting the checkpoint sweep.
|
|
519
|
+
throw start.reason;
|
|
492
520
|
}
|
|
493
|
-
throw error;
|
|
494
521
|
}
|
|
522
|
+
return starts[0].status === "fulfilled" ? "started" : "already_running";
|
|
495
523
|
},
|
|
496
524
|
startVideoGenerationWorkflow: async ({ accountId, workspaceId, operationId }) => {
|
|
497
525
|
try {
|