@cat-factory/server 0.316.0 → 0.318.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.
Files changed (47) hide show
  1. package/dist/agents/ContainerAgentExecutor.d.ts +24 -20
  2. package/dist/agents/ContainerAgentExecutor.d.ts.map +1 -1
  3. package/dist/agents/ContainerAgentExecutor.js +61 -113
  4. package/dist/agents/ContainerAgentExecutor.js.map +1 -1
  5. package/dist/agents/ContainerEnvironmentProbeAgent.d.ts +95 -29
  6. package/dist/agents/ContainerEnvironmentProbeAgent.d.ts.map +1 -1
  7. package/dist/agents/ContainerEnvironmentProbeAgent.js +177 -61
  8. package/dist/agents/ContainerEnvironmentProbeAgent.js.map +1 -1
  9. package/dist/agents/ModelRouter.d.ts +40 -4
  10. package/dist/agents/ModelRouter.d.ts.map +1 -1
  11. package/dist/agents/ModelRouter.js +1 -1
  12. package/dist/agents/ModelRouter.js.map +1 -1
  13. package/dist/agents/containerJobAccounting.d.ts +10 -3
  14. package/dist/agents/containerJobAccounting.d.ts.map +1 -1
  15. package/dist/agents/containerJobAccounting.js +11 -4
  16. package/dist/agents/containerJobAccounting.js.map +1 -1
  17. package/dist/agents/containerJobAuth.d.ts +146 -0
  18. package/dist/agents/containerJobAuth.d.ts.map +1 -0
  19. package/dist/agents/containerJobAuth.js +171 -0
  20. package/dist/agents/containerJobAuth.js.map +1 -0
  21. package/dist/agents/dispatchPromptContext.d.ts +30 -0
  22. package/dist/agents/dispatchPromptContext.d.ts.map +1 -0
  23. package/dist/agents/dispatchPromptContext.js +23 -0
  24. package/dist/agents/dispatchPromptContext.js.map +1 -0
  25. package/dist/agents/singleKindModel.d.ts +46 -0
  26. package/dist/agents/singleKindModel.d.ts.map +1 -0
  27. package/dist/agents/singleKindModel.js +59 -0
  28. package/dist/agents/singleKindModel.js.map +1 -0
  29. package/dist/agents/testCredentials.d.ts +25 -0
  30. package/dist/agents/testCredentials.d.ts.map +1 -0
  31. package/dist/agents/testCredentials.js +23 -0
  32. package/dist/agents/testCredentials.js.map +1 -0
  33. package/dist/index.d.ts +3 -0
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +5 -0
  36. package/dist/index.js.map +1 -1
  37. package/dist/modules/environments/EnvironmentController.d.ts.map +1 -1
  38. package/dist/modules/environments/EnvironmentController.js +39 -2
  39. package/dist/modules/environments/EnvironmentController.js.map +1 -1
  40. package/dist/persistence/rpc-allowlist.d.ts.map +1 -1
  41. package/dist/persistence/rpc-allowlist.js +9 -0
  42. package/dist/persistence/rpc-allowlist.js.map +1 -1
  43. package/dist/runtime/keyDrift.js +1 -1
  44. package/dist/runtime/keyDrift.js.map +1 -1
  45. package/dist/runtime/platformHealth.js +4 -2
  46. package/dist/runtime/platformHealth.js.map +1 -1
  47. package/package.json +9 -9
