@kici-dev/orchestrator 0.4.0 → 0.6.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/__test-helpers__/fake-scaler-state-store.d.ts +24 -0
- package/dist/__test-helpers__/mock-db-query.d.ts +130 -0
- package/dist/__test-helpers__/mock-db.d.ts +27 -0
- package/dist/agent/agent-version.d.ts +34 -0
- package/dist/agent/dispatcher.d.ts +151 -13
- package/dist/agent/host-roster.d.ts +37 -5
- package/dist/agent/registry.d.ts +2 -0
- package/dist/app.d.ts +56 -7
- package/dist/approvals/apply-decision.d.ts +67 -19
- package/dist/approvals/held-run-release-wiring.d.ts +45 -0
- package/dist/approvals/independent-wiring.d.ts +55 -0
- package/dist/approvals/resume-router.d.ts +9 -0
- package/dist/approvals/triggerer-subject.d.ts +37 -0
- package/dist/cache/dep-cache.d.ts +54 -10
- package/dist/cache/global-eval-round-cache.d.ts +95 -0
- package/dist/cache/index.d.ts +3 -0
- package/dist/cache/pending-global-evals.d.ts +42 -0
- package/dist/cache/pending-inits.d.ts +10 -0
- package/dist/cli/api-client.d.ts +18 -0
- package/dist/cli/commands/cluster-settings.d.ts +52 -3
- package/dist/cli/commands/diagnose.d.ts +6 -0
- package/dist/cli/commands/held-run.d.ts +58 -0
- package/dist/cli/commands/runs.d.ts +1 -0
- package/dist/cli/commands/trust-policy.d.ts +117 -12
- package/dist/cli/kici-admin.d.ts +10 -0
- package/dist/cli.js +2770 -938
- package/dist/cluster/cluster-identity.d.ts +3 -3
- package/dist/cluster/cluster-settings-reader.d.ts +59 -1
- package/dist/cluster/coordinator.d.ts +44 -0
- package/dist/cluster/index.d.ts +4 -0
- package/dist/cluster/peer-handler.d.ts +19 -0
- package/dist/cluster/peer-registry.d.ts +17 -0
- package/dist/cluster/plan-headroom-store.d.ts +26 -0
- package/dist/cluster/worker-eviction.d.ts +69 -0
- package/dist/cold-store/load-event-log-range.d.ts +10 -3
- package/dist/cold-store/load-secret-audit-log-range.d.ts +4 -3
- package/dist/cold-store/tables/secret-audit-log.d.ts +4 -3
- package/dist/config.d.ts +48 -21
- package/dist/content-requirements-cache.d.ts +55 -0
- package/dist/contexts/held-runs.d.ts +123 -16
- package/dist/contexts/protection/aggregate.d.ts +3 -2
- package/dist/contexts/protection/branch-gate.d.ts +15 -0
- package/dist/contexts/protection/concurrency-gate.d.ts +8 -2
- package/dist/contexts/protection/pipeline.d.ts +18 -0
- package/dist/contexts/release-queued-holds.d.ts +35 -0
- package/dist/dashboard/attestation-filters.d.ts +8 -0
- package/dist/dashboard/handler.d.ts +6 -24
- package/dist/db/migrations/109_cluster_settings_cache_knobs.d.ts +4 -0
- package/dist/db/migrations/110_cluster_settings_global_eval_knobs.d.ts +4 -0
- package/dist/db/migrations/111_cluster_settings_global_eval_wait.d.ts +4 -0
- package/dist/db/migrations/112_execution_runs_workflow_repo.d.ts +4 -0
- package/dist/db/migrations/113_execution_runs_workflow_repo_index.d.ts +30 -0
- package/dist/db/migrations/114_ingest_queue_claim.d.ts +4 -0
- package/dist/db/migrations/115_global_workflows_cluster_switch.d.ts +9 -0
- package/dist/db/migrations/116_org_plan_headroom.d.ts +4 -0
- package/dist/db/migrations/117_invoke_gate_proxy_jobs.d.ts +4 -0
- package/dist/db/migrations/118_invoke_gate_scheduling.d.ts +4 -0
- package/dist/db/migrations/119_scaler_ha_ownership.d.ts +4 -0
- package/dist/db/migrations/120_cluster_settings_scaler_reaper.d.ts +4 -0
- package/dist/db/migrations/121_org_settings_allow_untrusted_dockerfile_builds.d.ts +19 -0
- package/dist/db/migrations/122_host_roster_scaler_managed.d.ts +25 -0
- package/dist/db/migrations/123_workflow_registrations_default_branch.d.ts +24 -0
- package/dist/db/migrations/124_cluster_settings_provision_backoff.d.ts +4 -0
- package/dist/db/migrations/125_org_trust_directory.d.ts +30 -0
- package/dist/db/migrations/126_held_runs_posted_pending_check.d.ts +28 -0
- package/dist/db/migrations/127_org_trust_policy_approval_expiry_seconds.d.ts +26 -0
- package/dist/db/migrations/128_execution_runs_global_eval_round.d.ts +23 -0
- package/dist/db/migrations/129_execution_runs_dispatch_routing_key.d.ts +24 -0
- package/dist/db/migrations/130_scaler_provision_outcomes.d.ts +32 -0
- package/dist/db/types.d.ts +428 -12
- package/dist/entry-helpers.d.ts +28 -0
- package/dist/events/event-emitter.d.ts +22 -0
- package/dist/events/event-router.d.ts +87 -1
- package/dist/events/types.d.ts +2 -23
- package/dist/git/credential-broker.d.ts +94 -0
- package/dist/git/inherited-secret.d.ts +24 -0
- package/dist/git/installation-token.d.ts +38 -0
- package/dist/git/job-context.d.ts +13 -0
- package/dist/index.js +324 -84
- package/dist/metrics/agent-metrics-aggregator.d.ts +4 -4
- package/dist/metrics/prometheus.d.ts +170 -3
- package/dist/oidc/oidc-mint-registration.d.ts +7 -3
- package/dist/orchestrator-core.d.ts +91 -24
- package/dist/pipeline/content-filter.d.ts +71 -0
- package/dist/pipeline/direct-ingress-deps.d.ts +100 -0
- package/dist/pipeline/dispatch-matched-workflow.d.ts +521 -11
- package/dist/pipeline/global-eval-round.d.ts +307 -0
- package/dist/pipeline/internal-event-pipeline.d.ts +140 -0
- package/dist/pipeline/invoke-gate.d.ts +164 -0
- package/dist/pipeline/job-contexts.d.ts +16 -17
- package/dist/pipeline/needs-scheduler.d.ts +13 -0
- package/dist/pipeline/process-webhook.d.ts +126 -31
- package/dist/pipeline/processor.d.ts +282 -23
- package/dist/pipeline/rerun.d.ts +21 -0
- package/dist/pipeline/resume-workflow.d.ts +40 -7
- package/dist/pipeline/route-or-dispatch-jobs.d.ts +15 -0
- package/dist/pipeline/security-hold-check.d.ts +217 -0
- package/dist/pipeline/test-pipeline.d.ts +12 -0
- package/dist/pipeline/undispatched-hold-checks.d.ts +23 -0
- package/dist/pipeline/webhook-payload-store.d.ts +20 -0
- package/dist/policy/dashboard-write-policy-listener.d.ts +62 -0
- package/dist/policy/dashboard-write-policy.d.ts +14 -0
- package/dist/provenance/backfill-run.d.ts +10 -1
- package/dist/provider-registry.d.ts +47 -4
- package/dist/providers/github/check-status-poster.d.ts +33 -3
- package/dist/providers/github/commit-message.d.ts +20 -0
- package/dist/providers/github/file-contents.d.ts +40 -0
- package/dist/providers/github/index.d.ts +2 -2
- package/dist/providers/github/normalizer.d.ts +3 -2
- package/dist/providers/local/normalizer.d.ts +3 -5
- package/dist/providers/universal-git/config.d.ts +2 -0
- package/dist/providers/universal-git/index.d.ts +11 -2
- package/dist/providers/universal-git/normalizer.d.ts +10 -0
- package/dist/queue/cleanup.d.ts +7 -1
- package/dist/queue/job-queue.d.ts +112 -7
- package/dist/queue/terminalize-unroutable.d.ts +13 -0
- package/dist/registration/extractor.d.ts +3 -3
- package/dist/registration/registration-index.d.ts +7 -0
- package/dist/registration/registration-run-match.d.ts +47 -0
- package/dist/registration/registration-store.d.ts +22 -0
- package/dist/reporting/check-run-reporter.d.ts +146 -2
- package/dist/reporting/check-run-summary.d.ts +25 -1
- package/dist/reporting/execution-tracker.d.ts +324 -9
- package/dist/reporting/log-chunk-sink.d.ts +8 -5
- package/dist/reporting/run-aggregator.d.ts +4 -14
- package/dist/reporting/step-display-order.d.ts +43 -0
- package/dist/routes/admin-held-runs.d.ts +142 -0
- package/dist/routes/admin-org-settings.d.ts +5 -0
- package/dist/routes/admin-trust-policy.d.ts +34 -6
- package/dist/routes/admin.d.ts +15 -0
- package/dist/scaler/backend-factory.d.ts +55 -0
- package/dist/scaler/bare-metal-backend.d.ts +43 -5
- package/dist/scaler/claim-store.d.ts +119 -0
- package/dist/scaler/config.d.ts +4 -0
- package/dist/scaler/container-backend.d.ts +18 -4
- package/dist/scaler/container-routing.d.ts +23 -0
- package/dist/scaler/container-spawn.d.ts +28 -0
- package/dist/scaler/event-backend.d.ts +124 -0
- package/dist/scaler/event-provision-reaper.d.ts +350 -0
- package/dist/scaler/failure-tracker.d.ts +1 -1
- package/dist/scaler/firecracker-backend.d.ts +33 -6
- package/dist/scaler/index.d.ts +11 -1
- package/dist/scaler/label-matcher.d.ts +4 -3
- package/dist/scaler/manager.d.ts +905 -76
- package/dist/scaler/resolve-container-auth.d.ts +45 -0
- package/dist/scaler/scaler-events.d.ts +11 -0
- package/dist/scaler/scaler-state-store.d.ts +294 -10
- package/dist/scaler/types.d.ts +131 -13
- package/dist/scaler/warm-pool.d.ts +132 -29
- package/dist/security/comment-handler.d.ts +50 -11
- package/dist/security/global-workflow-policy.d.ts +52 -12
- package/dist/security/identity-link.d.ts +60 -0
- package/dist/security/lock-source.d.ts +11 -13
- package/dist/security/reduced-privilege-note.d.ts +59 -0
- package/dist/security/trust-directory-store.d.ts +233 -0
- package/dist/security/trust-policy-gate.d.ts +94 -59
- package/dist/security/trust-policy-store.d.ts +3 -0
- package/dist/security/trust-resolver.d.ts +14 -98
- package/dist/server.d.ts +11 -1
- package/dist/server.js +56329 -42234
- package/dist/stale-detector/gate-deadline-detector.d.ts +52 -0
- package/dist/stale-detector/stale-run-detector.d.ts +61 -3
- package/dist/standalone.js +37619 -23570
- package/dist/webhook/ingest-accept.d.ts +70 -0
- package/dist/webhook/ingest-overflow-buffer.d.ts +35 -4
- package/dist/webhook/ingest-overflow-replayer.d.ts +50 -6
- package/dist/worker/in-memory-job-queue.d.ts +1 -1
- package/dist/ws/agent-handler.d.ts +31 -4
- package/dist/ws/dashboard-context-handler.d.ts +28 -4
- package/dist/ws/dashboard-global-workflows-handler.d.ts +36 -9
- package/dist/ws/execution-status-frame.d.ts +32 -0
- package/dist/ws/failure-messages.d.ts +2 -0
- package/dist/ws/git-credential-relay.d.ts +39 -0
- package/dist/ws/oidc-token-relay.d.ts +6 -11
- package/dist/ws/platform-client.d.ts +68 -9
- package/dist/ws/test-relay-handlers.d.ts +35 -10
- package/installer-image-digests.json +3 -3
- package/package.json +24 -19
- package/sbom.spdx.json +868 -821
- package/dist/approvals/team-membership-lookup.d.ts +0 -13
- package/dist/pipeline/inline-eval.d.ts +0 -44
- package/dist/providers/github/contributor-resolver.d.ts +0 -30
- package/dist/security/contributor-cache.d.ts +0 -83
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tier-2 global eval round — orchestrator side.
|
|
3
|
+
*
|
|
4
|
+
* A global workflow can declare a `filter` predicate and `DynamicJobFn`
|
|
5
|
+
* generators, neither of which the orchestrator may run: author code never
|
|
6
|
+
* returns to this process. So the candidates that need either one are grouped by
|
|
7
|
+
* the workflow repo they live in and handed to ONE pre-run job per
|
|
8
|
+
* (event × workflow repo × registered SHA). That job checks out the workflow
|
|
9
|
+
* repo and the source repo once, runs every candidate's filter and then its
|
|
10
|
+
* generators, and reports a verdict per candidate.
|
|
11
|
+
*
|
|
12
|
+
* The round precedes any run row by design: its whole purpose is to decide which
|
|
13
|
+
* global workflows produce a run at all, so creating one up-front would defeat
|
|
14
|
+
* it.
|
|
15
|
+
*
|
|
16
|
+
* This module owns the partition, the grouping, and the dispatch-and-await. The
|
|
17
|
+
* caller decides what to do with the verdicts.
|
|
18
|
+
*/
|
|
19
|
+
import type { GlobalEvalCandidateResult, LockWorkflow, SimulatedEvent, WorkflowDecision } from '@kici-dev/engine';
|
|
20
|
+
import { type GlobalEvalRoundCache } from '../cache/global-eval-round-cache.js';
|
|
21
|
+
import type { PendingGlobalEvalTracker } from '../cache/pending-global-evals.js';
|
|
22
|
+
import type { ClusterSettingsReader } from '../cluster/cluster-settings-reader.js';
|
|
23
|
+
import type { ProviderBundle } from '../provider-registry.js';
|
|
24
|
+
import type { QueuedJobInput } from '../queue/job-queue.js';
|
|
25
|
+
import type { RegisteredWorkflow } from '../registration/registration-index.js';
|
|
26
|
+
import type { WebhookInfo } from '../webhook/handler.js';
|
|
27
|
+
/** Name prefix identifying a round job in the queue, logs, and the dashboard. */
|
|
28
|
+
export declare const ROUND_JOB_PREFIX = "__globaleval__";
|
|
29
|
+
/** Metric label values for a per-candidate round verdict. */
|
|
30
|
+
export declare const GlobalEvalVerdictOutcome: {
|
|
31
|
+
/** The candidate's `filter` admitted it and its jobs are dispatching. */
|
|
32
|
+
readonly Run: 'run';
|
|
33
|
+
/** The candidate's `filter` returned false — a decided exclusion. */
|
|
34
|
+
readonly Filtered: 'filtered';
|
|
35
|
+
/** The round could not decide: it failed, breached a budget, or never reported. */
|
|
36
|
+
readonly Indeterminate: 'indeterminate';
|
|
37
|
+
};
|
|
38
|
+
export type GlobalEvalVerdictOutcome = (typeof GlobalEvalVerdictOutcome)[keyof typeof GlobalEvalVerdictOutcome];
|
|
39
|
+
/** Metric label values for a dispatched round's outcome. */
|
|
40
|
+
export declare const GlobalEvalRoundResultLabel: {
|
|
41
|
+
readonly Success: 'success';
|
|
42
|
+
readonly Error: 'error';
|
|
43
|
+
};
|
|
44
|
+
export type GlobalEvalRoundResultLabel = (typeof GlobalEvalRoundResultLabel)[keyof typeof GlobalEvalRoundResultLabel];
|
|
45
|
+
/** Metric label values for one round-cache lookup. */
|
|
46
|
+
export declare const GlobalEvalCacheLookupResult: {
|
|
47
|
+
readonly Hit: 'hit';
|
|
48
|
+
readonly Miss: 'miss';
|
|
49
|
+
/** The round input could not be serialized, so no key exists to look up. */
|
|
50
|
+
readonly Unkeyable: 'unkeyable';
|
|
51
|
+
};
|
|
52
|
+
export type GlobalEvalCacheLookupResult = (typeof GlobalEvalCacheLookupResult)[keyof typeof GlobalEvalCacheLookupResult];
|
|
53
|
+
/** One matched global workflow the round may have to decide on. */
|
|
54
|
+
export interface GlobalEvalCandidate {
|
|
55
|
+
reg: RegisteredWorkflow;
|
|
56
|
+
lockEntry: LockWorkflow;
|
|
57
|
+
/**
|
|
58
|
+
* The trigger-match decision this candidate came from, carried so the round's
|
|
59
|
+
* verdict can be appended to its trace. Optional because the round itself
|
|
60
|
+
* never reads it — only the caller that explains an exclusion does.
|
|
61
|
+
*/
|
|
62
|
+
decision?: WorkflowDecision;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Stable identity for one candidate across a round's results.
|
|
66
|
+
*
|
|
67
|
+
* The registration id alone is unique, but the workflow name is appended so a
|
|
68
|
+
* lookup reads honestly at the call site and a malformed registration with a
|
|
69
|
+
* blank id still separates two workflows.
|
|
70
|
+
*/
|
|
71
|
+
export declare function candidateKey(candidate: GlobalEvalCandidate): string;
|
|
72
|
+
/**
|
|
73
|
+
* Split matched global candidates into those that can dispatch straight away and
|
|
74
|
+
* those that must go through an eval round first.
|
|
75
|
+
*
|
|
76
|
+
* A candidate needs the round when it declares a `filter` (only the agent may
|
|
77
|
+
* run the predicate) or carries a `DynamicJobFn` (only the agent may run the
|
|
78
|
+
* generator). Everything else is fully described by the lock file, so routing it
|
|
79
|
+
* through a round would add a job dispatch and an agent round trip for nothing.
|
|
80
|
+
*/
|
|
81
|
+
export declare function partitionCandidates(candidates: readonly GlobalEvalCandidate[]): {
|
|
82
|
+
immediate: GlobalEvalCandidate[];
|
|
83
|
+
needsRound: GlobalEvalCandidate[];
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Group by workflow repo, routing key, and registered SHA.
|
|
87
|
+
*
|
|
88
|
+
* All three parts are load-bearing. The repo identifier and the SHA together pin
|
|
89
|
+
* the exact tree the round checks out, so two registrations of one repo at
|
|
90
|
+
* different commits can never share a checkout; the routing key pins which
|
|
91
|
+
* provider bundle mints the clone credentials, so two providers owning the same
|
|
92
|
+
* repo path stay separate.
|
|
93
|
+
*/
|
|
94
|
+
export declare function groupCandidates(candidates: readonly GlobalEvalCandidate[]): Map<string, GlobalEvalCandidate[]>;
|
|
95
|
+
/** Minimal dispatcher surface the round needs (the real one is the job dispatcher). */
|
|
96
|
+
export interface GlobalEvalDispatcher {
|
|
97
|
+
dispatch(input: QueuedJobInput): Promise<{
|
|
98
|
+
status: string;
|
|
99
|
+
jobId: string;
|
|
100
|
+
}>;
|
|
101
|
+
/**
|
|
102
|
+
* Take a still-queued round job out of the queue when nobody is waiting for
|
|
103
|
+
* it any more. Optional so a test double can omit it.
|
|
104
|
+
*/
|
|
105
|
+
cancelQueuedJob?(jobId: string, reason: string): Promise<void>;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Minimal provider-registry surface: resolving the bundle that owns a routing
|
|
109
|
+
* key. The round needs it because the workflow repo and the event's source repo
|
|
110
|
+
* can live behind different providers.
|
|
111
|
+
*/
|
|
112
|
+
export interface GlobalEvalProviderRegistry {
|
|
113
|
+
getByRoutingKey(routingKey: string): ProviderBundle | undefined;
|
|
114
|
+
}
|
|
115
|
+
/** Minimal agent-registry surface used to pick the round job's platform labels. */
|
|
116
|
+
export interface GlobalEvalAgentRegistry {
|
|
117
|
+
findAvailable(labels: string[]): Array<{
|
|
118
|
+
platform: string;
|
|
119
|
+
arch: string;
|
|
120
|
+
version?: string | null;
|
|
121
|
+
}>;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* First agent release whose job runner understands a `globalEvalRound`
|
|
125
|
+
* dispatch.
|
|
126
|
+
*
|
|
127
|
+
* Below it the agent has no round branch at all: the job falls through to the
|
|
128
|
+
* standard executor, whose `jobConfig` carries no `source.file`, so it either
|
|
129
|
+
* fails outright or reports success without the `globalEvalComplete` the
|
|
130
|
+
* orchestrator is waiting for. Either way the round never settles.
|
|
131
|
+
*
|
|
132
|
+
* Customers upgrade their orchestrator and their agents on their own schedule
|
|
133
|
+
* (`.claude/rules/compatibility.md`), so an orchestrator ahead of its fleet is
|
|
134
|
+
* a supported state and not an error — but it is one this module has to
|
|
135
|
+
* recognise, because the damage is not confined to the new feature. A global
|
|
136
|
+
* workflow that merely *contains* a generator now routes through the round, so
|
|
137
|
+
* an unrecognised round means static jobs that ran yesterday stop running.
|
|
138
|
+
*/
|
|
139
|
+
export declare const MIN_GLOBAL_EVAL_AGENT_VERSION = "0.5.0";
|
|
140
|
+
export interface GlobalEvalRoundDeps {
|
|
141
|
+
dispatcher: GlobalEvalDispatcher;
|
|
142
|
+
pendingGlobalEvals: PendingGlobalEvalTracker;
|
|
143
|
+
/**
|
|
144
|
+
* Required, unlike the three optional deps below: it is what resolves the
|
|
145
|
+
* WORKFLOW repo's own provider bundle. Omitting it would leave the round with
|
|
146
|
+
* no clone URL for the workflow repo at all, so it is not a dep that can
|
|
147
|
+
* degrade quietly.
|
|
148
|
+
*/
|
|
149
|
+
providerRegistry: GlobalEvalProviderRegistry;
|
|
150
|
+
clusterSettings?: ClusterSettingsReader;
|
|
151
|
+
globalEvalCache?: GlobalEvalRoundCache;
|
|
152
|
+
agentRegistry?: GlobalEvalAgentRegistry;
|
|
153
|
+
}
|
|
154
|
+
export interface GlobalEvalRoundArgs {
|
|
155
|
+
deps: GlobalEvalRoundDeps;
|
|
156
|
+
info: WebhookInfo;
|
|
157
|
+
event: SimulatedEvent;
|
|
158
|
+
candidates: readonly GlobalEvalCandidate[];
|
|
159
|
+
/** Source repo that triggered the event. */
|
|
160
|
+
repoIdentifier: string;
|
|
161
|
+
/** Source repo SHA the event lands on. */
|
|
162
|
+
ref: string;
|
|
163
|
+
dispatchBundle: ProviderBundle;
|
|
164
|
+
dispatchCredentials: Record<string, unknown>;
|
|
165
|
+
/** Cluster defaults for the round budgets and the wait ceiling (`config.ts`). */
|
|
166
|
+
config: {
|
|
167
|
+
globalEvalRoundTimeoutMs: number;
|
|
168
|
+
globalEvalCandidateTimeoutMs: number;
|
|
169
|
+
globalEvalWaitTimeoutMs: number;
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Why a fleet cannot decide a round, or `null` when it might.
|
|
174
|
+
*
|
|
175
|
+
* Only ONE state is refused: every registered init-runner reports a version, and
|
|
176
|
+
* every one of them is below the minimum. Everything else returns `null`.
|
|
177
|
+
*
|
|
178
|
+
* An empty fleet is not refused — nothing is registered yet, an agent may
|
|
179
|
+
* register a second from now, and the queue's own unroutable handling already
|
|
180
|
+
* covers a round nobody picks up.
|
|
181
|
+
*
|
|
182
|
+
* A fleet containing even ONE agent whose version cannot be read is not refused
|
|
183
|
+
* either, and the bar is deliberately that strict rather than "all unknown".
|
|
184
|
+
* Refusing suppresses every global workflow for the delivery — the exact damage
|
|
185
|
+
* this function exists to prevent — so it must rest on proof, not on ignorance
|
|
186
|
+
* about part of the fleet. Getting it wrong in the other direction merely costs
|
|
187
|
+
* a slow failure with a less useful message.
|
|
188
|
+
*
|
|
189
|
+
* Refusing here rather than at dispatch is deliberate. A too-old fleet does not
|
|
190
|
+
* clear within a retry, so dispatching would spend two full wait ceilings of
|
|
191
|
+
* inline webhook latency to reach the same verdict — and reach it with a
|
|
192
|
+
* timeout message that says nothing about agent versions.
|
|
193
|
+
*/
|
|
194
|
+
export declare function unsupportedFleetReason(agentRegistry?: GlobalEvalAgentRegistry): string | null;
|
|
195
|
+
/**
|
|
196
|
+
* Narrow one wire verdict into a value the dispatch path can trust.
|
|
197
|
+
*
|
|
198
|
+
* The round-result schema proves only "array of objects with string keys" — its
|
|
199
|
+
* `jobs` entries are a deliberate unvalidated pass-through of `LockJob`, whose
|
|
200
|
+
* single source of truth is a TypeScript interface with no Zod mirror. So every
|
|
201
|
+
* field is re-checked here rather than dereferenced: `run` is compared, not
|
|
202
|
+
* coerced, and a generated job missing a usable `name` makes the whole candidate
|
|
203
|
+
* indeterminate rather than being dropped silently. Fail-closed is the right
|
|
204
|
+
* direction — a generator whose output we cannot read is a generator whose
|
|
205
|
+
* verdict we cannot act on, and half-dispatching its jobs would be worse than
|
|
206
|
+
* reporting that we could not decide.
|
|
207
|
+
*/
|
|
208
|
+
export declare function narrowVerdict(raw: unknown, workflowName: string): GlobalEvalCandidateResult;
|
|
209
|
+
/**
|
|
210
|
+
* Cap on the joined reason string a decided-nothing round reports.
|
|
211
|
+
*
|
|
212
|
+
* The reasons are authored by an agent running author code, so their combined
|
|
213
|
+
* length is unbounded — and this string travels verbatim into the commit
|
|
214
|
+
* check's `output.summary`, which GitHub caps at 65535 characters. An overflow
|
|
215
|
+
* is rejected with a 422 that the best-effort post swallows, so the check
|
|
216
|
+
* disappears in exactly the case it exists for. Realistic reasons are one short
|
|
217
|
+
* sentence each; this bound makes that structural instead of lucky.
|
|
218
|
+
*/
|
|
219
|
+
export declare const MAX_ROUND_REASON_CHARS = 4000;
|
|
220
|
+
/**
|
|
221
|
+
* Truncate `text` to at most `max` UTF-16 code units, marking that it was cut.
|
|
222
|
+
*
|
|
223
|
+
* Two edges the naive slice gets wrong, both of which matter because the result
|
|
224
|
+
* is posted to an API with a hard character limit:
|
|
225
|
+
*
|
|
226
|
+
* - A `max` smaller than the marker would make `max - marker.length` negative,
|
|
227
|
+
* and appending the marker to an empty slice then returns a string LONGER
|
|
228
|
+
* than `max`. Such a `max` cannot carry both content and a marker, so the
|
|
229
|
+
* marker itself is truncated instead.
|
|
230
|
+
* - Slicing at an arbitrary index can cut a surrogate pair in half, leaving a
|
|
231
|
+
* lone surrogate that is not valid text. The cut backs off by one unit when
|
|
232
|
+
* it lands between the halves of a pair.
|
|
233
|
+
*/
|
|
234
|
+
export declare function truncateReasonText(text: string, max: number): string;
|
|
235
|
+
/**
|
|
236
|
+
* One round that produced no verdicts after every attempt, described well enough
|
|
237
|
+
* for the caller to record it.
|
|
238
|
+
*
|
|
239
|
+
* There is exactly one of these per failed round — never one per candidate. The
|
|
240
|
+
* round exists to collapse N candidate workflows into a single pre-run job, so
|
|
241
|
+
* fanning its failure back out into N run rows and N checks would undo the fan-out
|
|
242
|
+
* reduction the whole design is for. `workflowNames` is what makes the single
|
|
243
|
+
* record honest: it names every workflow the failure suppressed.
|
|
244
|
+
*/
|
|
245
|
+
export interface GlobalEvalRoundFailure {
|
|
246
|
+
/** The last attempt's round-job run id — what an errored run row is written under. */
|
|
247
|
+
runId: string;
|
|
248
|
+
/** Repo the suppressed workflows are authored in. */
|
|
249
|
+
workflowRepoIdentifier: string;
|
|
250
|
+
/** Every candidate workflow this round suppressed. */
|
|
251
|
+
workflowNames: string[];
|
|
252
|
+
/** The last attempt's error. */
|
|
253
|
+
error: string;
|
|
254
|
+
/** How many attempts were made before giving up. `0` ⇒ never dispatched. */
|
|
255
|
+
attempts: number;
|
|
256
|
+
/**
|
|
257
|
+
* True when the round itself completed and decided some candidates, leaving
|
|
258
|
+
* only {@link workflowNames} undecided.
|
|
259
|
+
*
|
|
260
|
+
* Recorded as its own record rather than folded into a total failure because
|
|
261
|
+
* the two read differently to an author: a total failure means nothing from
|
|
262
|
+
* this repo ran, a partial one means their workflow specifically could not be
|
|
263
|
+
* decided while its neighbours ran fine. Without it, whether a broken filter
|
|
264
|
+
* is visible at all depends on how many unrelated global workflows happen to
|
|
265
|
+
* share a workflow repo — a per-candidate budget breach in a group of one
|
|
266
|
+
* produces a check, and the identical fault in a group of two produces
|
|
267
|
+
* nothing.
|
|
268
|
+
*/
|
|
269
|
+
partial?: boolean;
|
|
270
|
+
}
|
|
271
|
+
/** Every candidate's verdict, plus the rounds that produced none. */
|
|
272
|
+
export interface GlobalEvalRoundsOutcome {
|
|
273
|
+
/** One verdict per candidate, keyed by {@link candidateKey}. */
|
|
274
|
+
verdicts: Map<string, GlobalEvalCandidateResult>;
|
|
275
|
+
/** One entry per round that failed outright — never one per candidate. */
|
|
276
|
+
failures: GlobalEvalRoundFailure[];
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Run every eval round the candidate set needs and return one verdict per
|
|
280
|
+
* candidate, plus one failure record per round that produced none.
|
|
281
|
+
*
|
|
282
|
+
* Rounds run one group at a time: a group is one dual checkout on one agent, and
|
|
283
|
+
* a push touching several workflow repos is the uncommon case. A group whose
|
|
284
|
+
* round fails marks only its own candidates indeterminate — one unreachable
|
|
285
|
+
* workflow repo must not suppress the workflows living in another.
|
|
286
|
+
*
|
|
287
|
+
* The failures are returned rather than recorded here: this module owns the
|
|
288
|
+
* partition, the grouping, and the dispatch-and-await, and has neither a
|
|
289
|
+
* database handle nor the inbound provider bundle a commit check must be posted
|
|
290
|
+
* through.
|
|
291
|
+
*/
|
|
292
|
+
export declare function runGlobalEvalRounds(args: GlobalEvalRoundArgs): Promise<GlobalEvalRoundsOutcome>;
|
|
293
|
+
/**
|
|
294
|
+
* Meter candidates a round never got to decide on.
|
|
295
|
+
*
|
|
296
|
+
* The caller's fail-closed paths — no pending-eval tracker, so a round could
|
|
297
|
+
* never settle — return before {@link runGlobalEvalRounds} is reached, and they
|
|
298
|
+
* suppress EVERY global workflow for that delivery. That is this subsystem's
|
|
299
|
+
* most severe outcome, and without this call it appeared in none of its
|
|
300
|
+
* metrics: one `logger.warn` was the whole record.
|
|
301
|
+
*
|
|
302
|
+
* Counted as `indeterminate` because that is exactly what happened — nothing
|
|
303
|
+
* evaluated the workflows — which also keeps `candidates` equal to the sum of
|
|
304
|
+
* the verdicts on every path.
|
|
305
|
+
*/
|
|
306
|
+
export declare function recordUnrunCandidates(count: number): void;
|
|
307
|
+
//# sourceMappingURL=global-eval-round.d.ts.map
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapter that dispatches an internally-triggered workflow through the SHARED
|
|
3
|
+
* pipeline (`dispatchMatchedWorkflow`) instead of a bespoke path.
|
|
4
|
+
*
|
|
5
|
+
* The bespoke path it replaces resolved no bound contexts, so no context vars,
|
|
6
|
+
* no scoped secrets and none of the context's protection rules reached an
|
|
7
|
+
* internally-triggered job; it also dropped every non-static job on the floor.
|
|
8
|
+
* Routing through the one pipeline is what keeps a fourth such gap from
|
|
9
|
+
* appearing.
|
|
10
|
+
*/
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
import type { LockFile, WorkflowDecision } from '@kici-dev/engine';
|
|
13
|
+
import { type ProcessingDeps } from './processor.js';
|
|
14
|
+
import type { ProviderBundle } from '../provider-registry.js';
|
|
15
|
+
/**
|
|
16
|
+
* The LIFECYCLE event names the orchestrator emits to itself. They are
|
|
17
|
+
* `__`-prefixed so they can never collide with a user `kiciEvent()` name, and
|
|
18
|
+
* each one maps onto a canonical trigger-event type (`__schedule_fire` is the
|
|
19
|
+
* one that is not a plain de-prefixing).
|
|
20
|
+
*
|
|
21
|
+
* NOT the whole set of orchestrator-minted events — the scaler manager mints two
|
|
22
|
+
* more under the other reserved prefix. NOT the trusted set either: two of these
|
|
23
|
+
* four are caused by a run and inherit its tier — `ORCHESTRATOR_MINTED_TRUSTED_EVENT_NAMES`
|
|
24
|
+
* is what the trust classification reads.
|
|
25
|
+
*
|
|
26
|
+
* Enumerated so this module compares against a checked value rather than a bare
|
|
27
|
+
* literal. It is NOT yet the single source: `cron/cron-scheduler.ts`,
|
|
28
|
+
* `events/event-emitter.ts` and `events/event-router.ts` still spell the same
|
|
29
|
+
* names inline, and converging them is a sweep this module's blast radius does
|
|
30
|
+
* not cover. The names are a protocol between those emitters, the router and
|
|
31
|
+
* this adapter, so a typo in any one of them silently stops matching.
|
|
32
|
+
*/
|
|
33
|
+
export declare const InternalSystemEventName: z.ZodEnum<{
|
|
34
|
+
__job_complete: "__job_complete";
|
|
35
|
+
__schedule_fire: "__schedule_fire";
|
|
36
|
+
__workflow_complete: "__workflow_complete";
|
|
37
|
+
__workflows_failed_batch: "__workflows_failed_batch";
|
|
38
|
+
}>;
|
|
39
|
+
/**
|
|
40
|
+
* Context for dispatching one internally-triggered workflow.
|
|
41
|
+
*
|
|
42
|
+
* Declared here rather than in `orchestrator-core.ts` because the adapter is
|
|
43
|
+
* the consumer that defines the shape; the composition root imports it.
|
|
44
|
+
*/
|
|
45
|
+
export interface InternalEventDispatchContext {
|
|
46
|
+
event: {
|
|
47
|
+
id: string;
|
|
48
|
+
eventName: string;
|
|
49
|
+
payload?: Record<string, unknown>;
|
|
50
|
+
};
|
|
51
|
+
routingKey: string;
|
|
52
|
+
repoIdentifier: string;
|
|
53
|
+
providerContext: Record<string, unknown>;
|
|
54
|
+
providerType: 'github' | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* Provider bundle for the acting repository, resolved by the composition root
|
|
57
|
+
* from the live registry.
|
|
58
|
+
*
|
|
59
|
+
* Required, not derived here: every dispatch site builds its clone URL as
|
|
60
|
+
* `bundle?.repoUrlBuilder?.buildCloneUrl(repoIdentifier) ?? ''`, and nothing
|
|
61
|
+
* downstream resolves a bundle from `deps.providerRegistry`. Passing
|
|
62
|
+
* `undefined` therefore dispatches every job with an empty repo URL AND
|
|
63
|
+
* classifies the run as a local-working-tree dispatch (the source-pack build
|
|
64
|
+
* and the source-cache probe both gate on `!!ctx.bundle`), so no source
|
|
65
|
+
* tarball is produced and the agent falls through to cloning `''`. It also
|
|
66
|
+
* carries the clone-token provider and the check-status poster.
|
|
67
|
+
*
|
|
68
|
+
* Legitimately `undefined` when the event carries no routing key, or when the
|
|
69
|
+
* registry has no bundle for it — the same state the pre-pipeline path
|
|
70
|
+
* rendered as an empty repo URL.
|
|
71
|
+
*/
|
|
72
|
+
bundle: ProviderBundle | undefined;
|
|
73
|
+
cronCommitSha: string;
|
|
74
|
+
/**
|
|
75
|
+
* The default branch of the registration this event dispatches, or `null`
|
|
76
|
+
* when it was never captured.
|
|
77
|
+
*
|
|
78
|
+
* A `__schedule_fire` run executes the default branch's lock file, so that
|
|
79
|
+
* branch IS the run's branch — which is what a context's branch restrictions
|
|
80
|
+
* evaluate. `null` presents no branch and the gate then rejects with its
|
|
81
|
+
* named-cause verdict; nothing substitutes a value, because a branch nobody
|
|
82
|
+
* proved is worse than an honest refusal.
|
|
83
|
+
*/
|
|
84
|
+
registrationDefaultBranch: string | null;
|
|
85
|
+
/**
|
|
86
|
+
* The run that summoned this one through an invoke gate, when there is one.
|
|
87
|
+
*
|
|
88
|
+
* Set ONLY by the invoke-gate summon path, whose event id is synthesized and
|
|
89
|
+
* therefore matches no `kici_events` row. Stating the emitting run directly
|
|
90
|
+
* is what lets a summoned run inherit its summoner's trust tier — the same
|
|
91
|
+
* inheritance every other user `kiciEvent()` subscriber gets — instead of
|
|
92
|
+
* always falling back to isolated on a lookup that cannot succeed.
|
|
93
|
+
*/
|
|
94
|
+
summonedByRunId?: string;
|
|
95
|
+
/**
|
|
96
|
+
* Chain depth to stamp on the started run. A webhook-triggered run leaves this
|
|
97
|
+
* unset (0); a run summoned by an invoke gate carries its summoner's depth + 1
|
|
98
|
+
* so the chain-depth circuit breaker bounds recursion when it fires its own
|
|
99
|
+
* gate.
|
|
100
|
+
*/
|
|
101
|
+
chainDepth?: number;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* The two identities an internal event renders into, which are NOT the same
|
|
105
|
+
* value for a user `kiciEvent()`:
|
|
106
|
+
*
|
|
107
|
+
* - `jobConfigType` is what workflows observe as `ctx.event.type` and what the
|
|
108
|
+
* trigger matcher matched against — the literal `kici_event` for a custom
|
|
109
|
+
* event.
|
|
110
|
+
* - `triggerEvent` is what the run carries and forwards to the Platform — the
|
|
111
|
+
* actual event name, so the dashboard can say which event fired.
|
|
112
|
+
*
|
|
113
|
+
* A system (`__`-prefixed) event renders both from its de-prefixed name, with
|
|
114
|
+
* `__schedule_fire` collapsing onto `schedule`.
|
|
115
|
+
*/
|
|
116
|
+
export declare function deriveInternalEventIdentity(eventName: string): {
|
|
117
|
+
jobConfigType: string;
|
|
118
|
+
triggerEvent: string;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* True when every tier the schema admits carries a rank.
|
|
122
|
+
*
|
|
123
|
+
* The `Record<TrustTier, number>` above already fails the build on an unranked
|
|
124
|
+
* tier, so this can only ever be false if the two drift through a cast. It is
|
|
125
|
+
* exported so a test asserts it against the live `TrustTierSchema.options`
|
|
126
|
+
* rather than against a transcription of it — the check has to read the schema
|
|
127
|
+
* to be worth having.
|
|
128
|
+
*/
|
|
129
|
+
export declare function trustTierRanksCoverEverySchemaTier(): boolean;
|
|
130
|
+
/**
|
|
131
|
+
* Dispatch one matched workflow for an internal event through the shared
|
|
132
|
+
* pipeline. Returns the spawned-run summary the bespoke path returned, or
|
|
133
|
+
* `null` when the decision names a workflow the lock file does not carry.
|
|
134
|
+
*/
|
|
135
|
+
export declare function dispatchInternalEventViaPipeline(decision: WorkflowDecision, lockFile: LockFile, ctx: InternalEventDispatchContext, deps: ProcessingDeps): Promise<{
|
|
136
|
+
runId: string;
|
|
137
|
+
repo: string;
|
|
138
|
+
workflow: string;
|
|
139
|
+
} | null>;
|
|
140
|
+
//# sourceMappingURL=internal-event-pipeline.d.ts.map
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invoke-gate executor.
|
|
3
|
+
*
|
|
4
|
+
* An invoke gate is a standard job whose only novel attribute is `invoke`: it
|
|
5
|
+
* never dispatches steps to an agent. When the gate becomes ready the
|
|
6
|
+
* orchestrator emits the gate's kici event at the source repo, matches the
|
|
7
|
+
* repo's opt-in subscribers, dispatches each as a normal in-repo run, and
|
|
8
|
+
* tracks each spawned run as a **proxy job** in the gate's own run.
|
|
9
|
+
*
|
|
10
|
+
* Correlation is a synchronous in-process match-and-dispatch — one orchestrator
|
|
11
|
+
* serves one org, so the executor matches + dispatches through an injected
|
|
12
|
+
* `summon` callback and gets the spawned run ids back directly. It ALSO persists
|
|
13
|
+
* `summoned_by_run_id` / `summoned_by_proxy_job` on each spawned run so proxy
|
|
14
|
+
* completion still resolves if the run finalizes on another HA instance.
|
|
15
|
+
*
|
|
16
|
+
* The gate itself is the aggregating graph node: downstream `needs` edges point
|
|
17
|
+
* at the gate, and its proxies are its fan-out children (`base_job_name` = the
|
|
18
|
+
* gate name). A zero-subscriber emit fails the gate by default; `optional: true`
|
|
19
|
+
* makes it a green skip.
|
|
20
|
+
*/
|
|
21
|
+
import { ExecutionJobStatus } from '@kici-dev/engine';
|
|
22
|
+
import type { LockInvoke, LockJob } from '@kici-dev/engine';
|
|
23
|
+
import type { Kysely } from 'kysely';
|
|
24
|
+
import type { Database } from '../db/types.js';
|
|
25
|
+
import type { ExecutionTracker } from '../reporting/execution-tracker.js';
|
|
26
|
+
/**
|
|
27
|
+
* The invoke parameters carried from a gate's lock job to its (possibly deferred)
|
|
28
|
+
* execution. Persisted on the pending job context so a gate released later — or
|
|
29
|
+
* after a crash-recovery restore — still summons instead of reaching an agent.
|
|
30
|
+
*/
|
|
31
|
+
export interface InvokeGateParams {
|
|
32
|
+
event: string;
|
|
33
|
+
payload?: Record<string, unknown>;
|
|
34
|
+
/** Require-by-default: false fails the gate on zero subscribers. */
|
|
35
|
+
optional: boolean;
|
|
36
|
+
/** Fan-out concurrency width applied to the gate's proxy children. */
|
|
37
|
+
maxParallel?: number;
|
|
38
|
+
/** Fan-out fail-fast policy applied to the gate's proxy children. */
|
|
39
|
+
failFast?: boolean;
|
|
40
|
+
/** The gate's own wall-clock timeout in ms (orchestrator-swept). */
|
|
41
|
+
timeoutMs?: number;
|
|
42
|
+
}
|
|
43
|
+
/** Extract the invoke parameters from a lock job, or undefined when it is not a gate. */
|
|
44
|
+
export declare function invokeParamsFromLockJob(lockJob: Pick<LockJob, 'invoke' | 'maxParallel' | 'failFast' | 'timeout'> & {
|
|
45
|
+
invoke?: LockInvoke;
|
|
46
|
+
}): InvokeGateParams | undefined;
|
|
47
|
+
/** True when a lock job is an invoke gate (carries an `invoke` action). */
|
|
48
|
+
export declare function isInvokeGate(lockJob: Pick<LockJob, 'invoke'>): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* A summon that was REFUSED, as opposed to one that matched nothing.
|
|
51
|
+
*
|
|
52
|
+
* The two are not the same verdict and must never render the same. A gate
|
|
53
|
+
* carrying `optional: true` treats zero summoned runs as a green skip, so a
|
|
54
|
+
* refusal returned as `[]` would report success for work that was declined.
|
|
55
|
+
* Throwing this makes the confusion unrepresentable: {@link runInvokeGate}
|
|
56
|
+
* fails the gate job with the reason, whatever order its own checks run in.
|
|
57
|
+
*/
|
|
58
|
+
export declare class SummonRefusedError extends Error {
|
|
59
|
+
constructor(reason: string);
|
|
60
|
+
}
|
|
61
|
+
/** One source-repo run summoned by an invoke gate. */
|
|
62
|
+
export interface SummonedRun {
|
|
63
|
+
/** The spawned run's id. */
|
|
64
|
+
runId: string;
|
|
65
|
+
/** The source repo the run belongs to (`owner/repo`). */
|
|
66
|
+
repo: string;
|
|
67
|
+
/** The subscribing workflow's name. */
|
|
68
|
+
workflow: string;
|
|
69
|
+
}
|
|
70
|
+
/** Arguments to the injected `summon` callback. */
|
|
71
|
+
export interface SummonArgs {
|
|
72
|
+
event: string;
|
|
73
|
+
payload?: Record<string, unknown>;
|
|
74
|
+
sourceRepo: string;
|
|
75
|
+
/** The chain depth to stamp on the summoned runs (already incremented). */
|
|
76
|
+
chainDepth: number;
|
|
77
|
+
/** The summoning gate's run id — tag each spawned run so its proxy resolves. */
|
|
78
|
+
summonedByRunId: string;
|
|
79
|
+
}
|
|
80
|
+
/** Dependencies for {@link runInvokeGate}. */
|
|
81
|
+
export interface InvokeGateDeps {
|
|
82
|
+
db: Kysely<Database>;
|
|
83
|
+
/** Insert the proxy rows + mark the gate terminal on a zero-match emit. */
|
|
84
|
+
executionTracker: Pick<ExecutionTracker, 'addJobsToRun' | 'onJobStatus' | 'reconcileSummonedRunIfTerminal'>;
|
|
85
|
+
/**
|
|
86
|
+
* Match the source repo's subscribers to `event`, dispatch each as a normal
|
|
87
|
+
* in-repo run, and return the spawned run ids. Implemented at the composition
|
|
88
|
+
* root, where it reuses the orchestrator's event match + trust-policy path.
|
|
89
|
+
*
|
|
90
|
+
* An empty array means the event matched no subscriber — a real outcome a
|
|
91
|
+
* gate may declare `optional`. A REFUSAL raises
|
|
92
|
+
* {@link SummonRefusedError} instead, so the two can never be read as the
|
|
93
|
+
* same thing.
|
|
94
|
+
*/
|
|
95
|
+
summon: (args: SummonArgs) => Promise<SummonedRun[]>;
|
|
96
|
+
/** Max chain depth allowed for a summon (the circuit breaker's bound). */
|
|
97
|
+
maxChainDepth: number;
|
|
98
|
+
}
|
|
99
|
+
/** Arguments identifying the gate to run. */
|
|
100
|
+
export interface RunInvokeGateArgs {
|
|
101
|
+
runId: string;
|
|
102
|
+
gateJobId: string;
|
|
103
|
+
gateJobName: string;
|
|
104
|
+
event: string;
|
|
105
|
+
payload?: Record<string, unknown>;
|
|
106
|
+
/** When true, a zero-subscriber emit succeeds; when false it fails the gate. */
|
|
107
|
+
optional: boolean;
|
|
108
|
+
sourceRepo: string;
|
|
109
|
+
chainDepth: number;
|
|
110
|
+
/** Fan-out concurrency width stamped on the proxy children (`maxParallel`). */
|
|
111
|
+
maxParallel?: number;
|
|
112
|
+
/** Fan-out fail-fast policy stamped on the proxy children (`failFast`). */
|
|
113
|
+
failFast?: boolean;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Aggregate an invoke gate's status from its proxies' terminal statuses. The
|
|
117
|
+
* gate stays open (`allTerminal: false`) until every proxy is terminal; then it
|
|
118
|
+
* is `failed` if any proxy is a failure, else `success`. Pure — the tracker's
|
|
119
|
+
* aggregation hook drives the DB reads and the gate transition.
|
|
120
|
+
*/
|
|
121
|
+
export declare function aggregateGateStatus(proxyStatuses: readonly string[]): {
|
|
122
|
+
allTerminal: boolean;
|
|
123
|
+
status?: ExecutionJobStatus;
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* The message a gate naming a reserved event fails with.
|
|
127
|
+
*
|
|
128
|
+
* Exported so the gate executor and its test state the same string, and so the
|
|
129
|
+
* SDK-side error can be compared against it.
|
|
130
|
+
*/
|
|
131
|
+
export declare function reservedEventMessage(event: string, prefix: string): string;
|
|
132
|
+
/** The message a require-by-default gate fails with when nothing subscribes. */
|
|
133
|
+
export declare function zeroSubscriberMessage(sourceRepo: string, event: string): string;
|
|
134
|
+
/**
|
|
135
|
+
* The proxy job name for a summoned run — the gate name, a `repo:workflow`
|
|
136
|
+
* variant, and the summoned run id. The run id makes the name **unique per run**:
|
|
137
|
+
* two summoned runs with the same `repo`+`workflow` (duplicate registrations)
|
|
138
|
+
* would otherwise share one `job_name`, so the mirror's `job_name` lookup would
|
|
139
|
+
* resolve one proxy and leave the other hanging. The human-readable label for the
|
|
140
|
+
* graph is carried separately in `variant_label` (`repo:workflow`).
|
|
141
|
+
*/
|
|
142
|
+
export declare function proxyJobName(gateJobName: string, run: SummonedRun): string;
|
|
143
|
+
/**
|
|
144
|
+
* Run an invoke gate: summon the source repo's subscribers and create one proxy
|
|
145
|
+
* child per spawned run. A zero-subscriber emit terminalizes the gate — failed
|
|
146
|
+
* by default, success under `optional`. When the chain-depth bound is reached
|
|
147
|
+
* the gate fails without summoning, so an invoke chain cannot loop.
|
|
148
|
+
*/
|
|
149
|
+
export declare function runInvokeGate(deps: InvokeGateDeps, args: RunInvokeGateArgs): Promise<void>;
|
|
150
|
+
/** Dependencies for {@link releaseInvokeGate}. */
|
|
151
|
+
export interface ReleaseInvokeGateDeps {
|
|
152
|
+
db: Kysely<Database>;
|
|
153
|
+
executionTracker: ExecutionTracker;
|
|
154
|
+
invokeGateDeps: InvokeGateDeps;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Release a ready invoke gate: swap its synthetic needs-pending row for a real
|
|
158
|
+
* `gate` job row, then run the gate executor. Reads the summoning run's source
|
|
159
|
+
* repo and chain depth from `execution_runs` (the chain depth bounds recursion).
|
|
160
|
+
* Shared by the needs-release path (`dispatchReadyJob`) and the root-gate
|
|
161
|
+
* post-registration nudge, so both go through one code path.
|
|
162
|
+
*/
|
|
163
|
+
export declare function releaseInvokeGate(deps: ReleaseInvokeGateDeps, runId: string, gateJobName: string, params: InvokeGateParams): Promise<void>;
|
|
164
|
+
//# sourceMappingURL=invoke-gate.d.ts.map
|
|
@@ -3,43 +3,42 @@
|
|
|
3
3
|
*
|
|
4
4
|
* A job binds an ordered list of contexts (`LockJob.contexts`). This
|
|
5
5
|
* module resolves that list into concrete context names (static values
|
|
6
|
-
* verbatim
|
|
7
|
-
* the per-context secrets/variables last-wins. It keeps the heavy fold logic
|
|
6
|
+
* verbatim; every dynamic element is resolved by the agent's init job) and
|
|
7
|
+
* folds the per-context secrets/variables last-wins. It keeps the heavy fold logic
|
|
8
8
|
* out of `dispatchMatchedWorkflow`, which must stay under the function-length cap.
|
|
9
9
|
*/
|
|
10
10
|
import { type Context, type HostFacts, type LockJob } from '@kici-dev/engine';
|
|
11
11
|
import type { SecretResolverApi } from '../secrets/secret-resolver.js';
|
|
12
12
|
import type { VariableStore } from '../contexts/variable-store.js';
|
|
13
13
|
/**
|
|
14
|
-
* Placeholder written into the persisted bound-env list for
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* Placeholder written into the persisted bound-env list for a dynamic element
|
|
15
|
+
* the orchestrator does not resolve at dispatch. The agent's init eval later
|
|
16
|
+
* overwrites the list with the resolved name.
|
|
17
17
|
*/
|
|
18
18
|
export declare const DYNAMIC_ENV_PLACEHOLDER = "(dynamic)";
|
|
19
19
|
/** Ordered resolved context names plus whether any element still needs agent init. */
|
|
20
20
|
export interface ResolvedJobContexts {
|
|
21
|
-
/** Resolved static
|
|
21
|
+
/** Resolved static names, in order. */
|
|
22
22
|
names: string[];
|
|
23
|
-
/** True when
|
|
23
|
+
/** True when a dynamic element must be resolved by an agent init job. */
|
|
24
24
|
needsInit: boolean;
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
27
|
* Resolve the ordered bound-context names from a lock job. Static elements
|
|
28
|
-
* use their value verbatim;
|
|
29
|
-
*
|
|
30
|
-
* be resolved here and flags `needsInit`.
|
|
28
|
+
* use their value verbatim; any dynamic element (inline or impure) is resolved
|
|
29
|
+
* by the agent's init job and flags `needsInit`.
|
|
31
30
|
*/
|
|
32
|
-
export declare function resolveJobContextNames(lockJob: LockJob
|
|
31
|
+
export declare function resolveJobContextNames(lockJob: LockJob): ResolvedJobContexts;
|
|
33
32
|
/**
|
|
34
33
|
* Build the ordered bound-context display list for persistence at dispatch.
|
|
35
34
|
* Unlike {@link resolveJobContextNames}, this never drops an unresolved
|
|
36
|
-
* element: a static element uses its value,
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
35
|
+
* element: a static element uses its value, and any dynamic element (which the
|
|
36
|
+
* orchestrator no longer resolves at dispatch) becomes the `(dynamic)`
|
|
37
|
+
* placeholder — so the persisted column reflects every declared slot in order.
|
|
38
|
+
* The deferred-init flow-back overwrites the placeholder once the agent
|
|
39
|
+
* resolves the name. Returns an empty array when the job binds no context.
|
|
41
40
|
*/
|
|
42
|
-
export declare function buildJobContextDisplayNames(lockJob: LockJob
|
|
41
|
+
export declare function buildJobContextDisplayNames(lockJob: LockJob): string[];
|
|
43
42
|
/** Merged secrets/variables across an ordered list of resolved contexts. */
|
|
44
43
|
export interface MultiEnvMergedData {
|
|
45
44
|
contextVars?: Record<string, string>;
|