@opengeni/worker-bundle 1.0.0-canary.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/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 +4 -0
- package/dist/activities/agent-turn/claude-usage-observer.d.ts +58 -0
- package/dist/activities/agent-turn/code-search.d.ts +60 -0
- package/dist/activities/agent-turn/codex-capacity.d.ts +13 -0
- package/dist/activities/agent-turn/codex-plan-entitlement.d.ts +105 -0
- package/dist/activities/agent-turn/codex.d.ts +3 -2
- package/dist/activities/agent-turn/compaction-prep.d.ts +21 -5
- package/dist/activities/agent-turn/credential-leases.d.ts +19 -33
- package/dist/activities/agent-turn/credential-restriction.d.ts +3 -0
- package/dist/activities/agent-turn/errors.d.ts +82 -11
- package/dist/activities/agent-turn/failure-settlement.d.ts +6 -3
- package/dist/activities/agent-turn/final-reply.d.ts +18 -0
- package/dist/activities/agent-turn/finalization-monitor.d.ts +17 -0
- package/dist/activities/agent-turn/finalization.d.ts +4 -2
- package/dist/activities/agent-turn/github-repository-bindings.d.ts +2 -0
- package/dist/activities/agent-turn/governance-model.d.ts +9 -2
- package/dist/activities/agent-turn/history-sink.d.ts +2 -0
- package/dist/activities/agent-turn/history.d.ts +2 -0
- package/dist/activities/agent-turn/input-wait-reply.d.ts +41 -0
- package/dist/activities/agent-turn/media-artifacts.d.ts +5 -3
- package/dist/activities/agent-turn/model-call-admission.d.ts +15 -0
- package/dist/activities/agent-turn/model-usage.d.ts +6 -0
- package/dist/activities/agent-turn/model-wire-dispatch.d.ts +15 -0
- package/dist/activities/agent-turn/optional-repositories.d.ts +59 -0
- package/dist/activities/agent-turn/provider-dispatch-barrier.d.ts +12 -1
- package/dist/activities/agent-turn/quiescence.d.ts +7 -7
- package/dist/activities/agent-turn/recovery-warning.d.ts +9 -0
- package/dist/activities/agent-turn/refresh-credentials.d.ts +6 -0
- package/dist/activities/agent-turn/run-credentials.d.ts +8 -3
- package/dist/activities/agent-turn/sandbox-establish.d.ts +2 -1
- package/dist/activities/agent-turn/sandbox-provision.d.ts +3 -1
- package/dist/activities/agent-turn/sandbox-route.d.ts +3 -20
- package/dist/activities/agent-turn/sandbox-runtime.d.ts +2 -2
- package/dist/activities/agent-turn/session-title.d.ts +34 -2
- package/dist/activities/agent-turn/skill-checkout.d.ts +25 -6
- package/dist/activities/agent-turn/skill-read.d.ts +70 -2
- package/dist/activities/agent-turn/skill-remove.d.ts +14 -0
- package/dist/activities/agent-turn/skill-search.d.ts +3 -1
- package/dist/activities/agent-turn/skill-tools.d.ts +15 -2
- package/dist/activities/agent-turn/skill-transfer.d.ts +18 -6
- package/dist/activities/agent-turn/stream-attempt.d.ts +7 -3
- package/dist/activities/agent-turn/subscription-lease.d.ts +38 -0
- package/dist/activities/agent-turn/tool-environment.d.ts +56 -6
- package/dist/activities/agent-turn/tool-policy.d.ts +43 -0
- package/dist/activities/agent-turn/turn-context.d.ts +29 -0
- package/dist/activities/agent-turn/turn-sandbox-access.d.ts +3 -0
- package/dist/activities/agent-turn/xai-capacity.d.ts +2 -1
- package/dist/activities/automations.d.ts +3 -1
- package/dist/activities/browser-deadline-checkpoint.d.ts +29 -0
- package/dist/activities/codemode-dispatcher.d.ts +9 -2
- package/dist/activities/codex-capacity.d.ts +18 -2
- package/dist/activities/codex-rotation.d.ts +7 -1
- package/dist/activities/context-compaction.d.ts +16 -2
- package/dist/activities/gmail-files.d.ts +32 -0
- package/dist/activities/goals.d.ts +8 -0
- package/dist/activities/knowledge-indexing.d.ts +19 -0
- package/dist/activities/mcp-account-routes.d.ts +3 -0
- package/dist/activities/mcp-credentials.d.ts +12 -13
- package/dist/activities/mcp-operation-authority.d.ts +0 -5
- package/dist/activities/model-call-fact-reconciler.d.ts +34 -0
- package/dist/activities/open-suffix-resume.d.ts +13 -0
- package/dist/activities/parent-wake.d.ts +6 -12
- package/dist/activities/retained-screenshots.d.ts +7 -2
- package/dist/activities/rig-verification.d.ts +2 -3
- package/dist/activities/run-credential-renewal.d.ts +1 -0
- package/dist/activities/run-credentials.d.ts +15 -3
- package/dist/activities/sandbox-images.d.ts +1 -1
- package/dist/activities/sandbox-lease.d.ts +47 -9
- package/dist/activities/scheduled-tasks.d.ts +4 -35
- package/dist/activities/session-state.d.ts +70 -3
- package/dist/activities/types.d.ts +55 -6
- package/dist/activities/workspace-capture.d.ts +2 -0
- package/dist/activities/workspace-credential-provider.d.ts +46 -0
- package/dist/{activities-control-HMFGSKC5.js → activities-control-BNPTRTOE.js} +1896 -418
- package/dist/activities-control-BNPTRTOE.js.map +1 -0
- package/dist/activities-control.d.ts +182 -4
- package/dist/{activities-turn-AP67JADS.js → activities-turn-5LGZOSVU.js} +4476 -1327
- package/dist/activities-turn-5LGZOSVU.js.map +1 -0
- package/dist/activities.d.ts +182 -4
- package/dist/artifact-materializer-entry.js +5 -2
- package/dist/artifact-materializer-entry.js.map +1 -1
- package/dist/artifact-outbox-entry.js +5 -2
- package/dist/artifact-outbox-entry.js.map +1 -1
- package/dist/browser-deadline-checkpoint-contract.d.ts +2 -0
- package/dist/{chunk-A4XQWVZK.js → chunk-3ZTRWDMN.js} +307 -4
- package/dist/chunk-3ZTRWDMN.js.map +1 -0
- package/dist/{chunk-RAMNYSAW.js → chunk-4ER3YXOT.js} +22 -4
- package/dist/chunk-4ER3YXOT.js.map +1 -0
- package/dist/{chunk-CYITU5UH.js → chunk-LJX7Z2KP.js} +1445 -343
- package/dist/chunk-LJX7Z2KP.js.map +1 -0
- package/dist/editable-artifact-materializer-service.d.ts +2 -2
- package/dist/editable-artifact-outbox-service.d.ts +2 -2
- package/dist/host-export-pump.d.ts +3 -1
- package/dist/host-export.d.ts +1 -1
- package/dist/host-export.js +1 -1
- package/dist/http.d.ts +1 -1
- package/dist/index.d.ts +95 -3
- package/dist/index.js +137 -43
- package/dist/index.js.map +1 -1
- package/dist/observability-metrics.d.ts +118 -8
- package/dist/retained-process-diagnostics.d.ts +4 -0
- package/dist/retained-process-retry.d.ts +7 -0
- package/dist/sandbox-resume.d.ts +55 -13
- package/dist/sandbox-routing.d.ts +5 -3
- package/dist/sandbox-snapshot-diagnostics.d.ts +20 -0
- package/dist/session-event-fanout.d.ts +6 -0
- package/dist/warm-capture-holder.d.ts +3 -0
- package/dist/worker-service-lifecycle.d.ts +12 -0
- package/dist/workflow-bundle.js +273 -41
- package/dist/workflows/activities.d.ts +1 -1
- package/dist/workflows/browser-deadline-checkpoint.d.ts +9 -0
- package/dist/workflows/scheduled-tasks.d.ts +4 -0
- package/dist/workflows/session.d.ts +36 -0
- package/dist/workflows.d.ts +1 -0
- package/package.json +21 -19
- package/src/activities/agent-run-admission.ts +17 -1
- package/src/activities/agent-turn/admission.ts +66 -3
- package/src/activities/agent-turn/agent-build.ts +84 -10
- package/src/activities/agent-turn/claim.ts +186 -43
- package/src/activities/agent-turn/claude-usage-observer.ts +221 -0
- package/src/activities/agent-turn/code-search.ts +275 -0
- package/src/activities/agent-turn/codex-capacity.ts +172 -8
- package/src/activities/agent-turn/codex-plan-entitlement.ts +254 -0
- package/src/activities/agent-turn/codex.ts +13 -5
- package/src/activities/agent-turn/compaction-prep.ts +170 -102
- package/src/activities/agent-turn/credential-leases.ts +122 -216
- package/src/activities/agent-turn/credential-restriction.ts +14 -0
- package/src/activities/agent-turn/errors.ts +647 -48
- package/src/activities/agent-turn/failure-settlement.ts +682 -130
- package/src/activities/agent-turn/final-reply.ts +46 -0
- package/src/activities/agent-turn/finalization-monitor.ts +120 -0
- package/src/activities/agent-turn/finalization.ts +146 -43
- package/src/activities/agent-turn/github-repository-bindings.ts +3 -1
- package/src/activities/agent-turn/governance-model.ts +69 -28
- package/src/activities/agent-turn/history-sink.ts +16 -2
- package/src/activities/agent-turn/history.ts +38 -11
- package/src/activities/agent-turn/input-wait-reply.ts +103 -0
- package/src/activities/agent-turn/knowledge-source-tools.ts +2 -0
- package/src/activities/agent-turn/media-artifacts.ts +2 -1
- package/src/activities/agent-turn/model-call-admission.ts +94 -0
- package/src/activities/agent-turn/model-usage.ts +64 -3
- package/src/activities/agent-turn/model-wire-dispatch.ts +27 -0
- package/src/activities/agent-turn/optional-repositories.ts +224 -0
- package/src/activities/agent-turn/provider-dispatch-barrier.ts +22 -0
- package/src/activities/agent-turn/quiescence.ts +10 -7
- package/src/activities/agent-turn/recovery-warning.ts +16 -0
- package/src/activities/agent-turn/refresh-credentials.ts +44 -0
- package/src/activities/agent-turn/run-credentials.ts +116 -55
- package/src/activities/agent-turn/run.ts +320 -93
- package/src/activities/agent-turn/sandbox-establish.ts +84 -8
- package/src/activities/agent-turn/sandbox-provision.ts +3 -1
- package/src/activities/agent-turn/sandbox-route.ts +4 -21
- package/src/activities/agent-turn/sandbox-runtime.ts +343 -18
- package/src/activities/agent-turn/session-title.ts +81 -5
- package/src/activities/agent-turn/skill-checkout.ts +111 -24
- package/src/activities/agent-turn/skill-install.ts +1 -1
- package/src/activities/agent-turn/skill-read.ts +374 -28
- package/src/activities/agent-turn/skill-remove.ts +46 -0
- package/src/activities/agent-turn/skill-save.ts +1 -1
- package/src/activities/agent-turn/skill-search.ts +10 -1
- package/src/activities/agent-turn/skill-selection.ts +4 -0
- package/src/activities/agent-turn/skill-tools.ts +112 -18
- package/src/activities/agent-turn/skill-transfer.ts +68 -23
- package/src/activities/agent-turn/stream-attempt.ts +313 -77
- package/src/activities/agent-turn/subscription-lease.ts +115 -0
- package/src/activities/agent-turn/tool-environment.ts +316 -167
- package/src/activities/agent-turn/tool-policy.ts +80 -1
- package/src/activities/agent-turn/turn-context.ts +35 -0
- package/src/activities/agent-turn/turn-sandbox-access.ts +7 -1
- package/src/activities/agent-turn/xai-capacity.ts +94 -51
- package/src/activities/automations.ts +76 -4
- package/src/activities/browser-deadline-checkpoint.ts +279 -0
- package/src/activities/capabilities.ts +108 -11
- package/src/activities/codemode-dispatcher.ts +36 -8
- package/src/activities/codex-capacity.ts +60 -25
- package/src/activities/codex-rotation.ts +39 -17
- package/src/activities/context-compaction.ts +75 -18
- package/src/activities/gmail-files.ts +88 -0
- package/src/activities/goals.ts +116 -121
- package/src/activities/google-drive-publication.ts +1 -3
- package/src/activities/image-generation-operation.ts +1 -1
- package/src/activities/knowledge-indexing.ts +269 -15
- package/src/activities/knowledge-source-sync.ts +10 -0
- package/src/activities/mcp-account-routes.ts +17 -0
- package/src/activities/mcp-credentials.ts +83 -194
- package/src/activities/mcp-operation-authority.ts +16 -41
- package/src/activities/model-call-fact-reconciler.ts +112 -0
- package/src/activities/open-suffix-resume.ts +15 -2
- package/src/activities/parent-wake.ts +46 -21
- package/src/activities/retained-screenshots.ts +82 -11
- package/src/activities/rig-verification.ts +38 -37
- package/src/activities/run-credential-renewal.ts +13 -1
- package/src/activities/run-credentials.ts +59 -17
- package/src/activities/run-input.ts +25 -16
- package/src/activities/sandbox-images.ts +0 -1
- package/src/activities/sandbox-lease.ts +798 -82
- package/src/activities/scheduled-tasks.ts +550 -178
- package/src/activities/session-state.ts +245 -6
- package/src/activities/site-auth-maintenance.ts +3 -0
- package/src/activities/types.ts +72 -5
- package/src/activities/workflow-wake.ts +2 -17
- package/src/activities/workspace-capture.ts +13 -2
- package/src/activities/workspace-credential-provider.ts +283 -0
- package/src/activities-control.ts +4 -0
- package/src/activities-turn.ts +5 -0
- package/src/activities.ts +13 -0
- package/src/activity-services.ts +16 -4
- package/src/browser-deadline-checkpoint-contract.ts +5 -0
- package/src/editable-artifact-hint-broker.ts +3 -0
- package/src/editable-artifact-materializer-service.ts +7 -3
- package/src/editable-artifact-outbox-service.ts +4 -4
- package/src/github-rest-mcp.ts +1 -2
- package/src/host-export-pump.ts +26 -4
- package/src/host-export.ts +3 -0
- package/src/http.ts +4 -2
- package/src/index.ts +99 -37
- package/src/observability-metrics.ts +510 -5
- package/src/personal-github-git-credentials.ts +2 -1
- package/src/pr-review-credentials.ts +1 -3
- package/src/retained-process-diagnostics.ts +50 -0
- package/src/retained-process-retry.ts +36 -0
- package/src/sandbox-resume.ts +388 -101
- package/src/sandbox-routing.ts +150 -25
- package/src/sandbox-snapshot-diagnostics.ts +112 -0
- package/src/session-event-fanout.ts +23 -0
- package/src/warm-capture-holder.ts +15 -0
- package/src/worker-service-lifecycle.ts +53 -0
- package/src/workflows/activities.ts +16 -1
- package/src/workflows/browser-deadline-checkpoint.ts +50 -0
- package/src/workflows/file-upload-reaper.ts +2 -0
- package/src/workflows/scheduled-tasks.ts +26 -13
- package/src/workflows/session.ts +242 -22
- package/src/workflows.ts +4 -0
- package/dist/activities/agent-turn/knowledge-sources.d.ts +0 -20
- package/dist/activities/packs.d.ts +0 -55
- package/dist/activities-control-HMFGSKC5.js.map +0 -1
- package/dist/activities-turn-AP67JADS.js.map +0 -1
- package/dist/chunk-A4XQWVZK.js.map +0 -1
- package/dist/chunk-CYITU5UH.js.map +0 -1
- package/dist/chunk-RAMNYSAW.js.map +0 -1
- package/src/activities/agent-turn/knowledge-sources.ts +0 -86
- package/src/activities/packs.ts +0 -166
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { environmentsEncryptionKeyBytes, type Settings } from "@opengeni/config";
|
|
2
|
+
import {
|
|
3
|
+
CredentialProviderResponse,
|
|
4
|
+
OPENGENI_SIGNATURE_HEADER,
|
|
5
|
+
signOpenGeniPayload,
|
|
6
|
+
type CredentialProviderRequest,
|
|
7
|
+
type RunCredentialsRequest,
|
|
8
|
+
type RunCredentialsResolution,
|
|
9
|
+
} from "@opengeni/contracts";
|
|
10
|
+
import {
|
|
11
|
+
decryptEnvironmentValue,
|
|
12
|
+
resolveWorkspaceCredentialProvider,
|
|
13
|
+
resolveInitiatingHuman,
|
|
14
|
+
type Database,
|
|
15
|
+
} from "@opengeni/db";
|
|
16
|
+
import { pinnedFetch, type OutboundNetworkSettings } from "@opengeni/network";
|
|
17
|
+
|
|
18
|
+
const MAX_RESPONSE_BYTES = 5 * 1024 * 1024;
|
|
19
|
+
export const GIT_CREDENTIALS_FILE = "git/credentials";
|
|
20
|
+
export const GIT_CREDENTIALS_FILE_ENV = "OPENGENI_GIT_CREDENTIALS_FILE";
|
|
21
|
+
|
|
22
|
+
export class CredentialProviderError extends Error {
|
|
23
|
+
constructor(message: string) {
|
|
24
|
+
super(message);
|
|
25
|
+
this.name = "CredentialProviderError";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type WorkspaceRunCredentialResolver = (
|
|
30
|
+
input: RunCredentialsRequest,
|
|
31
|
+
) => Promise<RunCredentialsResolution>;
|
|
32
|
+
|
|
33
|
+
type ProviderOk = Extract<CredentialProviderResponse, { status: "ok" }>;
|
|
34
|
+
|
|
35
|
+
function gitCredentialLine(entry: NonNullable<ProviderOk["git"]>[number]): string {
|
|
36
|
+
const username = encodeURIComponent(entry.username ?? "x-access-token");
|
|
37
|
+
return `https://${username}:${encodeURIComponent(entry.password)}@${entry.host.toLowerCase()}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Turn the provider's `git` sugar into ordinary run-credential material: one
|
|
42
|
+
* renewable credential-store file plus a read-only helper configured through
|
|
43
|
+
* GIT_CONFIG_* environment entries appended after any the host already set.
|
|
44
|
+
*/
|
|
45
|
+
export function withGitCredentialHelper(response: ProviderOk): ProviderOk {
|
|
46
|
+
if (!response.git?.length) return response;
|
|
47
|
+
const environment = { ...response.environment };
|
|
48
|
+
const existingCount = Number.parseInt(environment.GIT_CONFIG_COUNT ?? "0", 10);
|
|
49
|
+
const index = Number.isFinite(existingCount) && existingCount > 0 ? existingCount : 0;
|
|
50
|
+
environment[`GIT_CONFIG_KEY_${index}`] = "credential.helper";
|
|
51
|
+
environment[`GIT_CONFIG_VALUE_${index}`] =
|
|
52
|
+
`!f() { test "$1" = get && sed '/^path=/d' | git credential-store --file="$${GIT_CREDENTIALS_FILE_ENV}" get; }; f`;
|
|
53
|
+
environment.GIT_CONFIG_COUNT = String(index + 1);
|
|
54
|
+
return {
|
|
55
|
+
...response,
|
|
56
|
+
environment,
|
|
57
|
+
files: [
|
|
58
|
+
...(response.files ?? []),
|
|
59
|
+
{
|
|
60
|
+
path: GIT_CREDENTIALS_FILE,
|
|
61
|
+
content: `${response.git.map(gitCredentialLine).join("\n")}\n`,
|
|
62
|
+
mode: "0400",
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
fileEnvironment: {
|
|
66
|
+
...response.fileEnvironment,
|
|
67
|
+
[GIT_CREDENTIALS_FILE_ENV]: GIT_CREDENTIALS_FILE,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function credentialProviderRequestBody(
|
|
73
|
+
input: RunCredentialsRequest,
|
|
74
|
+
initiatingHumanSubjectId: string | null,
|
|
75
|
+
initiatingHuman: CredentialProviderRequest["initiatingHuman"] = null,
|
|
76
|
+
selection: Pick<CredentialProviderRequest, "lane" | "mcpServers"> = {
|
|
77
|
+
lane: "workspace",
|
|
78
|
+
mcpServers: [],
|
|
79
|
+
},
|
|
80
|
+
): CredentialProviderRequest {
|
|
81
|
+
const via = input.initiatorContext.via;
|
|
82
|
+
const latestHop = Array.isArray(via) ? via.at(-1) : null;
|
|
83
|
+
return {
|
|
84
|
+
type: "credentials.request",
|
|
85
|
+
lane: selection.lane,
|
|
86
|
+
mcpServers: selection.mcpServers.map(({ id, url }) => ({ id, url })),
|
|
87
|
+
purpose: input.purpose,
|
|
88
|
+
forceRefresh: input.forceRefresh,
|
|
89
|
+
accountId: input.accountId,
|
|
90
|
+
workspaceId: input.workspaceId,
|
|
91
|
+
sessionId: input.sessionId,
|
|
92
|
+
rootSessionId: input.rootSessionId,
|
|
93
|
+
parentSessionId: input.parentSessionId,
|
|
94
|
+
turnId: input.turnId,
|
|
95
|
+
attemptId: input.attemptId,
|
|
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
|
+
},
|
|
107
|
+
initiatingHumanSubjectId,
|
|
108
|
+
initiatingHuman,
|
|
109
|
+
sandboxBackend: input.effectiveSandboxBackend,
|
|
110
|
+
sandboxOs: input.sandboxOs,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Drop undefined-valued keys; the runtime normalizer re-validates everything. */
|
|
115
|
+
function wire<T>(value: unknown): T {
|
|
116
|
+
return JSON.parse(JSON.stringify(value)) as T;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function toRunCredentialsResolution(
|
|
120
|
+
response: CredentialProviderResponse,
|
|
121
|
+
scope: { accountId: string; workspaceId: string; sessionId: string },
|
|
122
|
+
): RunCredentialsResolution {
|
|
123
|
+
if (response.status === "not_applicable") return { status: "not_applicable", ...scope };
|
|
124
|
+
if (response.status === "auth_needed") {
|
|
125
|
+
return wire({ status: "auth_needed", ...scope, authNeeded: response.authNeeded });
|
|
126
|
+
}
|
|
127
|
+
const { git: _git, ...material } = withGitCredentialHelper(response);
|
|
128
|
+
return wire({ ...material, status: "ok", ...scope, environment: material.environment ?? {} });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function readBoundedText(response: Response): Promise<string> {
|
|
132
|
+
const declared = Number(response.headers.get("content-length"));
|
|
133
|
+
if (Number.isFinite(declared) && declared > MAX_RESPONSE_BYTES) {
|
|
134
|
+
throw new CredentialProviderError("credential provider response is too large");
|
|
135
|
+
}
|
|
136
|
+
const reader = response.body?.getReader();
|
|
137
|
+
if (!reader) return "";
|
|
138
|
+
const chunks: Uint8Array[] = [];
|
|
139
|
+
let total = 0;
|
|
140
|
+
for (;;) {
|
|
141
|
+
const { done, value } = await reader.read();
|
|
142
|
+
if (done) break;
|
|
143
|
+
total += value.byteLength;
|
|
144
|
+
if (total > MAX_RESPONSE_BYTES) {
|
|
145
|
+
await reader.cancel().catch(() => undefined);
|
|
146
|
+
throw new CredentialProviderError("credential provider response is too large");
|
|
147
|
+
}
|
|
148
|
+
chunks.push(value);
|
|
149
|
+
}
|
|
150
|
+
return new TextDecoder().decode(Buffer.concat(chunks));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export type WorkspaceCredentialProviderDeps = {
|
|
154
|
+
fetch?: typeof pinnedFetch;
|
|
155
|
+
resolveProvider?: typeof resolveWorkspaceCredentialProvider;
|
|
156
|
+
resolveHuman?: typeof resolveInitiatingHuman;
|
|
157
|
+
/** Trusted selected session-attached remote targets, frozen by the worker. */
|
|
158
|
+
mcpServers?: readonly { id: string; url: string }[];
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Resolve the workspace's configured HTTP credential provider, if any. The
|
|
163
|
+
* returned resolver has the same contract as a host `runCredentials` port, so
|
|
164
|
+
* provisioning, file materialization, renewal, and reconnect notices reuse the
|
|
165
|
+
* existing run-credential lifecycle unchanged.
|
|
166
|
+
*/
|
|
167
|
+
export async function workspaceCredentialProviderResolver(
|
|
168
|
+
db: Database,
|
|
169
|
+
settings: Settings,
|
|
170
|
+
scope: { accountId: string; workspaceId: string },
|
|
171
|
+
initiatingHumanSubjectId: string | null,
|
|
172
|
+
deps: WorkspaceCredentialProviderDeps = {},
|
|
173
|
+
): Promise<WorkspaceRunCredentialResolver | null> {
|
|
174
|
+
const row = await (deps.resolveProvider ?? resolveWorkspaceCredentialProvider)(db, scope);
|
|
175
|
+
if (!row) return null;
|
|
176
|
+
if (!row.enabled) {
|
|
177
|
+
// A disabled workspace override is an explicit pause, not an absent
|
|
178
|
+
// provider. Return a resolver so the deployment port cannot be borrowed.
|
|
179
|
+
return async (input) => ({
|
|
180
|
+
status: "not_applicable",
|
|
181
|
+
accountId: input.accountId,
|
|
182
|
+
workspaceId: input.workspaceId,
|
|
183
|
+
sessionId: input.sessionId,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
const key = environmentsEncryptionKeyBytes(settings);
|
|
187
|
+
if (!key) {
|
|
188
|
+
throw new CredentialProviderError(
|
|
189
|
+
"OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY is required to use a workspace credential provider",
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
const fetchImpl = deps.fetch ?? pinnedFetch;
|
|
193
|
+
const network: OutboundNetworkSettings = settings;
|
|
194
|
+
const selection = {
|
|
195
|
+
lane:
|
|
196
|
+
"workspaceId" in row && row.workspaceId !== null
|
|
197
|
+
? ("workspace" as const)
|
|
198
|
+
: ("organization" as const),
|
|
199
|
+
mcpServers: (deps.mcpServers ?? []).map(({ id, url }) => ({ id, url })),
|
|
200
|
+
};
|
|
201
|
+
return async (input) => {
|
|
202
|
+
const echo = {
|
|
203
|
+
accountId: input.accountId,
|
|
204
|
+
workspaceId: input.workspaceId,
|
|
205
|
+
sessionId: input.sessionId,
|
|
206
|
+
};
|
|
207
|
+
// Rotation and disabling take effect on the next outbound request. Never
|
|
208
|
+
// switch registration mid-attempt if deletion exposes an inherited row.
|
|
209
|
+
const current = await (deps.resolveProvider ?? resolveWorkspaceCredentialProvider)(db, scope);
|
|
210
|
+
if (!current?.enabled || current.id !== row.id) {
|
|
211
|
+
return { status: "not_applicable", ...echo };
|
|
212
|
+
}
|
|
213
|
+
const secret = decryptEnvironmentValue(key, current.secretEncrypted);
|
|
214
|
+
const initiatingHuman = await (deps.resolveHuman ?? resolveInitiatingHuman)(
|
|
215
|
+
db,
|
|
216
|
+
scope,
|
|
217
|
+
initiatingHumanSubjectId,
|
|
218
|
+
input.turnId,
|
|
219
|
+
);
|
|
220
|
+
const body = JSON.stringify(
|
|
221
|
+
credentialProviderRequestBody(input, initiatingHumanSubjectId, initiatingHuman, selection),
|
|
222
|
+
);
|
|
223
|
+
let status: number;
|
|
224
|
+
let text: string;
|
|
225
|
+
try {
|
|
226
|
+
const response = await fetchImpl(
|
|
227
|
+
current.url,
|
|
228
|
+
{
|
|
229
|
+
method: "POST",
|
|
230
|
+
headers: {
|
|
231
|
+
"content-type": "application/json",
|
|
232
|
+
"user-agent": "OpenGeni-CredentialProvider/1",
|
|
233
|
+
[OPENGENI_SIGNATURE_HEADER]: await signOpenGeniPayload(secret, body),
|
|
234
|
+
},
|
|
235
|
+
body,
|
|
236
|
+
signal: AbortSignal.timeout(current.timeoutMs),
|
|
237
|
+
},
|
|
238
|
+
network,
|
|
239
|
+
{ label: "Workspace credential provider", requireHttpsOutsideLocalTest: true },
|
|
240
|
+
);
|
|
241
|
+
status = response.status;
|
|
242
|
+
text = await readBoundedText(response);
|
|
243
|
+
} catch (error) {
|
|
244
|
+
return failure(input, echo, `request failed: ${errorName(error)}`);
|
|
245
|
+
}
|
|
246
|
+
if (status < 200 || status >= 300) {
|
|
247
|
+
return failure(input, echo, `returned HTTP ${status}`);
|
|
248
|
+
}
|
|
249
|
+
let parsed: CredentialProviderResponse;
|
|
250
|
+
try {
|
|
251
|
+
parsed = CredentialProviderResponse.parse(JSON.parse(text));
|
|
252
|
+
} catch {
|
|
253
|
+
return failure(input, echo, "returned an invalid response body");
|
|
254
|
+
}
|
|
255
|
+
return toRunCredentialsResolution(parsed, echo);
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function errorName(error: unknown): string {
|
|
260
|
+
if (error instanceof Error) {
|
|
261
|
+
return error.name === "TimeoutError" || error.name === "AbortError" ? "timeout" : error.name;
|
|
262
|
+
}
|
|
263
|
+
return "unknown";
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* A failed renewal throws so the renewal loop keeps the last good material and
|
|
268
|
+
* retries with backoff. A failed first provision degrades to a visible
|
|
269
|
+
* reconnect notice instead of blocking the turn.
|
|
270
|
+
*/
|
|
271
|
+
function failure(
|
|
272
|
+
input: RunCredentialsRequest,
|
|
273
|
+
echo: { accountId: string; workspaceId: string; sessionId: string },
|
|
274
|
+
reason: string,
|
|
275
|
+
): RunCredentialsResolution {
|
|
276
|
+
const message = `Workspace credential provider ${reason}`;
|
|
277
|
+
if (input.purpose === "renewal") throw new CredentialProviderError(message);
|
|
278
|
+
return {
|
|
279
|
+
status: "auth_needed",
|
|
280
|
+
...echo,
|
|
281
|
+
authNeeded: [{ reason: "refresh_failed", message }],
|
|
282
|
+
};
|
|
283
|
+
}
|
|
@@ -2,10 +2,12 @@ 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";
|
|
8
9
|
import { createGoalActivities } from "./activities/goals";
|
|
10
|
+
import { createModelCallFactReconcilerActivities } from "./activities/model-call-fact-reconciler";
|
|
9
11
|
import { createKnowledgeSourceSyncActivities } from "./activities/knowledge-source-sync";
|
|
10
12
|
import { createRetainedScreenshotMaintenanceActivities } from "./activities/retained-screenshot-reaper";
|
|
11
13
|
import { createRigVerificationActivities } from "./activities/rig-verification";
|
|
@@ -39,7 +41,9 @@ export function createControlActivitiesFromServices(
|
|
|
39
41
|
...createCodexCapacityActivities(services),
|
|
40
42
|
...createRigVerificationActivities(services),
|
|
41
43
|
...createBrowserStateArtifactMaintenanceActivities(services),
|
|
44
|
+
...createBrowserDeadlineCheckpointActivities(services),
|
|
42
45
|
...createFileUploadReaperActivities(services),
|
|
46
|
+
...createModelCallFactReconcilerActivities(services),
|
|
43
47
|
...createRetainedScreenshotMaintenanceActivities(services),
|
|
44
48
|
...createWorkflowWakeActivities(services),
|
|
45
49
|
...createSandboxLeaseActivities(services),
|
package/src/activities-turn.ts
CHANGED
|
@@ -12,6 +12,9 @@ function createTurnActivityServices(
|
|
|
12
12
|
let servicesPromise: Promise<TurnActivityServices> | null = null;
|
|
13
13
|
return async () => {
|
|
14
14
|
servicesPromise ??= (async () => {
|
|
15
|
+
if (!dependencies.requestWorkerDrain) {
|
|
16
|
+
throw new Error("Turn activities require a host requestWorkerDrain lifecycle edge");
|
|
17
|
+
}
|
|
15
18
|
const services = await shared();
|
|
16
19
|
return {
|
|
17
20
|
...services,
|
|
@@ -20,6 +23,8 @@ function createTurnActivityServices(
|
|
|
20
23
|
createProductionAgentRuntime({
|
|
21
24
|
metrics: runtimeMetricsHooksForObservability(services.observability),
|
|
22
25
|
}),
|
|
26
|
+
requestWorkerDrain: dependencies.requestWorkerDrain,
|
|
27
|
+
turnFinalizationTimeoutMs: dependencies.turnFinalizationTimeoutMs,
|
|
23
28
|
summarizeContextForCompaction:
|
|
24
29
|
dependencies.summarizeContextForCompaction ?? summarizeForCompaction,
|
|
25
30
|
};
|
package/src/activities.ts
CHANGED
|
@@ -69,6 +69,12 @@ export function createActivityTestHarness(dependencies: ActivityDependencies = {
|
|
|
69
69
|
createProductionAgentRuntime({
|
|
70
70
|
metrics: runtimeMetricsHooksForObservability(common.observability),
|
|
71
71
|
}),
|
|
72
|
+
requestWorkerDrain:
|
|
73
|
+
dependencies.requestWorkerDrain ??
|
|
74
|
+
(() => {
|
|
75
|
+
throw new Error("Test host must provide requestWorkerDrain for stalled cleanup");
|
|
76
|
+
}),
|
|
77
|
+
turnFinalizationTimeoutMs: dependencies.turnFinalizationTimeoutMs,
|
|
72
78
|
summarizeContextForCompaction:
|
|
73
79
|
dependencies.summarizeContextForCompaction ?? summarizeForCompaction,
|
|
74
80
|
documentServices: dependencies.documentServices ?? createDocumentServices(common.settings),
|
|
@@ -99,6 +105,8 @@ export const persistSessionAttemptQuiescence =
|
|
|
99
105
|
defaultControlActivities.persistSessionAttemptQuiescence;
|
|
100
106
|
export const reconcileSessionAttemptQuiescence =
|
|
101
107
|
defaultControlActivities.reconcileSessionAttemptQuiescence;
|
|
108
|
+
export const reconcileSettledSessionAttempt =
|
|
109
|
+
defaultControlActivities.reconcileSettledSessionAttempt;
|
|
102
110
|
export const recoverDispatch = defaultControlActivities.recoverDispatch;
|
|
103
111
|
export const recoverEscapedMcpTimeout = defaultControlActivities.recoverEscapedMcpTimeout;
|
|
104
112
|
export const peekSessionWork = defaultControlActivities.peekSessionWork;
|
|
@@ -106,6 +114,7 @@ export const settleSessionInputWait = defaultControlActivities.settleSessionInpu
|
|
|
106
114
|
export const expireSessionHumanInput = defaultControlActivities.expireSessionHumanInput;
|
|
107
115
|
export const expireSessionInteractionIntervention =
|
|
108
116
|
defaultControlActivities.expireSessionInteractionIntervention;
|
|
117
|
+
export const expireScheduledRunHumanWait = defaultControlActivities.expireScheduledRunHumanWait;
|
|
109
118
|
export const markSessionIdle = defaultControlActivities.markSessionIdle;
|
|
110
119
|
export const dispatchScheduledTaskRun = defaultControlActivities.dispatchScheduledTaskRun;
|
|
111
120
|
export const dispatchAutomationRun = defaultControlActivities.dispatchAutomationRun;
|
|
@@ -116,6 +125,9 @@ export const maybeContinueGoal = defaultControlActivities.maybeContinueGoal;
|
|
|
116
125
|
export const getCodexCapacityWait = defaultControlActivities.getCodexCapacityWait;
|
|
117
126
|
export const reconcileCodexCapacityWait = defaultControlActivities.reconcileCodexCapacityWait;
|
|
118
127
|
export const prepareSandboxLeaseSweep = defaultControlActivities.prepareSandboxLeaseSweep;
|
|
128
|
+
export const listDueBrowserCheckpoints = defaultControlActivities.listDueBrowserCheckpoints;
|
|
129
|
+
export const checkpointBrowserBeforeDeadline =
|
|
130
|
+
defaultControlActivities.checkpointBrowserBeforeDeadline;
|
|
119
131
|
export const drainSandboxLease = defaultControlActivities.drainSandboxLease;
|
|
120
132
|
export const maintainSandboxLeaseSweep = defaultControlActivities.maintainSandboxLeaseSweep;
|
|
121
133
|
export const reapSandboxLeases = defaultControlActivities.reapSandboxLeases;
|
|
@@ -124,6 +136,7 @@ export const recoverVideoGenerationWorkflows =
|
|
|
124
136
|
defaultControlActivities.recoverVideoGenerationWorkflows;
|
|
125
137
|
export const maintainRetainedScreenshots = defaultControlActivities.maintainRetainedScreenshots;
|
|
126
138
|
export const maintainBrowserStateArtifacts = defaultControlActivities.maintainBrowserStateArtifacts;
|
|
139
|
+
export const reconcileRecentModelCallFacts = defaultControlActivities.reconcileRecentModelCallFacts;
|
|
127
140
|
export const maintainSiteAuthConnections = defaultControlActivities.maintainSiteAuthConnections;
|
|
128
141
|
export const dispatchSessionWorkflowWakes = defaultControlActivities.dispatchSessionWorkflowWakes;
|
|
129
142
|
export const verifyRigChange = defaultControlActivities.verifyRigChange;
|
package/src/activity-services.ts
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
codeSearchDeploymentPolicy,
|
|
3
|
+
dbSearchPath,
|
|
4
|
+
getSettings,
|
|
5
|
+
resolveNatsControlPlaneAuth,
|
|
6
|
+
} from "@opengeni/config";
|
|
7
|
+
import {
|
|
8
|
+
configureChildLifecycleNotices,
|
|
9
|
+
configureCodeSearchDeploymentPolicy,
|
|
10
|
+
createDb,
|
|
11
|
+
} from "@opengeni/db";
|
|
3
12
|
import { createNatsEventBus } from "@opengeni/events";
|
|
4
13
|
import { createObservability } from "@opengeni/observability";
|
|
5
14
|
import { resolveCatalogSettings } from "@opengeni/core";
|
|
6
15
|
import { createObjectStorage } from "@opengeni/storage";
|
|
7
16
|
import type { ActivityDependencies, SharedActivityServices } from "./activities/types";
|
|
8
|
-
import {
|
|
17
|
+
import { observabilityEventBusOptions } from "./observability-metrics";
|
|
9
18
|
import { buildPersonalGitHubGitCredentials } from "./personal-github-git-credentials";
|
|
10
19
|
import { createStandaloneConnectionCredentialsPort } from "./pr-review-credentials";
|
|
11
20
|
|
|
@@ -40,6 +49,9 @@ export function createSharedActivityServices(
|
|
|
40
49
|
// Child lifecycle notice producers are process-global in @opengeni/db;
|
|
41
50
|
// install the boot-validated rollout flag once for this worker.
|
|
42
51
|
configureChildLifecycleNotices({ enabled: settings.childLifecycleNoticesEnabled });
|
|
52
|
+
// Child, scheduled and automation sessions created here freeze their
|
|
53
|
+
// code_search decision from the same deployment policy as the API.
|
|
54
|
+
configureCodeSearchDeploymentPolicy(codeSearchDeploymentPolicy(settings));
|
|
43
55
|
return {
|
|
44
56
|
settings: resolvedSettings,
|
|
45
57
|
catalogSourceSettings: settings,
|
|
@@ -51,7 +63,7 @@ export function createSharedActivityServices(
|
|
|
51
63
|
controlPlaneAuth
|
|
52
64
|
? { user: controlPlaneAuth.user, pass: controlPlaneAuth.password }
|
|
53
65
|
: undefined,
|
|
54
|
-
|
|
66
|
+
observabilityEventBusOptions(observability),
|
|
55
67
|
)),
|
|
56
68
|
objectStorage: dependencies.objectStorage ?? createObjectStorage(settings),
|
|
57
69
|
observability,
|
|
@@ -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
|
+
}
|
|
@@ -161,6 +161,9 @@ export async function connectEditableArtifactHintBroker(
|
|
|
161
161
|
name,
|
|
162
162
|
reconnect: true,
|
|
163
163
|
maxReconnectAttempts: -1,
|
|
164
|
+
// Same policy as the event bus: a broker or auth-callout restart must not
|
|
165
|
+
// permanently close this connection after two identical auth errors.
|
|
166
|
+
ignoreAuthErrorAbort: true,
|
|
164
167
|
reconnectTimeWait: 2_000,
|
|
165
168
|
reconnectJitter: 1_000,
|
|
166
169
|
reconnectJitterTLS: 1_000,
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
dbSearchPath,
|
|
3
|
+
getArtifactMaterializerSettings,
|
|
4
|
+
type ArtifactMaterializerSettings,
|
|
5
|
+
} from "@opengeni/config";
|
|
2
6
|
import {
|
|
3
7
|
createDb,
|
|
4
8
|
dbSql,
|
|
@@ -77,7 +81,7 @@ export type CreateEditableArtifactMaterializerServiceInput = Readonly<{
|
|
|
77
81
|
}>;
|
|
78
82
|
|
|
79
83
|
export type ProductionEditableArtifactMaterializerServiceOptions = Readonly<{
|
|
80
|
-
settings:
|
|
84
|
+
settings: ArtifactMaterializerSettings;
|
|
81
85
|
/** Dedicated DSN; never defaults to settings.databaseUrl. */
|
|
82
86
|
databaseUrl: string;
|
|
83
87
|
declaredDatabaseRole: string;
|
|
@@ -671,7 +675,7 @@ export async function createMaterializerSidecarFromEnvironment(
|
|
|
671
675
|
}> | null> {
|
|
672
676
|
const sidecar = readEditableArtifactMaterializerSidecarEnvironment(environment);
|
|
673
677
|
if (!sidecar) return null;
|
|
674
|
-
const settings =
|
|
678
|
+
const settings = getArtifactMaterializerSettings(environment);
|
|
675
679
|
const service = await createProductionEditableArtifactMaterializerService({
|
|
676
680
|
settings,
|
|
677
681
|
databaseUrl: sidecar.databaseUrl,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
dbSearchPath,
|
|
3
|
-
|
|
3
|
+
getArtifactOutboxSettings,
|
|
4
4
|
resolveNatsControlPlaneAuth,
|
|
5
|
-
type
|
|
5
|
+
type ArtifactOutboxSettings,
|
|
6
6
|
} from "@opengeni/config";
|
|
7
7
|
import type { EditableArtifactOutboxDispatchSummary } from "@opengeni/core";
|
|
8
8
|
import { createObservability, type Observability } from "@opengeni/observability";
|
|
@@ -51,7 +51,7 @@ export type CreateEditableArtifactOutboxServiceInput = Readonly<{
|
|
|
51
51
|
}>;
|
|
52
52
|
|
|
53
53
|
export type ProductionEditableArtifactOutboxServiceOptions = Readonly<{
|
|
54
|
-
settings:
|
|
54
|
+
settings: ArtifactOutboxSettings;
|
|
55
55
|
databaseUrl: string;
|
|
56
56
|
declaredDatabaseRole: string;
|
|
57
57
|
natsUrl: string;
|
|
@@ -360,7 +360,7 @@ export async function createOutboxSidecarFromEnvironment(
|
|
|
360
360
|
): Promise<Readonly<{ service: EditableArtifactOutboxService; httpPort: number }> | null> {
|
|
361
361
|
const sidecar = readEditableArtifactOutboxSidecarEnvironment(environment);
|
|
362
362
|
if (!sidecar) return null;
|
|
363
|
-
const settings =
|
|
363
|
+
const settings = getArtifactOutboxSettings(environment);
|
|
364
364
|
const controlPlaneAuth = resolveNatsControlPlaneAuth(settings);
|
|
365
365
|
const natsAuth: EditableArtifactHintNatsAuth = controlPlaneAuth
|
|
366
366
|
? {
|
package/src/github-rest-mcp.ts
CHANGED
|
@@ -113,7 +113,7 @@ export async function buildGitHubRestMcpForTurn(input: {
|
|
|
113
113
|
resolveAuthority: appResolver,
|
|
114
114
|
}),
|
|
115
115
|
});
|
|
116
|
-
configs.push(internalMcpConfig(GITHUB_REST_MCP_APP_SERVER_ID, "GitHub —
|
|
116
|
+
configs.push(internalMcpConfig(GITHUB_REST_MCP_APP_SERVER_ID, "GitHub — Opengeni bot"));
|
|
117
117
|
toolRefs.push({ kind: "mcp", id: GITHUB_REST_MCP_APP_SERVER_ID });
|
|
118
118
|
connectorBindings.push(
|
|
119
119
|
...githubConnectorBindings(GITHUB_REST_MCP_APP_SERVER_ID, appRepositories),
|
|
@@ -519,7 +519,6 @@ function exactPersonalDelegation(
|
|
|
519
519
|
delegation.providerDomain !== "github.com" ||
|
|
520
520
|
delegation.kind !== "oauth2" ||
|
|
521
521
|
!delegation.originWorkspaceId ||
|
|
522
|
-
!delegation.userDelegation ||
|
|
523
522
|
!delegation.personalGitHubRepositorySelection
|
|
524
523
|
) {
|
|
525
524
|
throw new GitHubRestAuthorityError("Personal GitHub delegation is unavailable");
|
package/src/host-export-pump.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { HostEventSink, HostUsageSink } from "@opengeni/contracts";
|
|
1
|
+
import type { HostEventSink, HostLifecycleFactSink, HostUsageSink } from "@opengeni/contracts";
|
|
2
2
|
import {
|
|
3
3
|
acknowledgeHostExportBatch,
|
|
4
4
|
claimHostExportBatch,
|
|
@@ -35,6 +35,8 @@ export type HostExportPumpOptions = {
|
|
|
35
35
|
db: Database;
|
|
36
36
|
eventSink?: HostEventSink;
|
|
37
37
|
usageSink?: HostUsageSink;
|
|
38
|
+
/** Content-free per-person product lifecycle facts (`lifecycle_fact`). */
|
|
39
|
+
lifecycleSink?: HostLifecycleFactSink;
|
|
38
40
|
observability?: Observability;
|
|
39
41
|
instanceId?: string;
|
|
40
42
|
pollIntervalMs?: number;
|
|
@@ -77,7 +79,7 @@ export type HostExportPump = {
|
|
|
77
79
|
* keys. Sink implementations must therefore deduplicate those keys.
|
|
78
80
|
*/
|
|
79
81
|
export function createHostExportPump(options: HostExportPumpOptions): HostExportPump {
|
|
80
|
-
if (!options.eventSink && !options.usageSink) {
|
|
82
|
+
if (!options.eventSink && !options.usageSink && !options.lifecycleSink) {
|
|
81
83
|
throw new Error("createHostExportPump requires at least one host sink");
|
|
82
84
|
}
|
|
83
85
|
const instanceId = validateInstanceId(
|
|
@@ -102,7 +104,9 @@ export function createHostExportPump(options: HostExportPumpOptions): HostExport
|
|
|
102
104
|
const configuredConsumer = (kind: HostExportKind): string | null =>
|
|
103
105
|
kind === "session_event"
|
|
104
106
|
? (options.eventSink?.consumerId ?? null)
|
|
105
|
-
:
|
|
107
|
+
: kind === "usage_event"
|
|
108
|
+
? (options.usageSink?.consumerId ?? null)
|
|
109
|
+
: (options.lifecycleSink?.consumerId ?? null);
|
|
106
110
|
|
|
107
111
|
const recordResult = (result: HostExportDrainResult, durationSeconds: number) => {
|
|
108
112
|
const labels = { kind: result.kind, outcome: result.outcome };
|
|
@@ -242,7 +246,7 @@ export function createHostExportPump(options: HostExportPumpOptions): HostExport
|
|
|
242
246
|
...common,
|
|
243
247
|
});
|
|
244
248
|
if (batch) return await deliverClaim(batch.events.length, () => sink.deliverEvents(batch));
|
|
245
|
-
} else {
|
|
249
|
+
} else if (kind === "usage_event") {
|
|
246
250
|
const sink = options.usageSink;
|
|
247
251
|
if (!sink) return { kind, outcome: "not_configured" };
|
|
248
252
|
const batch = await claimHostExportBatch(options.db, {
|
|
@@ -250,6 +254,16 @@ export function createHostExportPump(options: HostExportPumpOptions): HostExport
|
|
|
250
254
|
...common,
|
|
251
255
|
});
|
|
252
256
|
if (batch) return await deliverClaim(batch.events.length, () => sink.deliverUsage(batch));
|
|
257
|
+
} else {
|
|
258
|
+
const sink = options.lifecycleSink;
|
|
259
|
+
if (!sink) return { kind, outcome: "not_configured" };
|
|
260
|
+
const batch = await claimHostExportBatch(options.db, {
|
|
261
|
+
kind,
|
|
262
|
+
...common,
|
|
263
|
+
});
|
|
264
|
+
if (batch) {
|
|
265
|
+
return await deliverClaim(batch.events.length, () => sink.deliverLifecycleFacts(batch));
|
|
266
|
+
}
|
|
253
267
|
}
|
|
254
268
|
} catch (error) {
|
|
255
269
|
return await settleFailure(error);
|
|
@@ -276,6 +290,7 @@ export function createHostExportPump(options: HostExportPumpOptions): HostExport
|
|
|
276
290
|
const results: HostExportDrainResult[] = [];
|
|
277
291
|
if (options.eventSink) results.push(await drainKind("session_event"));
|
|
278
292
|
if (options.usageSink) results.push(await drainKind("usage_event"));
|
|
293
|
+
if (options.lifecycleSink) results.push(await drainKind("lifecycle_fact"));
|
|
279
294
|
return results;
|
|
280
295
|
};
|
|
281
296
|
|
|
@@ -326,9 +341,16 @@ export function createHostExportPump(options: HostExportPumpOptions): HostExport
|
|
|
326
341
|
consumerId: options.usageSink.consumerId,
|
|
327
342
|
});
|
|
328
343
|
}
|
|
344
|
+
if (options.lifecycleSink) {
|
|
345
|
+
await registerHostExportConsumer(scoped, {
|
|
346
|
+
kind: "lifecycle_fact",
|
|
347
|
+
consumerId: options.lifecycleSink.consumerId,
|
|
348
|
+
});
|
|
349
|
+
}
|
|
329
350
|
});
|
|
330
351
|
if (options.eventSink) retiringKinds.delete("session_event");
|
|
331
352
|
if (options.usageSink) retiringKinds.delete("usage_event");
|
|
353
|
+
if (options.lifecycleSink) retiringKinds.delete("lifecycle_fact");
|
|
332
354
|
running = true;
|
|
333
355
|
loopPromise = (async () => {
|
|
334
356
|
while (true) {
|
package/src/host-export.ts
CHANGED
package/src/http.ts
CHANGED
|
@@ -120,6 +120,7 @@ export async function runReadinessChecks(
|
|
|
120
120
|
export function dbReadyCheck(
|
|
121
121
|
db: Database,
|
|
122
122
|
posture?: RuntimeDatabasePostureOptions,
|
|
123
|
+
afterDatabaseReady?: () => Promise<void>,
|
|
123
124
|
): () => Promise<void> {
|
|
124
125
|
let inFlight: Promise<void> | undefined;
|
|
125
126
|
return () => {
|
|
@@ -130,9 +131,10 @@ export function dbReadyCheck(
|
|
|
130
131
|
inFlight = (async () => {
|
|
131
132
|
if (posture) {
|
|
132
133
|
await assertRuntimeDatabasePosture(db, posture);
|
|
133
|
-
|
|
134
|
+
} else {
|
|
135
|
+
await db.execute(dbSql`select 1`);
|
|
134
136
|
}
|
|
135
|
-
await
|
|
137
|
+
await afterDatabaseReady?.();
|
|
136
138
|
})().finally(() => {
|
|
137
139
|
inFlight = undefined;
|
|
138
140
|
});
|