@@ -0,0 +1,146 @@
1
+ import type { HarnessKind, ModelRef, SubscriptionVendor } from '@cat-factory/kernel';
2
+ import type { ContainerSessionService } from '../containers/ContainerSessionService.js';
3
+ /** A subscription token leased from the workspace's pool for a vendor. */
4
+ export interface LeasedSubscriptionToken {
5
+ tokenId: string;
6
+ secret: string;
7
+ }
8
+ /** Lease the least-loaded subscription token for a vendor, or throw if none. */
9
+ export type LeaseSubscriptionToken = (workspaceId: string, vendor: SubscriptionVendor) => Promise<LeasedSubscriptionToken>;
10
+ /**
11
+ * Lease the run-initiator's OWN activated personal credential for an individual-usage
12
+ * vendor (Claude). Scoped to the run + user (not pooled); throws a
13
+ * `CredentialRequiredError` when the run has no live activation (the user must re-enter
14
+ * their password). Returns just the raw secret and no token id, since there is no pool
15
+ * rotation/usage to attribute for a single-user credential.
16
+ */
17
+ export type LeasePersonalSubscriptionToken = (executionId: string, userId: string, vendor: SubscriptionVendor) => Promise<{
18
+ secret: string;
19
+ }>;
20
+ export interface ContainerJobAuthDependencies {
21
+ /** Mints the signed LLM-proxy session token the container uses (Pi harness). */
22
+ sessionService: ContainerSessionService;
23
+ /**
24
+ * Public base URL of the facade's OpenAI-compatible LLM proxy, including the
25
+ * `/v1` suffix: Pi posts to `${proxyBaseUrl}/chat/completions`.
26
+ */
27
+ proxyBaseUrl: string;
28
+ /**
29
+ * Resolve a workspace's owning account id, signed into the proxy session token so the
30
+ * proxy can lease an account-scoped API key from the merged pool. Optional; absent ⇒
31
+ * only the workspace + initiator scopes are leased.
32
+ */
33
+ resolveAccountId?: (workspaceId: string) => Promise<string | null | undefined>;
34
+ /**
35
+ * Lease a pooled subscription token for a vendor. Required for the Claude Code /
36
+ * Codex subscription harnesses; absent ⇒ those harnesses are unavailable and a
37
+ * subscription-only model fails loudly at dispatch.
38
+ */
39
+ leaseSubscriptionToken?: LeaseSubscriptionToken;
40
+ /**
41
+ * Lease the run-initiator's personal (individual-usage) credential for a vendor like
42
+ * Claude. Required to run an individual-usage model; absent ⇒ such models fail loudly
43
+ * at dispatch (the per-user personal store isn't wired on this deployment).
44
+ */
45
+ leasePersonalSubscriptionToken?: LeasePersonalSubscriptionToken;
46
+ /**
47
+ * Whether the WORKSPACE holds a pooled token for a vendor, and whether the USER holds their own
48
+ * personal one. Optional; absent ⇒ {@link ContainerJobAuthResolver.describeAuthGap} reports no
49
+ * gap it cannot see, and the lease itself is still the authority.
50
+ *
51
+ * They sit here as well as on the model router because they answer the same question at two
52
+ * moments, and only this module can turn the answer into a REFUSAL: the router asks "should a
53
+ * dual-mode model switch to its subscription flavour?", the gap check asks "is there a credential
54
+ * to open the harness this model already names?". A subscription-ONLY model (Codex) carries its
55
+ * harness whatever the pool holds, so nothing in the routing can notice the missing token: it
56
+ * surfaces as a throw from the lease, which on a dry run costs a branch, a provision and a
57
+ * teardown to reach.
58
+ */
59
+ hasSubscriptionToken?: (workspaceId: string, vendor: SubscriptionVendor) => Promise<boolean>;
60
+ hasPersonalSubscription?: (userId: string, vendor: SubscriptionVendor) => Promise<boolean>;
61
+ /**
62
+ * NATIVE LOCAL EXECUTION (local facade only, opt-in via `LOCAL_NATIVE_AGENTS`): when this
63
+ * returns true for a resolved subscription harness + vendor, the job carries
64
+ * `ambientAuth: true` INSTEAD of a leased credential. The harness (run as a host process)
65
+ * drives the developer's OWN installed `claude` / `codex` CLI with its ambient login. No
66
+ * token is leased and no personal-credential gate applies. It is passed the resolved
67
+ * `vendor` precisely so it can refuse a non-native vendor that merely REUSES the
68
+ * `claude-code` harness (GLM/Kimi/DeepSeek): those carry an Anthropic-compatible
69
+ * `subscriptionBaseUrl`, which ambient auth would silently drop, running the step on the
70
+ * developer's own Anthropic login instead of the pinned vendor. Default off everywhere
71
+ * else, so the Cloudflare/Node leasing paths are untouched.
72
+ */
73
+ nativeAmbientAuth?: (harness: HarnessKind, vendor: SubscriptionVendor | undefined) => boolean;
74
+ }
75
+ /**
76
+ * WHICH credential channel a dispatch needs: the harness the resolved model names, the vendor
77
+ * behind it, and whose it would be.
78
+ *
79
+ * Split out of {@link ContainerJobAuthRequest} because {@link
80
+ * ContainerJobAuthResolver.describeAuthGap} answers from exactly this much and no more. Asking it
81
+ * for a whole request would have an admission check invent an `executionId` for a run that does not
82
+ * exist yet, and a fabricated id is a thing later code reads as one.
83
+ */
84
+ export interface ContainerJobAuthSubject {
85
+ harness: HarnessKind;
86
+ subscriptionVendor: SubscriptionVendor | undefined;
87
+ workspaceId: string;
88
+ /**
89
+ * Whoever started the work. An individual-usage credential belongs to one person, so its lease
90
+ * cannot be resolved without one.
91
+ */
92
+ initiatedByUserId?: string | null;
93
+ }
94
+ /** One dispatch's identity, as the auth channels need it. */
95
+ export interface ContainerJobAuthRequest extends ContainerJobAuthSubject {
96
+ ref: ModelRef;
97
+ /**
98
+ * The correlation id the container's model calls are metered under. A pipeline step passes its
99
+ * run id; a single-job flow passes the id of the row that IS its run, which is also the id its
100
+ * personal-credential activation was minted against.
101
+ */
102
+ executionId: string;
103
+ /** The kind the spend is filed under, signed into the proxy session token. */
104
+ agentKind: string;
105
+ }
106
+ /** What a dispatch spreads into its job body, plus the pooled token id it must attribute back. */
107
+ export interface ContainerJobAuth {
108
+ auth: Record<string, unknown>;
109
+ subscriptionTokenId?: string;
110
+ }
111
+ /**
112
+ * Resolve the per-job auth the harness carries: the proxy session token for Pi, or a
113
+ * leased subscription token for Claude Code / Codex. Spread into every job body so the
114
+ * per-kind bodies (and the single-job flows) can't drift on which auth they forward.
115
+ */
116
+ export declare class ContainerJobAuthResolver {
117
+ private readonly deps;
118
+ constructor(deps: ContainerJobAuthDependencies);
119
+ /**
120
+ * Why this dispatch's credential could not be resolved, or `null` when it can be, WITHOUT
121
+ * leasing anything.
122
+ *
123
+ * For a flow that can be refused before it spends: the environment dry run asks it at admission,
124
+ * where "this deployment has no Codex subscription connected" is a sentence an operator can act
125
+ * on, rather than at the dispatch, where the same fact arrives as a failed run with a branch and
126
+ * a provisioned environment behind it.
127
+ *
128
+ * It lives beside {@link resolve} rather than in the caller so the two cannot disagree: every
129
+ * refusal below is one `resolve` would have thrown, in the same words. It is deliberately NOT a
130
+ * guarantee (a token can be revoked between the check and the lease, and a personal activation
131
+ * is minted after it), so the lease stays the authority and this is only the cheap early no.
132
+ */
133
+ describeAuthGap(subject: ContainerJobAuthSubject): Promise<string | null>;
134
+ /**
135
+ * The three refusals {@link describeAuthGap} and {@link resolve} share, worded ONCE.
136
+ *
137
+ * Shared because the whole value of the gap check is that admission refuses for the reason the
138
+ * dispatch would have: two spellings of "no Codex subscription connected" is how an operator
139
+ * comes to fix the thing the earlier message named and hit the later one.
140
+ */
141
+ private harnessUnconfigured;
142
+ private personalStoreUnconfigured;
143
+ private noIdentifiedInitiator;
144
+ resolve(request: ContainerJobAuthRequest): Promise<ContainerJobAuth>;
145
+ }
146
+ //# sourceMappingURL=containerJobAuth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"containerJobAuth.d.ts","sourceRoot":"","sources":["../../src/agents/containerJobAuth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAMpF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAA;AAiBvF,0EAA0E;AAC1E,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;CACf;AAED,gFAAgF;AAChF,MAAM,MAAM,sBAAsB,GAAG,CACnC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,kBAAkB,KACvB,OAAO,CAAC,uBAAuB,CAAC,CAAA;AAErC;;;;;;GAMG;AACH,MAAM,MAAM,8BAA8B,GAAG,CAC3C,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,kBAAkB,KACvB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAEhC,MAAM,WAAW,4BAA4B;IAC3C,gFAAgF;IAChF,cAAc,EAAE,uBAAuB,CAAA;IACvC;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAA;IAC9E;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,sBAAsB,CAAA;IAC/C;;;;OAIG;IACH,8BAA8B,CAAC,EAAE,8BAA8B,CAAA;IAC/D;;;;;;;;;;;;OAYG;IACH,oBAAoB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAC5F,uBAAuB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAC1F;;;;;;;;;;;OAWG;IACH,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,kBAAkB,GAAG,SAAS,KAAK,OAAO,CAAA;CAC9F;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,WAAW,CAAA;IACpB,kBAAkB,EAAE,kBAAkB,GAAG,SAAS,CAAA;IAClD,WAAW,EAAE,MAAM,CAAA;IACnB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAClC;AAED,6DAA6D;AAC7D,MAAM,WAAW,uBAAwB,SAAQ,uBAAuB;IACtE,GAAG,EAAE,QAAQ,CAAA;IACb;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB,8EAA8E;IAC9E,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,kGAAkG;AAClG,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAC7B;AAED;;;;GAIG;AACH,qBAAa,wBAAwB;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAjC,YAA6B,IAAI,EAAE,4BAA4B,EAAI;IAEnE;;;;;;;;;;;;;OAaG;IACG,eAAe,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAoC9E;IAED;;;;;;OAMG;IACH,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,yBAAyB;IAOjC,OAAO,CAAC,qBAAqB;IAIvB,OAAO,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA4FzE;CACF"}
@@ -0,0 +1,171 @@
1
+ import { CredentialRequiredError, SUBSCRIPTION_VENDORS, isIndividualVendor, } from '@cat-factory/kernel';
2
+ /**
3
+ * Resolve the per-job auth the harness carries: the proxy session token for Pi, or a
4
+ * leased subscription token for Claude Code / Codex. Spread into every job body so the
5
+ * per-kind bodies (and the single-job flows) can't drift on which auth they forward.
6
+ */
7
+ export class ContainerJobAuthResolver {
8
+ deps;
9
+ constructor(deps) {
10
+ this.deps = deps;
11
+ }
12
+ /**
13
+ * Why this dispatch's credential could not be resolved, or `null` when it can be, WITHOUT
14
+ * leasing anything.
15
+ *
16
+ * For a flow that can be refused before it spends: the environment dry run asks it at admission,
17
+ * where "this deployment has no Codex subscription connected" is a sentence an operator can act
18
+ * on, rather than at the dispatch, where the same fact arrives as a failed run with a branch and
19
+ * a provisioned environment behind it.
20
+ *
21
+ * It lives beside {@link resolve} rather than in the caller so the two cannot disagree: every
22
+ * refusal below is one `resolve` would have thrown, in the same words. It is deliberately NOT a
23
+ * guarantee (a token can be revoked between the check and the lease, and a personal activation
24
+ * is minted after it), so the lease stays the authority and this is only the cheap early no.
25
+ */
26
+ async describeAuthGap(subject) {
27
+ const { harness, subscriptionVendor, workspaceId } = subject;
28
+ // The Pi harness mints its own proxy session token, so there is nothing to be short of.
29
+ if (harness === 'pi')
30
+ return null;
31
+ if (this.deps.nativeAmbientAuth?.(harness, subscriptionVendor))
32
+ return null;
33
+ if (!subscriptionVendor)
34
+ return this.harnessUnconfigured(harness);
35
+ if (isIndividualVendor(subscriptionVendor)) {
36
+ if (!this.deps.leasePersonalSubscriptionToken) {
37
+ return this.personalStoreUnconfigured(subscriptionVendor);
38
+ }
39
+ const userId = subject.initiatedByUserId ?? undefined;
40
+ if (!userId)
41
+ return this.noIdentifiedInitiator(subscriptionVendor);
42
+ // The password-backed ACTIVATION is minted per run and cannot exist yet; what is checkable
43
+ // now is whether this person has the subscription at all.
44
+ if (this.deps.hasPersonalSubscription &&
45
+ !(await this.deps.hasPersonalSubscription(userId, subscriptionVendor))) {
46
+ return (`Running a ${subscriptionVendor} model needs your own personal ${subscriptionVendor} ` +
47
+ `subscription, and none is connected to your account.`);
48
+ }
49
+ return null;
50
+ }
51
+ if (!this.deps.leaseSubscriptionToken)
52
+ return this.harnessUnconfigured(harness);
53
+ if (this.deps.hasSubscriptionToken &&
54
+ !(await this.deps.hasSubscriptionToken(workspaceId, subscriptionVendor))) {
55
+ return (`This model runs on a ${subscriptionVendor} subscription, and this workspace has no ` +
56
+ `${subscriptionVendor} subscription token connected. Connect one, or pick a different model.`);
57
+ }
58
+ return null;
59
+ }
60
+ /**
61
+ * The three refusals {@link describeAuthGap} and {@link resolve} share, worded ONCE.
62
+ *
63
+ * Shared because the whole value of the gap check is that admission refuses for the reason the
64
+ * dispatch would have: two spellings of "no Codex subscription connected" is how an operator
65
+ * comes to fix the thing the earlier message named and hit the later one.
66
+ */
67
+ harnessUnconfigured(harness) {
68
+ return (`The ${harness} harness is not configured on this deployment; connect a ` +
69
+ `subscription token or pick a different model.`);
70
+ }
71
+ personalStoreUnconfigured(vendor) {
72
+ return (`Personal ${vendor} subscriptions are not configured on this ` +
73
+ `deployment (no ENCRYPTION_KEY); pick a different model.`);
74
+ }
75
+ noIdentifiedInitiator(vendor) {
76
+ return `Running a ${vendor} model requires a signed-in user with a personal subscription.`;
77
+ }
78
+ async resolve(request) {
79
+ const { harness, ref, subscriptionVendor, workspaceId, executionId, agentKind } = request;
80
+ const initiatedByUserId = request.initiatedByUserId ?? undefined;
81
+ if (harness === 'pi') {
82
+ const accountId = this.deps.resolveAccountId
83
+ ? await this.deps.resolveAccountId(workspaceId)
84
+ : undefined;
85
+ const sessionToken = await this.deps.sessionService.mint({
86
+ workspaceId,
87
+ accountId: accountId ?? undefined,
88
+ userId: initiatedByUserId,
89
+ executionId,
90
+ agentKind,
91
+ provider: ref.provider,
92
+ model: ref.model,
93
+ });
94
+ // `proxyPhasePath` states what THIS backend serves: the phase-tagged completions route
95
+ // the harness tags Pi's base URL with, so each call is attributed to the run phase that
96
+ // spent it (`docs/initiatives/token-burn-instrumentation.md`). Unconditional: the route
97
+ // is part of `llmProxyController`, so any backend running this code has it. It is the
98
+ // harness that may be older or newer, and telling it what we serve is what keeps an
99
+ // image pinned by a runner pool (or `LOCAL_HARNESS_IMAGE`) from posting every model call
100
+ // to a 404. Same shape as `webSearch` on the step path: the backend declares, the harness
101
+ // points.
102
+ return {
103
+ auth: {
104
+ harness,
105
+ proxyBaseUrl: this.deps.proxyBaseUrl,
106
+ proxyPhasePath: true,
107
+ sessionToken,
108
+ },
109
+ };
110
+ }
111
+ // Native local execution: the harness runs the developer's own CLI with its ambient
112
+ // login, so we lease NOTHING and gate NOTHING, and only flag ambient auth for the harness.
113
+ // Passed the vendor so it can refuse a non-native vendor reusing the `claude-code`
114
+ // harness (GLM/Kimi/DeepSeek), whose subscriptionBaseUrl ambient auth would drop.
115
+ if (this.deps.nativeAmbientAuth?.(harness, subscriptionVendor)) {
116
+ return { auth: { harness, ambientAuth: true } };
117
+ }
118
+ if (!subscriptionVendor)
119
+ throw new Error(this.harnessUnconfigured(harness));
120
+ // Individual-usage vendors (Claude) are NOT pooled: lease the run-initiator's OWN
121
+ // activated personal credential. Pooled vendors (GLM/Kimi/DeepSeek/Codex) lease
122
+ // from the workspace pool. Either path hands the RAW credential to the resolved
123
+ // runner transport (see the trust note below).
124
+ let secret;
125
+ let subscriptionTokenId;
126
+ if (isIndividualVendor(subscriptionVendor)) {
127
+ if (!this.deps.leasePersonalSubscriptionToken) {
128
+ throw new Error(this.personalStoreUnconfigured(subscriptionVendor));
129
+ }
130
+ if (!initiatedByUserId) {
131
+ // No identified initiator (auth-disabled/local dev): an individual-usage
132
+ // credential is owned by a specific user and can't be resolved without one.
133
+ throw new CredentialRequiredError(this.noIdentifiedInitiator(subscriptionVendor), {
134
+ vendor: subscriptionVendor,
135
+ reason: 'no_subscription',
136
+ });
137
+ }
138
+ // Throws CredentialRequiredError(password_required) when the run has no live
139
+ // activation: the dispatch path surfaces it as a clear, retriable failure.
140
+ const leased = await this.deps.leasePersonalSubscriptionToken(executionId, initiatedByUserId, subscriptionVendor);
141
+ secret = leased.secret;
142
+ }
143
+ else {
144
+ if (!this.deps.leaseSubscriptionToken)
145
+ throw new Error(this.harnessUnconfigured(harness));
146
+ const leased = await this.deps.leaseSubscriptionToken(workspaceId, subscriptionVendor);
147
+ subscriptionTokenId = leased.tokenId;
148
+ secret = leased.secret;
149
+ }
150
+ // SECURITY/TRUST: unlike the Pi harness (short-lived, model-locked proxy session
151
+ // token) this hands the RAW, long-lived subscription credential (a Claude OAuth
152
+ // token or a full ChatGPT auth.json) to the resolved runner transport. For the
153
+ // Cloudflare backend that is an ephemeral, managed per-run container. For a
154
+ // self-hosted runner pool it is the WORKSPACE'S OWN BYO infra (it connected the
155
+ // pool), so the credential stays within the workspace's trust domain, but a
156
+ // workspace should only point its subscription-harness steps at a runner pool it
157
+ // operates, since the credential leaves the backend to reach it.
158
+ // Non-Anthropic Claude-Code vendors (GLM/Kimi/DeepSeek) need their Anthropic-
159
+ // compatible base URL; Anthropic itself uses the OAuth token against api.anthropic.com.
160
+ const baseUrl = SUBSCRIPTION_VENDORS[subscriptionVendor].baseUrl;
161
+ return {
162
+ auth: {
163
+ harness,
164
+ subscriptionToken: secret,
165
+ ...(baseUrl ? { subscriptionBaseUrl: baseUrl } : {}),
166
+ },
167
+ ...(subscriptionTokenId ? { subscriptionTokenId } : {}),
168
+ };
169
+ }
170
+ }
171
+ //# sourceMappingURL=containerJobAuth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"containerJobAuth.js","sourceRoot":"","sources":["../../src/agents/containerJobAuth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { AgentRunContext } from '@cat-factory/kernel';
2
+ import type { DesignImageDelivery } from '@cat-factory/kernel';
3
+ import type { ResolvedToolServers } from './toolServers.js';
4
+ import type { ResolvedTestCredentials } from './testCredentials.js';
5
+ /**
6
+ * The context the dispatch's PROMPTS are rendered from: the run context the engine built, plus
7
+ * the facts only the dispatch knows.
8
+ *
9
+ * Its own function because the list keeps growing and each entry is there for the same reason: the
10
+ * value depends on the RESOLVED harness, model or credential store, none of which the engine has
11
+ * in hand when it builds the context. The prompt and the agent-context snapshot must both describe
12
+ * the container that was actually started, so this is the one place a dispatch-level fact is
13
+ * layered on, rather than each consumer re-deriving it and one of them forgetting.
14
+ */
15
+ export declare function buildDispatchPromptContext(context: AgentRunContext, resolved: {
16
+ /** What MCP the resolved harness could actually be given, and what could not be wired. */
17
+ tools: ResolvedToolServers;
18
+ /** Whether the design pictures were attached, and the reason when they were not. */
19
+ designImageDelivery?: DesignImageDelivery | undefined;
20
+ /**
21
+ * The frame's test credentials as resolved HERE, from the read that produced the values this
22
+ * job carries. It overrides the engine's own earlier read of the same store, and normally says
23
+ * the same thing. When it does not, only this one describes the container the prompt is for,
24
+ * and the divergence is the one that matters: a store that answered at admission and failed at
25
+ * dispatch would otherwise advertise `$API_TOKEN` to an agent whose shell has no such
26
+ * variable, which the agent reads as a service rejecting a credential it was given.
27
+ */
28
+ testCredentials?: ResolvedTestCredentials | undefined;
29
+ }): AgentRunContext;
30
+ //# sourceMappingURL=dispatchPromptContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatchPromptContext.d.ts","sourceRoot":"","sources":["../../src/agents/dispatchPromptContext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAC9D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAC3D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAA;AAEnE;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE;IACR,0FAA0F;IAC1F,KAAK,EAAE,mBAAmB,CAAA;IAC1B,oFAAoF;IACpF,mBAAmB,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAA;IACrD;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,uBAAuB,GAAG,SAAS,CAAA;CACtD,GACA,eAAe,CAWjB"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The context the dispatch's PROMPTS are rendered from: the run context the engine built, plus
3
+ * the facts only the dispatch knows.
4
+ *
5
+ * Its own function because the list keeps growing and each entry is there for the same reason: the
6
+ * value depends on the RESOLVED harness, model or credential store, none of which the engine has
7
+ * in hand when it builds the context. The prompt and the agent-context snapshot must both describe
8
+ * the container that was actually started, so this is the one place a dispatch-level fact is
9
+ * layered on, rather than each consumer re-deriving it and one of them forgetting.
10
+ */
11
+ export function buildDispatchPromptContext(context, resolved) {
12
+ const { tools, designImageDelivery, testCredentials } = resolved;
13
+ return {
14
+ ...context,
15
+ ...(tools.toolServers.length ? { toolServers: tools.toolServers } : {}),
16
+ ...(tools.unavailableToolServers.length
17
+ ? { unavailableToolServers: tools.unavailableToolServers }
18
+ : {}),
19
+ ...(designImageDelivery ? { designImageDelivery } : {}),
20
+ ...(testCredentials ? { testSecrets: testCredentials.brief } : {}),
21
+ };
22
+ }
23
+ //# sourceMappingURL=dispatchPromptContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatchPromptContext.js","sourceRoot":"","sources":["../../src/agents/dispatchPromptContext.ts"],"names":[],"mappings":"AAKA;;;;;;;;;GASG;AACH,MAAM,UAAU,0BAA0B,CACxC,OAAwB,EACxB,QAcC;IAED,MAAM,EAAE,KAAK,EAAE,mBAAmB,EAAE,eAAe,EAAE,GAAG,QAAQ,CAAA;IAChE,OAAO;QACL,GAAG,OAAO;QACV,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,GAAG,CAAC,KAAK,CAAC,sBAAsB,CAAC,MAAM;YACrC,CAAC,CAAC,EAAE,sBAAsB,EAAE,KAAK,CAAC,sBAAsB,EAAE;YAC1D,CAAC,CAAC,EAAE,CAAC;QACP,GAAG,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,mBAAmB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,eAAe,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACnE,CAAA;AACH,CAAC"}
@@ -0,0 +1,46 @@
1
+ import type { BlockRepository, HarnessKind, LocalModelDeclarations, ModelFlavor, ModelRef, SubscriptionVendor } from '@cat-factory/kernel';
2
+ import { type ModelRouterDependencies } from './ModelRouter.js';
3
+ /** What one single-job dispatch runs: the model, the harness for it, and the vendor it leases. */
4
+ export interface SingleKindModel {
5
+ ref: ModelRef;
6
+ harness: HarnessKind;
7
+ subscriptionVendor?: SubscriptionVendor;
8
+ }
9
+ /** Resolve {@link SingleKindModel} for one dispatch. */
10
+ export type ResolveSingleKindModel = (input: {
11
+ workspaceId: string;
12
+ /** The frame (board block) the job runs against: whatever it pins, and its preset. */
13
+ blockId: string;
14
+ /** The kind whose preset entry decides the model, and whose spend the calls are filed under. */
15
+ agentKind: string;
16
+ /** Whoever started it: whose personal (individual-usage) subscription may be leased. */
17
+ initiatedByUserId?: string | null;
18
+ }) => Promise<SingleKindModel>;
19
+ export interface SingleKindModelResolverDependencies extends ModelRouterDependencies {
20
+ /** Read the frame's own model pins. `null` (a deleted frame) ⇒ nothing is pinned. */
21
+ blockRepository: Pick<BlockRepository, 'get'>;
22
+ /**
23
+ * The route ORDER the preset in force states. Resolved beside the model rather than left out,
24
+ * because the two come from one row: a preset that puts Bedrock ahead of a direct key means it
25
+ * for every dispatch its model reaches, and a single-job flow that skipped it would run the
26
+ * preset's model over a route the preset ranked last.
27
+ */
28
+ resolvePresetProviderPreference?: (workspaceId: string, modelPresetId?: string) => Promise<readonly ModelFlavor[] | undefined>;
29
+ /**
30
+ * The INITIATOR's local-runner declarations, folded onto a resolved local ref by the shared
31
+ * resolver (a local model has no catalog entry to carry its modality). Threaded for the reason
32
+ * {@link import('./ModelRouter.js').ModelRouter.resolveRef} documents: ONE resolution serves
33
+ * every path, so a workspace preset naming an Ollama model must not resolve to a ref with the
34
+ * modality folded on for a pipeline step and left off for a single-job dispatch at the same
35
+ * frame. Absent ⇒ no local runners are consulted, which is the honest answer for a flow with no
36
+ * identified initiator.
37
+ */
38
+ resolveLocalModelDeclarations?: (userId: string) => Promise<readonly LocalModelDeclarations[] | undefined>;
39
+ }
40
+ /**
41
+ * Build the shared single-job model resolution: the frame's pins and the preset in force, folded
42
+ * through the same {@link ModelRouter} the pipeline dispatch uses, so a dry run and a coder step on
43
+ * the same frame cannot disagree about which model the workspace chose.
44
+ */
45
+ export declare function buildSingleKindModelResolver(deps: SingleKindModelResolverDependencies): ResolveSingleKindModel;
46
+ //# sourceMappingURL=singleKindModel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"singleKindModel.d.ts","sourceRoot":"","sources":["../../src/agents/singleKindModel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,WAAW,EACX,sBAAsB,EACtB,WAAW,EACX,QAAQ,EACR,kBAAkB,EACnB,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAAe,KAAK,uBAAuB,EAAE,MAAM,kBAAkB,CAAA;AAwB5E,kGAAkG;AAClG,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,QAAQ,CAAA;IACb,OAAO,EAAE,WAAW,CAAA;IACpB,kBAAkB,CAAC,EAAE,kBAAkB,CAAA;CACxC;AAED,wDAAwD;AACxD,MAAM,MAAM,sBAAsB,GAAG,CAAC,KAAK,EAAE;IAC3C,WAAW,EAAE,MAAM,CAAA;IACnB,sFAAsF;IACtF,OAAO,EAAE,MAAM,CAAA;IACf,gGAAgG;IAChG,SAAS,EAAE,MAAM,CAAA;IACjB,wFAAwF;IACxF,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAClC,KAAK,OAAO,CAAC,eAAe,CAAC,CAAA;AAE9B,MAAM,WAAW,mCAAoC,SAAQ,uBAAuB;IAClF,qFAAqF;IACrF,eAAe,EAAE,IAAI,CAAC,eAAe,EAAE,KAAK,CAAC,CAAA;IAC7C;;;;;OAKG;IACH,+BAA+B,CAAC,EAAE,CAChC,WAAW,EAAE,MAAM,EACnB,aAAa,CAAC,EAAE,MAAM,KACnB,OAAO,CAAC,SAAS,WAAW,EAAE,GAAG,SAAS,CAAC,CAAA;IAChD;;;;;;;;OAQG;IACH,6BAA6B,CAAC,EAAE,CAC9B,MAAM,EAAE,MAAM,KACX,OAAO,CAAC,SAAS,sBAAsB,EAAE,GAAG,SAAS,CAAC,CAAA;CAC5D;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,mCAAmC,GACxC,sBAAsB,CA0BxB"}
@@ -0,0 +1,59 @@
1
+ import { describeError } from '@cat-factory/kernel';
2
+ import { ModelRouter } from './ModelRouter.js';
3
+ import { logger } from '../observability/logger.js';
4
+ /**
5
+ * Build the shared single-job model resolution: the frame's pins and the preset in force, folded
6
+ * through the same {@link ModelRouter} the pipeline dispatch uses, so a dry run and a coder step on
7
+ * the same frame cannot disagree about which model the workspace chose.
8
+ */
9
+ export function buildSingleKindModelResolver(deps) {
10
+ const router = new ModelRouter(deps);
11
+ return async (input) => {
12
+ const block = await readFrame(deps, input.workspaceId, input.blockId);
13
+ const providerPreference = deps.resolvePresetProviderPreference
14
+ ? await deps.resolvePresetProviderPreference(input.workspaceId, block?.modelPresetId)
15
+ : undefined;
16
+ const localModelDeclarations = deps.resolveLocalModelDeclarations && input.initiatedByUserId
17
+ ? await deps.resolveLocalModelDeclarations(input.initiatedByUserId)
18
+ : undefined;
19
+ return router.resolveDispatchRef({
20
+ agentKind: input.agentKind,
21
+ workspaceId: input.workspaceId,
22
+ ...(input.initiatedByUserId ? { initiatedByUserId: input.initiatedByUserId } : {}),
23
+ ...(providerPreference?.length ? { providerPreference } : {}),
24
+ ...(localModelDeclarations?.length ? { localModelDeclarations } : {}),
25
+ block: {
26
+ ...(block?.modelId ? { modelId: block.modelId } : {}),
27
+ ...(block?.modelPresetId ? { modelPresetId: block.modelPresetId } : {}),
28
+ },
29
+ }, input.workspaceId);
30
+ };
31
+ }
32
+ /**
33
+ * The frame, or NOTHING, on any answer this read can give.
34
+ *
35
+ * A frame that cannot be read pins nothing, which is the same statement as a frame that pins
36
+ * nothing: the workspace's default preset then decides. Deliberately not an error, and swallowed
37
+ * on a THROW as well as on a `null`: the two are the same fact through different channels, and in
38
+ * mothership mode this read crosses `/internal/persistence`, so a transient RPC failure is the
39
+ * likelier of the pair. The read is here to HONOUR a pin; refusing a diagnostic because a
40
+ * projection lagged (or a repository blinked) would trade a resolvable model for a run that failed
41
+ * at `probing` with an environment already standing.
42
+ *
43
+ * Logged rather than silent, because a pin that stopped being honoured is otherwise invisible: the
44
+ * dispatch succeeds on the preset's model and nothing says the frame asked for another.
45
+ */
46
+ async function readFrame(deps, workspaceId, blockId) {
47
+ try {
48
+ return await deps.blockRepository.get(workspaceId, blockId);
49
+ }
50
+ catch (error) {
51
+ logger.warn('single-job dispatch: the frame could not be read, so its model pin is ignored', {
52
+ workspaceId,
53
+ blockId,
54
+ ...describeError(error),
55
+ });
56
+ return null;
57
+ }
58
+ }
59
+ //# sourceMappingURL=singleKindModel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"singleKindModel.js","sourceRoot":"","sources":["../../src/agents/singleKindModel.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,WAAW,EAAgC,MAAM,kBAAkB,CAAA;AAC5E,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAA;AAoEnD;;;;GAIG;AACH,MAAM,UAAU,4BAA4B,CAC1C,IAAyC;IAEzC,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,CAAA;IACpC,OAAO,KAAK,EAAE,KAAK,EAAE,EAAE;QACrB,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;QACrE,MAAM,kBAAkB,GAAG,IAAI,CAAC,+BAA+B;YAC7D,CAAC,CAAC,MAAM,IAAI,CAAC,+BAA+B,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,EAAE,aAAa,CAAC;YACrF,CAAC,CAAC,SAAS,CAAA;QACb,MAAM,sBAAsB,GAC1B,IAAI,CAAC,6BAA6B,IAAI,KAAK,CAAC,iBAAiB;YAC3D,CAAC,CAAC,MAAM,IAAI,CAAC,6BAA6B,CAAC,KAAK,CAAC,iBAAiB,CAAC;YACnE,CAAC,CAAC,SAAS,CAAA;QACf,OAAO,MAAM,CAAC,kBAAkB,CAC9B;YACE,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,GAAG,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,KAAK,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAClF,GAAG,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7D,GAAG,CAAC,sBAAsB,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,sBAAsB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrE,KAAK,EAAE;gBACL,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACrD,GAAG,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACxE;SACF,EACD,KAAK,CAAC,WAAW,CAClB,CAAA;IACH,CAAC,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,KAAK,UAAU,SAAS,CACtB,IAAyC,EACzC,WAAmB,EACnB,OAAe;IAEf,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;IAC7D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,+EAA+E,EAAE;YAC3F,WAAW;YACX,OAAO;YACP,GAAG,aAAa,CAAC,KAAK,CAAC;SACxB,CAAC,CAAA;QACF,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { Logger, TestCredentialBrief, TestSecretEntry } from '@cat-factory/kernel';
2
+ /** What one dispatch needs: the values for the container, and the state for the prompt. */
3
+ export interface ResolvedTestCredentials {
4
+ /** The pairs the harness turns into environment variables of this one job's agent process. */
5
+ env: {
6
+ key: string;
7
+ value: string;
8
+ }[];
9
+ /** The same read, as the state the prompt states. Never carries a value. */
10
+ brief: TestCredentialBrief;
11
+ }
12
+ /**
13
+ * Resolve a frame's test credentials for a dispatch that is entitled to them.
14
+ *
15
+ * `resolve` absent ⇒ this deployment has no sealed store wired at all, which is a DEPLOYMENT fact
16
+ * (`unwired`) rather than an empty list: no service on the board can be handed credentials, so
17
+ * telling a human to configure this one is the wrong instruction.
18
+ */
19
+ export declare function resolveTestCredentials(args: {
20
+ resolve?: (workspaceId: string, blockId: string) => Promise<TestSecretEntry[]>;
21
+ workspaceId: string;
22
+ blockId: string;
23
+ logger: Logger;
24
+ }): Promise<ResolvedTestCredentials>;
25
+ //# sourceMappingURL=testCredentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testCredentials.d.ts","sourceRoot":"","sources":["../../src/agents/testCredentials.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAsBvF,2FAA2F;AAC3F,MAAM,WAAW,uBAAuB;IACtC,8FAA8F;IAC9F,GAAG,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACrC,4EAA4E;IAC5E,KAAK,EAAE,mBAAmB,CAAA;CAC3B;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAAC,IAAI,EAAE;IACjD,OAAO,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,eAAe,EAAE,CAAC,CAAA;IAC9E,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;CACf,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAanC"}
@@ -0,0 +1,23 @@
1
+ import { runBestEffort } from '@cat-factory/kernel';
2
+ /**
3
+ * Resolve a frame's test credentials for a dispatch that is entitled to them.
4
+ *
5
+ * `resolve` absent ⇒ this deployment has no sealed store wired at all, which is a DEPLOYMENT fact
6
+ * (`unwired`) rather than an empty list: no service on the board can be handed credentials, so
7
+ * telling a human to configure this one is the wrong instruction.
8
+ */
9
+ export async function resolveTestCredentials(args) {
10
+ if (!args.resolve)
11
+ return { env: [], brief: { status: 'unwired' } };
12
+ const entries = await runBestEffort(args.logger, 'resolve the frame test credentials', () => args.resolve(args.workspaceId, args.blockId));
13
+ if (!entries)
14
+ return { env: [], brief: { status: 'unreadable' } };
15
+ return {
16
+ env: entries.map((entry) => ({ key: entry.key, value: entry.value })),
17
+ brief: {
18
+ status: 'resolved',
19
+ refs: entries.map((entry) => ({ key: entry.key, description: entry.description })),
20
+ },
21
+ };
22
+ }
23
+ //# sourceMappingURL=testCredentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testCredentials.js","sourceRoot":"","sources":["../../src/agents/testCredentials.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AA6BnD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,IAK5C;IACC,IAAI,CAAC,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,CAAA;IACnE,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,oCAAoC,EAAE,GAAG,EAAE,CAC1F,IAAI,CAAC,OAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,CAC9C,CAAA;IACD,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE,CAAA;IACjE,OAAO;QACL,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;QACrE,KAAK,EAAE;YACL,MAAM,EAAE,UAAU;YAClB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;SACnF;KACF,CAAA;AACH,CAAC"}
package/dist/index.d.ts CHANGED
@@ -45,6 +45,9 @@ export { bedrockAllowListFromEnv, bedrockRegionFromEnv, type BedrockEnv } from '
45
45
  export { ContainerRepoBootstrapper, type ContainerRepoBootstrapperDependencies, } from './agents/ContainerRepoBootstrapper.js';
