@kici-dev/orchestrator 0.3.0 → 0.5.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 (68) hide show
  1. package/dist/__test-helpers__/mock-db.d.ts +4 -0
  2. package/dist/agent/agent-version.d.ts +34 -0
  3. package/dist/agent/dispatcher.d.ts +70 -0
  4. package/dist/app.d.ts +23 -1
  5. package/dist/cache/global-eval-round-cache.d.ts +88 -0
  6. package/dist/cache/index.d.ts +3 -0
  7. package/dist/cache/pending-global-evals.d.ts +42 -0
  8. package/dist/cache/pending-inits.d.ts +10 -0
  9. package/dist/cli/commands/cluster-settings.d.ts +41 -3
  10. package/dist/cli/commands/runs.d.ts +1 -0
  11. package/dist/cli.js +1252 -623
  12. package/dist/cluster/cluster-settings-reader.d.ts +53 -1
  13. package/dist/config.d.ts +27 -0
  14. package/dist/content-requirements-cache.d.ts +55 -0
  15. package/dist/db/migrations/109_cluster_settings_cache_knobs.d.ts +4 -0
  16. package/dist/db/migrations/110_cluster_settings_global_eval_knobs.d.ts +4 -0
  17. package/dist/db/migrations/111_cluster_settings_global_eval_wait.d.ts +4 -0
  18. package/dist/db/migrations/112_execution_runs_workflow_repo.d.ts +4 -0
  19. package/dist/db/migrations/113_execution_runs_workflow_repo_index.d.ts +30 -0
  20. package/dist/db/migrations/114_ingest_queue_claim.d.ts +4 -0
  21. package/dist/db/migrations/115_global_workflows_cluster_switch.d.ts +9 -0
  22. package/dist/db/types.d.ts +60 -2
  23. package/dist/metrics/agent-metrics-aggregator.d.ts +2 -2
  24. package/dist/metrics/prometheus.d.ts +59 -0
  25. package/dist/orchestrator-core.d.ts +12 -1
  26. package/dist/pipeline/content-filter.d.ts +71 -0
  27. package/dist/pipeline/dispatch-matched-workflow.d.ts +247 -8
  28. package/dist/pipeline/global-eval-round.d.ts +293 -0
  29. package/dist/pipeline/job-contexts.d.ts +16 -17
  30. package/dist/pipeline/process-webhook.d.ts +7 -0
  31. package/dist/pipeline/processor.d.ts +56 -2
  32. package/dist/pipeline/route-or-dispatch-jobs.d.ts +6 -0
  33. package/dist/pipeline/test-pipeline.d.ts +12 -0
  34. package/dist/pipeline/webhook-payload-store.d.ts +20 -0
  35. package/dist/provenance/backfill-run.d.ts +10 -1
  36. package/dist/provider-registry.d.ts +38 -3
  37. package/dist/providers/github/check-status-poster.d.ts +22 -3
  38. package/dist/providers/github/commit-message.d.ts +20 -0
  39. package/dist/providers/github/file-contents.d.ts +40 -0
  40. package/dist/providers/github/index.d.ts +2 -0
  41. package/dist/providers/universal-git/config.d.ts +2 -0
  42. package/dist/providers/universal-git/normalizer.d.ts +10 -0
  43. package/dist/queue/cleanup.d.ts +7 -1
  44. package/dist/queue/job-queue.d.ts +69 -6
  45. package/dist/queue/terminalize-unroutable.d.ts +13 -0
  46. package/dist/registration/registration-run-match.d.ts +47 -0
  47. package/dist/reporting/check-run-reporter.d.ts +52 -1
  48. package/dist/reporting/execution-tracker.d.ts +117 -7
  49. package/dist/reporting/log-chunk-sink.d.ts +8 -5
  50. package/dist/routes/admin-org-settings.d.ts +5 -0
  51. package/dist/routes/admin.d.ts +6 -0
  52. package/dist/scaler/manager.d.ts +10 -8
  53. package/dist/secrets/pg-secret-store.d.ts +20 -2
  54. package/dist/security/global-workflow-policy.d.ts +52 -12
  55. package/dist/server.js +27579 -23783
  56. package/dist/standalone.js +5865 -2211
  57. package/dist/webhook/ingest-accept.d.ts +70 -0
  58. package/dist/webhook/ingest-overflow-buffer.d.ts +35 -4
  59. package/dist/webhook/ingest-overflow-replayer.d.ts +50 -6
  60. package/dist/ws/agent-handler.d.ts +3 -0
  61. package/dist/ws/dashboard-global-workflows-handler.d.ts +30 -9
  62. package/dist/ws/execution-status-frame.d.ts +32 -0
  63. package/dist/ws/platform-client.d.ts +14 -0
  64. package/dist/ws/test-relay-handlers.d.ts +35 -10
  65. package/installer-image-digests.json +3 -3
  66. package/package.json +4 -4
  67. package/sbom.spdx.json +57 -52
  68. package/dist/pipeline/inline-eval.d.ts +0 -44