46
46
  export { ContainerEnvConfigRepairer, type ContainerEnvConfigRepairerDependencies, } from './agents/ContainerEnvConfigRepairer.js';
47
47
  export { ContainerEnvironmentProbeAgent, type ContainerEnvironmentProbeAgentDependencies, } from './agents/ContainerEnvironmentProbeAgent.js';
48
+ export { ContainerJobAuthResolver, type ContainerJobAuth, type ContainerJobAuthDependencies, type ContainerJobAuthRequest, type ContainerJobAuthSubject, type LeasePersonalSubscriptionToken, type LeaseSubscriptionToken, } from './agents/containerJobAuth.js';
49
+ export { ContainerJobAccounting, type ContainerJobAccountingDeps, } from './agents/containerJobAccounting.js';
50
+ export { buildSingleKindModelResolver, type ResolveSingleKindModel, type SingleKindModel, type SingleKindModelResolverDependencies, } from './agents/singleKindModel.js';
48
51
  export { buildResolveRepoTarget, buildResolveRepoTargets, buildListWorkspaceRunRepos, type ResolveRepoTargetDependencies, type ResolveRepoTargetsDependencies, type ResolveRepoTargets, type ResolvedRepoTargets, type RepoCheckout, type ListWorkspaceRunRepos, type ListWorkspaceRunReposDependencies, type WorkspaceRunRepo, } from './agents/resolveRepoTarget.js';
49
52
  export { makeRepoFiles, makeResolveDeployCloneTarget, makeResolveRepoFiles, makeResolveRepoFilesForCoords, makeResolveRunRepoContext, runRepoOps, } from './agents/repoFiles.js';
50
53
  export { makePreviewJobBuilder, type PreviewJobBuilderDependencies, } from './preview/previewJobBuilder.js';