@@ -72,6 +72,10 @@ export interface MockDbMocks {
72
72
  insertExecute: ReturnType<typeof vi.fn>;
73
73
  insertExecuteTakeFirstOrThrow: ReturnType<typeof vi.fn>;
74
74
  onConflict: ReturnType<typeof vi.fn>;
75
+ /** The upsert's ON CONFLICT DO UPDATE SET payload — shared across both the
76
+ * column() and columns() shapes, so a test can assert what an existing row
77
+ * is actually updated with. */
78
+ doUpdateSet: ReturnType<typeof vi.fn>;
75
79
  updateTable: ReturnType<typeof vi.fn>;
76
80
  updateSet: ReturnType<typeof vi.fn>;
77
81
  updateWhere: ReturnType<typeof vi.fn>;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Comparing a self-reported agent version against a feature's minimum.
3
+ *
4
+ * An agent reports its own package version in `agent.register`
5
+ * (`agentRegisterSchema.version`), and the field is optional — an agent old
6
+ * enough to omit it predates every feature this module gates on. The
7
+ * orchestrator and the agent are deployed and upgraded independently
8
+ * (`.claude/rules/compatibility.md`), so a feature the orchestrator ships is
9
+ * routinely dispatched to a fleet that cannot run it, and the version is the
10
+ * only fact the orchestrator has about what the fleet understands.
11
+ */
12
+ /** A version's release triple, with any prerelease suffix dropped. */
13
+ type VersionBase = [number, number, number];
14
+ /**
15
+ * Parse `MAJOR.MINOR.PATCH`, ignoring any `-prerelease` suffix, or `null` when
16
+ * the string is not a version at all.
17
+ *
18
+ * The prerelease suffix is dropped rather than ordered because the suffixes in
19
+ * play are build counters from the dev registry (`0.5.0-9159`), not semver
20
+ * release candidates. Strict semver orders those BELOW `0.5.0`, which would
21
+ * read every staging agent as too old for a feature it in fact carries.
22
+ */
23
+ export declare function parseVersionBase(version: string): VersionBase | null;
24
+ /**
25
+ * True when `version` is at least `minimum`, comparing release bases only.
26
+ *
27
+ * An absent or unparseable version returns `false`: the orchestrator learns
28
+ * nothing from it, and a feature that needs a specific agent build must not
29
+ * treat "we cannot tell" as "yes". Callers decide what an all-unknown fleet
30
+ * means for them — this function only reports what the version proves.
31
+ */
32
+ export declare function agentVersionAtLeast(version: string | null | undefined, minimum: string): boolean;
33
+ export {};
34
+ //# sourceMappingURL=agent-version.d.ts.map
@@ -2,6 +2,26 @@ import type { AgentRegistry } from './registry.js';
2
2
  import { type JobQueue, type QueuedJob, type QueuedJobInput } from '../queue/job-queue.js';
3
3
  import type { ScaleResult, ScalerRedispatchTrigger } from '../scaler/types.js';
4
4
  import type { ResourceRequest } from '@kici-dev/engine';
5
+ /**
6
+ * Why a freed agent's queue drain claimed nothing.
7
+ *
8
+ * Named rather than inlined because the value travels: it is the `reason`
9
+ * field of the drain-declined log line and the assertion subject of the tests
10
+ * that pin each exit. The five members are the five ways
11
+ * `Dispatcher.drainForAgent` can return without a dispatch.
12
+ */
13
+ export declare enum AgentDrainDecline {
14
+ /** The agent id is not in the in-memory registry (already disconnected). */
15
+ NotRegistered = "agent-not-registered",
16
+ /** This coordinator is draining and must not claim new work. */
17
+ CoordinatorDraining = "coordinator-draining",
18
+ /** The agent is already at `maxConcurrency`. */
19
+ NoCapacity = "no-capacity",
20
+ /** The host is flagged reboot-pending, so anything dispatched would be lost. */
21
+ RebootPending = "reboot-pending",
22
+ /** Nothing in the queue matched this agent's labels or pin. */
23
+ NoMatchingJob = "no-matching-job"
24
+ }
5
25
  /**
6
26
  * Default per-pass cap on how many pending jobs a capacity-freed re-drive
7
27
  * re-offers to the scaler. Bounds the burst so a single free event cannot storm
@@ -85,6 +105,12 @@ export declare class Dispatcher {
85
105
  * the same tick.
86
106
  */
87
107
  private redriveInFlight;
108
+ /**
109
+ * Single-flight guard for `redrivePendingToConnectedAgents`. The per-coord
110
+ * safety-net tick must not overlap itself, so a slow re-drive can't be
111
+ * re-entered by the next interval fire and double-scan the same pending rows.
112
+ */
113
+ private pendingRedriveInFlight;
88
114
  /**
89
115
  * Tracks which jobs are dispatched to which agents.
90
116
  * Used for marking jobs as failed on agent disconnect.
@@ -268,6 +294,12 @@ export declare class Dispatcher {
268
294
  */
269
295
  releaseRebootPending(agentId: string): Promise<void>;
270
296
  onAgentAvailable(agentId: string): Promise<void>;
297
+ /**
298
+ * Claim at most one queued job for a freed agent.
299
+ *
300
+ * Returns the reason nothing was dispatched, or null when a job was.
301
+ */
302
+ private drainForAgent;
271
303
  /** Record that a job began executing on its agent. */
272
304
  markJobStarted(jobId: string): void;
273
305
  /**
@@ -316,6 +348,44 @@ export declare class Dispatcher {
316
348
  * fresh agent is spawned bound to this job.
317
349
  */
318
350
  private redispatch;
351
+ /**
352
+ * Safety-net re-drive: deliver every pending job that a currently-connected,
353
+ * idle, matching agent could take, through the same atomic claim the drain
354
+ * uses (`dispatchBoundJob` → `dequeueById`).
355
+ *
356
+ * Runs per-coordinator and is NOT leader-gated: each coordinator drains the
357
+ * shared queue onto its OWN connected agents, and the atomic claim guarantees
358
+ * at most one agent (on any coordinator) wins each job, so concurrent ticks
359
+ * cannot double-dispatch.
360
+ *
361
+ * It closes the requeue re-drive gap. A job requeued by `handleAckExpiry` /
362
+ * `onJobRejected` / the leader ack sweep gets exactly one delivery attempt —
363
+ * `redispatch`'s single `findAvailable` + `dispatchBoundJob`. An idle matching
364
+ * agent whose own drain trigger (registration / completion / status) already
365
+ * fired before the requeue has no further trigger, so if that one attempt
366
+ * transiently misses the agent — the agent is momentarily at capacity while an
367
+ * in-flight drain holds its eagerly-claimed slot, or the agent is connected to
368
+ * a different coordinator than the one that ran the expiry — the requeued
369
+ * pending job would otherwise sit undelivered until it expired. This tick
370
+ * re-attempts delivery onto connected agents so the miss recovers on the next
371
+ * sweep.
372
+ *
373
+ * It never consults the scaler (that is `retryPendingScaleRequests`) and never
374
+ * spawns: it only places jobs onto agents already connected here.
375
+ *
376
+ * A failure propagates to the caller rather than being swallowed here — the
377
+ * per-coord interval wrapper is the single error-log site, matching the
378
+ * sibling recovery/ack sweeps that share its cadence. The `finally` only
379
+ * releases the single-flight guard.
380
+ */
381
+ redrivePendingToConnectedAgents(maxJobs?: number): Promise<number>;
382
+ /**
383
+ * Pick a connected agent that may take a pending job, or null. A pinned
384
+ * host-fanout child may run ONLY on its pinned agent — and `dequeueById`
385
+ * ignores the pin — so a pinned job is routed to its own agent and never
386
+ * offered to `findAvailable`, which would mis-deliver it to any label match.
387
+ */
388
+ private selectConnectedTargetForPending;
319
389
  /**
320
390
  * Handle agent disconnect. Starts per-job recovery timers instead of
321
391
  * immediately failing jobs, giving the agent a grace period to reconnect.
package/dist/app.d.ts CHANGED
@@ -26,6 +26,7 @@ import type { PeerRegistry } from './cluster/peer-registry.js';
26
26
  import type { JobQueue } from './queue/job-queue.js';
27
27
  import type { DedupCache } from './webhook/dedup.js';
28
28
  import type { LockFileCache } from './lockfile-cache.js';
29
+ import type { ContentRequirementsCache } from './content-requirements-cache.js';
29
30
  import type { PlatformClient } from './ws/platform-client.js';
30
31
  import type { ScalerManager } from './scaler/manager.js';
31
32
  import type { ProviderRegistry } from './provider-registry.js';
@@ -37,7 +38,8 @@ import type { ArtifactStore } from './artifacts/artifact-store.js';
37
38
  import type { DispatchCacheRefTracker } from './cache/index.js';
38
39
  import type { PendingBuildTracker } from './cache/index.js';
39
40
  import type { PendingInitTracker } from './cache/index.js';
40
- import type { PendingDynamicTracker } from './cache/index.js';
41
+ import type { PendingDynamicTracker, PendingGlobalEvalTracker } from './cache/index.js';
42
+ import type { GlobalEvalRoundCache } from './cache/index.js';
41
43
  import type { CacheStorage } from './storage/types.js';
42
44
  import type { ProvenanceTrustRoot } from './provenance/trust-root.js';
43
45
  import type { LocalSigner } from './oidc/local-dev-signer.js';
@@ -66,6 +68,7 @@ import type { CronScheduler } from './cron/cron-scheduler.js';
66
68
  import { type ConfigRouteDeps } from './routes/admin-config.js';
67
69
  import { WebhookIngestOutcome } from './pipeline/process-webhook.js';
68
70
  import type { IngestOverflowBuffer } from './webhook/ingest-overflow-buffer.js';
71
+ import type { IngestOverflowReplayer } from './webhook/ingest-overflow-replayer.js';
69
72
  import { type OverflowDelivery } from './webhook/ingest-overflow-types.js';
70
73
  import type { IngestAdmissionController } from './webhook/ingest-admission.js';
71
74
  import type { OrgIngestCapReader } from './webhook/org-ingest-cap-reader.js';
@@ -108,6 +111,7 @@ export interface AppDependencies {
108
111
  jobQueue: JobQueue;
109
112
  dedup: DedupCache;
110
113
  lockFileCache: LockFileCache;
114
+ contentRequirementsCache: ContentRequirementsCache;
111
115
  providerRegistry: ProviderRegistry;
112
116
  platformClient?: PlatformClient;
113
117
  /**
@@ -126,6 +130,13 @@ export interface AppDependencies {
126
130
  * for later replay; absent → capture disabled.
127
131
  */
128
132
  ingestOverflowBuffer?: IngestOverflowBuffer;
133
+ /**
134
+ * Drain loop for the durable ingest queue. The accept path borrows its
135
+ * claim-release so a failed pipeline hands the row back for retry through the
136
+ * same attempt ceiling the drain uses; absent → the accept path degrades to
137
+ * inline ingestion.
138
+ */
139
+ ingestOverflowReplayer?: IngestOverflowReplayer;
129
140
  /**
130
141
  * Fulfil deferred attestations on demand (mints in this process, which owns
131
142
  * the Platform WS). Backs `POST /api/v1/admin/attestations/retry`. Wired only
@@ -208,6 +219,8 @@ export interface AppDependencies {
208
219
  pendingInits?: PendingInitTracker;
209
220
  /** Pending dynamic tracker for DynamicJobFn evaluation coordination. */
210
221
  pendingDynamics?: PendingDynamicTracker;
222
+ pendingGlobalEvals?: PendingGlobalEvalTracker;
223
+ globalEvalCache?: GlobalEvalRoundCache;
211
224
  /** Commit status reporter for setting pending/success/failure/error on commits. Optional. */
212
225
  checkRunReporter?: CheckRunReporter;
213
226
  /** Execution tracker for DB persistence of execution state. Optional — requires database. */
@@ -250,6 +263,15 @@ export interface AppDependencies {
250
263
  eventEmitter?: EventEmitter;
251
264
  /** Generic webhook source manager. Optional -- if not set, generic webhooks are disabled. */
252
265
  genericSourceManager?: GenericSourceManager;
266
+ /**
267
+ * Re-register a generic source's provider bundle from its database row,
268
+ * resolving to true when a per-routing-key bundle is now registered.
269
+ *
270
+ * Built in orchestrator-core so it shares the exact dependency bag the
271
+ * startup enumeration and the LISTEN/NOTIFY drain already register with.
272
+ * Optional -- wirings without one keep the previous behaviour.
273
+ */
274
+ ensureProviderBundle?: (routingKey: string) => Promise<boolean>;
253
275
  /** GitHub direct-ingress route deps (hybrid/independent only). Optional -- if not set, the direct GitHub ingress route is not mounted. */
254
276
  githubSourceStore?: SourceStore;
255
277
  githubVerifyDeps?: VerifyInboundDeps;
@@ -0,0 +1,88 @@
1
+ /**
2
+ * LRU for completed Tier-2 global eval round results.
3
+ *
4
+ * **This cache only helps webhook redelivery and same-input re-deliveries.** Its
5
+ * key pins the source commit and a digest of the whole round input, and every
6
+ * real push carries a new source SHA — so a developer pushing twice gets two
7
+ * rounds, by design. Read it as a duplicate-delivery guard, never as a
8
+ * steady-state optimization: sizing it up buys nothing, and a hit rate near zero
9
+ * on a healthy cluster is the expected shape, not a defect.
10
+ *
11
+ * There is deliberately no TTL. A round verdict is a pure function of its
12
+ * inputs, and {@link globalEvalRoundCacheKey} covers all of them, so an entry
13
+ * cannot go stale within its own key — the same content-addressable argument the
14
+ * lock-file and content caches make for their SHA-keyed entries, without their
15
+ * branch-name escape hatch.
16
+ */
17
+ import type { GlobalEvalRoundResult } from '@kici-dev/engine';
18
+ /**
19
+ * Key one round over **every input its verdict depends on**.
20
+ *
21
+ * The three SHAs are not enough, and the gap is a wrong-answer bug rather than a
22
+ * missed-hit one. `createFilterContext` and `buildGeneratorContext` both receive
23
+ * the whole event, so a filter can branch on `event.type`, `targetBranch`,
24
+ * `changedFiles`, or anything in the raw payload. A push to `main` at commit X
25
+ * and a pull-request synchronize whose head is commit X share all three SHAs and
26
+ * genuinely deserve different verdicts: their events differ, and their
27
+ * changed-file sets are computed over different ranges. Two branches pointing at
28
+ * one commit collide the same way. `workflowRoutingKey` is in the key for the
29
+ * reason `groupCandidates` keeps it in the group key — it selects the provider
30
+ * bundle that mints the clone credentials.
31
+ *
32
+ * The candidate list and the event are folded in as a SHA-256 digest of their
33
+ * JSON, so the key stays a bounded string no matter how large a payload is. Both
34
+ * are rebuilt by the same code from the same delivery, so a genuine redelivery
35
+ * reproduces the digest; anything else is a miss, which costs a round rather
36
+ * than a wrong verdict.
37
+ *
38
+ * Returns `null` when the inputs cannot be serialized. A round that cannot be
39
+ * keyed is simply not cached — never keyed on a partial input, which is how a
40
+ * cache starts answering questions it was not asked.
41
+ */
42
+ export declare function globalEvalRoundCacheKey(args: {
43
+ workflowRepoIdentifier: string;
44
+ workflowSha: string;
45
+ workflowRoutingKey: string;
46
+ sourceSha: string;
47
+ /** The exact per-candidate payload the round job carries. */
48
+ candidates: unknown;
49
+ /** The exact event the round job carries. */
50
+ event: unknown;
51
+ }): string | null;
52
+ /**
53
+ * A round that left ANY candidate undecided is not a result worth replaying.
54
+ *
55
+ * An agent-side budget breach reports `success` with the affected candidates
56
+ * marked indeterminate — the whole round on a round-budget breach, one workflow
57
+ * on a candidate-budget breach. Caching either turns one slow round into a
58
+ * permanently replayed failure for the whole key: a webhook redelivery, which
59
+ * is exactly when an operator is retrying, would be served the stored failure
60
+ * instead of running again, and the round's own retry sits behind this cache
61
+ * read so a stored breach short-circuits that too.
62
+ *
63
+ * Requiring EVERY candidate to be decided costs almost no hit rate — an
64
+ * indeterminate verdict is the exceptional path — and buys a guarantee that is
65
+ * easy to state: nothing a redelivery replays was ever undecided.
66
+ *
67
+ * An empty candidate list is not cacheable either. For a non-empty group it
68
+ * means every candidate is about to be recorded as "no verdict", which is the
69
+ * same failure by another route.
70
+ */
71
+ export declare function isCacheableRoundResult(result: GlobalEvalRoundResult): boolean;
72
+ export declare class GlobalEvalRoundCache {
73
+ private readonly cache;
74
+ private hits;
75
+ private misses;
76
+ constructor(options: {
77
+ max: number;
78
+ });
79
+ get(key: string): GlobalEvalRoundResult | undefined;
80
+ set(key: string, value: GlobalEvalRoundResult): void;
81
+ /** Hit/miss counters for metrics and debugging. */
82
+ stats(): {
83
+ hits: number;
84
+ misses: number;
85
+ size: number;
86
+ };
87
+ }
88
+ //# sourceMappingURL=global-eval-round-cache.d.ts.map
@@ -15,4 +15,7 @@ export { PendingBuildTracker } from './pending-builds.js';
15
15
  export { PendingInitTracker } from './pending-inits.js';
16
16
  export type { InitResult } from './pending-inits.js';
17
17
  export { PendingDynamicTracker } from './pending-dynamics.js';
18
+ export { PendingGlobalEvalTracker } from './pending-global-evals.js';
19
+ export { GlobalEvalRoundCache, globalEvalRoundCacheKey, isCacheableRoundResult, } from './global-eval-round-cache.js';
20
+ export { parseGlobalEvalResult } from './pending-global-evals.js';
18
21
  //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Pending tracker for the Tier-2 global eval round.
3
+ *
4
+ * The orchestrator dispatches one round job per (event × workflow repo) and
5
+ * waits for the agent to return a verdict per candidate workflow. The
6
+ * underlying tracker logic lives in `PendingTracker<GlobalEvalRoundResult>`;
7
+ * this subclass wires the round-specific logger prefix and disconnect error.
8
+ *
9
+ * Unlike the dynamic-eval and init trackers, this round runs BEFORE any run row
10
+ * exists: its whole purpose is to decide which global workflows produce a run at
11
+ * all, so creating one up-front would defeat it.
12
+ */
13
+ import { type GlobalEvalRoundResult } from '@kici-dev/engine';
14
+ import { PendingTracker } from './pending-tracker.js';
15
+ /**
16
+ * Validate the round result carried on a `job.status` message before it reaches
17
+ * the tracker.
18
+ *
19
+ * **This is the one place the round's wire payload is validated.** The status
20
+ * message's `data` field is `z.record(z.string(), z.unknown())`, so nothing
21
+ * upstream checks `globalEvalResult` against its schema — a cast at the message
22
+ * handler would be a type assertion over an arbitrary agent-supplied value, and
23
+ * any agent that owns the round job can send one. Parsing here keeps the
24
+ * "typed but unvalidated" boundary at a single named function instead of spread
25
+ * across every consumer.
26
+ *
27
+ * The parsed value is widened back to {@link GlobalEvalRoundResult}: the schema
28
+ * is deliberately loose about `jobs` (the lock-job shape has no Zod mirror), and
29
+ * the dispatch path re-checks every generated job before it builds anything from
30
+ * one.
31
+ */
32
+ export declare function parseGlobalEvalResult(raw: unknown): {
33
+ ok: true;
34
+ value: GlobalEvalRoundResult;
35
+ } | {
36
+ ok: false;
37
+ error: string;
38
+ };
39
+ export declare class PendingGlobalEvalTracker extends PendingTracker<GlobalEvalRoundResult> {
40
+ constructor();
41
+ }
42
+ //# sourceMappingURL=pending-global-evals.d.ts.map
@@ -18,6 +18,16 @@ export interface InitResult {
18
18
  * function. The dispatch path re-materializes these into N execution jobs.
19
19
  */
20
20
  matrixValues?: Array<Record<string, string | undefined>>;
21
+ /**
22
+ * Verdict of the workflow-level `filter`, reported only when the init job was
23
+ * asked to evaluate one. `false` means the workflow does not apply to this
24
+ * event and its job must not be dispatched.
25
+ *
26
+ * Optional on purpose: an agent that predates the filter never sends it, so
27
+ * absence means "no verdict was reported" and dispatch proceeds — only an
28
+ * explicit `false` suppresses.
29
+ */
30
+ filterPassed?: boolean;
21
31
  }
22
32
  export declare class PendingInitTracker extends PendingTracker<InitResult> {
23
33
  constructor();
@@ -25,14 +25,52 @@ interface StringKnobSpec {
25
25
  expects: string;
26
26
  validate: (value: string) => boolean;
27
27
  }
28
+ /**
29
+ * One boolean knob. A third spec type for the same reason {@link StringKnobSpec}
30
+ * is separate from {@link KnobSpec}: the value parser, the PATCH body type, and
31
+ * the `show` formatter all differ. A boolean also has no bounds to validate —
32
+ * only two accepted spellings.
33
+ */
34
+ interface BooleanKnobSpec {
35
+ field: string;
36
+ flag: string;
37
+ label: string;
38
+ }
28
39
  /** camelCase → value (null clears the override). */
29
- type PatchBody = Record<string, number | string | null>;
30
- /** Parse an integer CLI flag with a minimum, exiting with an error on failure. */
31
- export declare function parseKnobValue(flag: string, value: string, min: number): number;
40
+ type PatchBody = Record<string, number | string | boolean | null>;
41
+ /**
42
+ * Parse an integer CLI flag against its knob's bounds, exiting on failure.
43
+ *
44
+ * `max` is optional because most knobs have no meaningful ceiling; the ones
45
+ * that do carry a hard boot-safety bound (see {@link KnobSpec.max}).
46
+ */
47
+ export declare function parseKnobValue(flag: string, value: string, min: number, max?: number): number;
32
48
  /** Validate a text CLI flag against its knob's shape, exiting on failure. */
33
49
  export declare function parseStringKnobValue(knob: StringKnobSpec, value: string): string;
50
+ /**
51
+ * Parse a boolean CLI flag, exiting on failure.
52
+ *
53
+ * Accepts only the exact strings `true` and `false`. A permissive parser (any
54
+ * non-empty string is true) would read `--global-workflows-enabled no` as an
55
+ * enable, which for this knob turns a typo into a security posture change.
56
+ */
57
+ export declare function parseBooleanKnobValue(knob: BooleanKnobSpec, value: string): boolean;
34
58
  /** Assemble the PATCH body from provided `set` flags (exits if none given). */
35
59
  export declare function buildClusterPatch(opts: Record<string, string | undefined>): PatchBody;
60
+ /**
61
+ * Warn when only one of the two ordered global-eval timeouts is being set.
62
+ *
63
+ * The orchestrator's wait ceiling must exceed the agent's round budget — the
64
+ * agent's budget starts only once the round job is RUNNING, so a lower ceiling
65
+ * fires on every round that merely waited for a free agent, failing every round
66
+ * silently. The server rejects the pair outright when BOTH effective values are
67
+ * stored, but it cannot compare against a column left NULL: NULL means "the
68
+ * orchestrator's configured default applies", and the route does not know that
69
+ * number. Setting one alone is exactly that blind spot, so warn here.
70
+ *
71
+ * Returns the lines rather than printing them so the check is unit-testable.
72
+ */
73
+ export declare function unpairedEvalTimeoutWarnings(patch: PatchBody): string[];
36
74
  /** Build the reset PATCH body: all knobs → null, or just the flagged ones. */
37
75
  export declare function buildClusterReset(opts: Record<string, boolean | undefined>): PatchBody;
38
76
  /**
@@ -9,6 +9,7 @@
9
9
  * runs list List execution runs (table or JSON)
10
10
  * runs show <runId> Show run detail with jobs and steps
11
11
  * runs jobs <runId> List jobs for a run (optional steps)
12
+ * runs logs <runId> --job <jobId> Print a page of one step's log lines
12
13
  * runs ephemeral-key <runId> Show scrub status of the run's key
13
14
  * runs secret-outputs <runId> List secret outputs (masked / reveal)
14
15
  *