@kici-dev/orchestrator 0.1.24 → 0.1.25

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 (60) hide show
  1. package/dist/app.d.ts +24 -0
  2. package/dist/audit/access-log.d.ts +4 -0
  3. package/dist/cancel/cancel-run.d.ts +2 -0
  4. package/dist/cli/api-client.d.ts +2 -0
  5. package/dist/cli/commands/attestations-list.d.ts +49 -0
  6. package/dist/cli/commands/attestations-retry.d.ts +28 -0
  7. package/dist/cli/commands/attestations.d.ts +2 -1
  8. package/dist/cli/commands/cluster.d.ts +3 -0
  9. package/dist/cli/local-github-ingress-url.d.ts +8 -0
  10. package/dist/cli.js +1021 -404
  11. package/dist/cluster/cluster-identity.d.ts +11 -0
  12. package/dist/cluster/reconcile-identity.d.ts +50 -0
  13. package/dist/cold-store/load-access-log-range.d.ts +4 -0
  14. package/dist/config.d.ts +2 -0
  15. package/dist/dashboard/attestation-filters.d.ts +62 -1
  16. package/dist/dashboard/handler.d.ts +24 -4
  17. package/dist/db/migrations/061_execution_runs_environment_id.d.ts +4 -0
  18. package/dist/db/migrations/062_execution_runs_agent_label.d.ts +4 -0
  19. package/dist/db/migrations/063_access_log_agent_label_index.d.ts +10 -0
  20. package/dist/db/migrations/064_execution_jobs_skipped_environments.d.ts +17 -0
  21. package/dist/db/migrations/065_pending_attestations.d.ts +22 -0
  22. package/dist/db/migrations/066_pending_attestations_rejected.d.ts +13 -0
  23. package/dist/db/types.d.ts +58 -0
  24. package/dist/environments/protection/satisfiability.d.ts +14 -9
  25. package/dist/events/circuit-breaker.d.ts +8 -5
  26. package/dist/events/event-router.d.ts +12 -4
  27. package/dist/events/event-store.d.ts +21 -3
  28. package/dist/events/trust-store.d.ts +6 -1
  29. package/dist/events/types.d.ts +6 -1
  30. package/dist/helpers/rate-limiter.d.ts +17 -2
  31. package/dist/index.js +2 -1
  32. package/dist/metrics/prometheus.d.ts +6 -0
  33. package/dist/pipeline/dispatch-matched-workflow.d.ts +6 -0
  34. package/dist/pipeline/manual-schedule.d.ts +1 -1
  35. package/dist/pipeline/process-webhook.d.ts +14 -1
  36. package/dist/pipeline/rerun.d.ts +1 -1
  37. package/dist/pipeline/test-pipeline.d.ts +6 -0
  38. package/dist/provenance/attestation-retrier.d.ts +92 -0
  39. package/dist/provenance/backfill-run.d.ts +41 -0
  40. package/dist/provenance/pending-attestations-repo.d.ts +64 -0
  41. package/dist/provenance/verify-at-ingest.d.ts +10 -3
  42. package/dist/reporting/execution-tracker.d.ts +15 -3
  43. package/dist/reporting/run-aggregator.d.ts +5 -1
  44. package/dist/routes/admin-access-log.d.ts +2 -1
  45. package/dist/routes/admin.d.ts +14 -0
  46. package/dist/routes/github-webhook.d.ts +37 -0
  47. package/dist/routes/health.d.ts +9 -0
  48. package/dist/routes/webhooks.d.ts +2 -4
  49. package/dist/server.js +3462 -1886
  50. package/dist/sources/source-store.d.ts +6 -0
  51. package/dist/stale-detector/workflow-deadline-detector.d.ts +25 -0
  52. package/dist/standalone.js +19335 -18258
  53. package/dist/webhook/dedup.d.ts +14 -0
  54. package/dist/ws/agent-handler.d.ts +23 -0
  55. package/dist/ws/oidc-token-relay.d.ts +31 -21
  56. package/dist/ws/orch-rpc.d.ts +9 -0
  57. package/dist/ws/platform-client.d.ts +18 -1
  58. package/dist/ws/test-relay-handlers.d.ts +2 -0
  59. package/package.json +5 -5
  60. package/sbom.spdx.json +55 -50
@@ -1,13 +1,21 @@
1
1
  /**
2
2
  * Sliding-window rate limiter.
3
3
  *
4
- * Tracks timestamps of recent events per key and rejects new events
5
- * once the count within the window exceeds the configured maximum.
4
+ * Tracks timestamps of recent events per key and rejects new events once the
5
+ * count within the window exceeds the configured maximum.
6
+ *
7
+ * The internal Map stays bounded to the recently-active key set. A checked key
8
+ * whose window has fully expired is dropped before this call decides whether to
9
+ * record a new timestamp, so a call that records nothing (for example when the
10
+ * limit is zero) leaves no residual bucket. Keys that are never checked again
11
+ * are reclaimed by an amortized sweep that runs at most once per window, so the
12
+ * per-call hot path stays O(1) amortized even though a single sweep is O(n).
6
13
  */
7
14
  export declare class SlidingWindowRateLimiter {
8
15
  private readonly maxPerWindow;
9
16
  private readonly windowMs;
10
17
  private state;
18
+ private lastSweepAt;
11
19
  constructor(maxPerWindow: number, windowMs?: number);
12
20
  /**
13
21
  * Check if a new event is allowed for the given key.
@@ -18,6 +26,13 @@ export declare class SlidingWindowRateLimiter {
18
26
  allowed: boolean;
19
27
  retryAfterMs?: number;
20
28
  };
29
+ /**
30
+ * Evict buckets whose newest timestamp is outside the window. Runs at most
31
+ * once per window so the amortized cost per check() stays O(1).
32
+ */
33
+ private maybeSweep;
34
+ /** Number of tracked keys currently held in the internal map. */
35
+ get size(): number;
21
36
  reset(): void;
22
37
  }
23
38
  //# sourceMappingURL=rate-limiter.d.ts.map
package/dist/index.js CHANGED
@@ -16,7 +16,6 @@ import { Octokit } from "@octokit/rest";
16
16
  import { createAppAuth } from "@octokit/auth-app";
17
17
  import { createServer } from "node:http";
18
18
  import { exec } from "node:child_process";
19
- import.meta.url;
20
19
  //#endregion
21
20
  //#region src/storage/s3.ts
22
21
  /**
@@ -1842,6 +1841,8 @@ var AdminApiClient = class {
1842
1841
  if (opts?.from) params.set("from", opts.from);
1843
1842
  if (opts?.to) params.set("to", opts.to);
1844
1843
  if (opts?.q) params.set("q", opts.q);
1844
+ if (opts?.agentLabel) params.set("agentLabel", opts.agentLabel);
1845
+ if (opts?.agentOnly) params.set("agentOnly", "true");
1845
1846
  if (opts?.limit !== void 0) params.set("limit", String(opts.limit));
1846
1847
  if (opts?.cursor) params.set("cursor", opts.cursor);
1847
1848
  const qs = params.toString();
@@ -6,6 +6,12 @@ export declare function setConfigVersion(value: number): void;
6
6
  export declare function setDeclaredHostsUnreachable(value: number): void;
7
7
  /** Set the current number of stale runs detected. */
8
8
  export declare function setStaleRunsCurrent(value: number): void;
9
+ /** Set the current number of deferred attestations awaiting a later mint. */
10
+ export declare function setPendingAttestations(value: number): void;
11
+ /** Set the age (seconds) of the oldest deferred attestation in the outbox. */
12
+ export declare function setPendingAttestationOldestAgeSeconds(value: number): void;
13
+ /** Set the current number of terminally-rejected deferred attestations. */
14
+ export declare function setRejectedAttestations(value: number): void;
9
15
  interface ScalerUsageRow {
10
16
  scaler: string;
11
17
  /** Backend type for this scaler (rollup dimension). `__global__` for the orchestrator-wide row. */
@@ -138,6 +138,12 @@ export interface DispatchMatchedWorkflowResult {
138
138
  dispatchedJobIds: string[];
139
139
  /** True when the workflow install gate paused the dispatch (held run). */
140
140
  held?: boolean;
141
+ /**
142
+ * User-visible warnings aggregated from dispatched jobs — today, one per job
143
+ * whose bound test-run environment(s) were unavailable and skipped. Surfaced
144
+ * on the accepted trigger response so the CLI can print them.
145
+ */
146
+ envWarnings?: string[];
141
147
  }
142
148
  /** Options controlling a (re-)dispatch of a matched workflow. */
143
149
  export interface DispatchMatchedWorkflowOptions {
@@ -15,7 +15,7 @@ import type { RegistrationIndex } from '../registration/registration-index.js';
15
15
  interface ManualScheduleDeps extends RerunDeps {
16
16
  registrationIndex: RegistrationIndex;
17
17
  }
18
- export declare function handleManualSchedule(registrationId: string, triggeredBy: string | null, deps: ManualScheduleDeps): Promise<{
18
+ export declare function handleManualSchedule(registrationId: string, triggeredBy: string | null, triggeredByAgentLabel: string | null, deps: ManualScheduleDeps): Promise<{
19
19
  newRunId: string;
20
20
  }>;
21
21
  export declare function buildManualJobConfig(workflow: LockWorkflow, mat: MaterializedJob): {
@@ -16,11 +16,24 @@
16
16
  * Internal helpers are pure phase functions returning typed results; the only
17
17
  * top-level export is `processWebhook`, callable from server.ts / app.ts.
18
18
  */
19
+ import { z } from 'zod';
19
20
  import type { SimulatedEvent } from '@kici-dev/engine';
20
21
  import type { WebhookInfo } from '../webhook/handler.js';
21
22
  import type { ProviderBundle } from '../provider-registry.js';
22
23
  import type { TrustResolution } from '../security/trust-resolver.js';
23
24
  import { type ProcessingDeps } from './processor.js';
25
+ /**
26
+ * Outcome of a single inbound-webhook ingestion. `duplicate` lets a direct-
27
+ * ingress route return `{ duplicate: true }` to GitHub's Recent Deliveries
28
+ * panel; `skipped` covers unknown provider / unknown event / no-repo paths;
29
+ * `processed` means the pipeline matched and dispatched (or recorded a run).
30
+ */
31
+ export declare const WebhookIngestOutcome: z.ZodEnum<{
32
+ processed: "processed";
33
+ skipped: "skipped";
34
+ duplicate: "duplicate";
35
+ }>;
36
+ export type WebhookIngestOutcome = z.infer<typeof WebhookIngestOutcome>;
24
37
  interface TrustOutcome {
25
38
  trustResolution: TrustResolution | undefined;
26
39
  /** Default 'base' for PR events; trust resolution may override to 'head'. */
@@ -52,6 +65,6 @@ export declare function resolveTrustForPR(args: {
52
65
  * 9. Match + dispatch global workflows for OTHER repos
53
66
  * 10. Forward Platform trace + record event log
54
67
  */
55
- export declare function processWebhook(info: WebhookInfo, deps: ProcessingDeps): Promise<void>;
68
+ export declare function processWebhook(info: WebhookInfo, deps: ProcessingDeps): Promise<WebhookIngestOutcome>;
56
69
  export {};
57
70
  //# sourceMappingURL=process-webhook.d.ts.map
@@ -67,7 +67,7 @@ export interface RerunDeps {
67
67
  */
68
68
  coldStore: ColdStore | null;
69
69
  }
70
- export declare function handleRerun(originalRunId: string, triggeredBy: string | null, deps: RerunDeps,
70
+ export declare function handleRerun(originalRunId: string, triggeredBy: string | null, triggeredByAgentLabel: string | null, deps: RerunDeps,
71
71
  /**
72
72
  * Phase F — routing key for the original run, forwarded by Platform
73
73
  * via the WS `run.rerun.request` payload. Required to address the
@@ -92,6 +92,12 @@ interface TestTriggerResult {
92
92
  reason?: string;
93
93
  /** Dispatched job IDs. */
94
94
  jobIds: string[];
95
+ /**
96
+ * User-visible warnings on an accepted run — today, one per job whose bound
97
+ * test-run environment(s) were unavailable (non-test or unconfigured) and
98
+ * skipped. Printed by the CLI on acceptance.
99
+ */
100
+ warnings?: string[];
95
101
  }
96
102
  /**
97
103
  * Repo identity for an inline-lock (local working tree) run. Derived from the
@@ -0,0 +1,92 @@
1
+ import type { PendingAttestationsRepo, PendingAttestationRow } from './pending-attestations-repo.js';
2
+ /** A minted token, a still-transient deferral, or a terminal rejection. */
3
+ export type RetrierMintResult = {
4
+ token: string;
5
+ expiresIn: number;
6
+ jti: string;
7
+ } | {
8
+ deferred: true;
9
+ code: string;
10
+ } | {
11
+ rejected: true;
12
+ reason: string;
13
+ };
14
+ export interface AttestationRetrierDeps {
15
+ repo: PendingAttestationsRepo;
16
+ /** Mint the deferred token (the OIDC relay with deferred params, Task 5). */
17
+ requestMint: (args: {
18
+ orchestratorId: string;
19
+ runId: string;
20
+ jobId: string;
21
+ audience: string;
22
+ deferred: {
23
+ statementHash: string;
24
+ origin: 'deferred' | 'offline-backfill';
25
+ };
26
+ }) => Promise<RetrierMintResult>;
27
+ /** Assemble + upload the bundle to object storage (initMeta included). */
28
+ uploadBundle: (args: {
29
+ runId: string;
30
+ jobId: string;
31
+ subjectDigest: string;
32
+ bundle: Record<string, unknown>;
33
+ storageKey: string;
34
+ }) => Promise<void>;
35
+ computeVerdict: (storageKey: string) => Promise<{
36
+ verifyStatus: string;
37
+ verifyReason: string | null;
38
+ verifiedAt: Date | null;
39
+ }>;
40
+ recordAttestation: (args: {
41
+ runId: string;
42
+ jobId: string;
43
+ subjectName: string;
44
+ subjectDigest: string;
45
+ storageKey: string;
46
+ mediaType: string;
47
+ verifyStatus: string;
48
+ verifyReason: string | null;
49
+ verifiedAt: Date | null;
50
+ }) => Promise<void>;
51
+ /** Replay the run/job rows the Platform missed (offline-backfill only). */
52
+ backfillRun: (runId: string) => Promise<void>;
53
+ setMetrics: (count: number, oldestCreatedAt: Date | null, rejected: number) => void;
54
+ isLeader: () => boolean;
55
+ orchestratorId: string;
56
+ intervalMs: number;
57
+ provenanceStorageKey: (runId: string, jobId: string, subjectDigest: string) => string;
58
+ }
59
+ export declare class AttestationRetrier {
60
+ private readonly deps;
61
+ private interval;
62
+ private running;
63
+ constructor(deps: AttestationRetrierDeps);
64
+ /**
65
+ * Start the periodic timer. Each tick self-gates on `isLeader()`, so it is
66
+ * safe to start on every instance — only the current Raft leader acts, and
67
+ * the `attestations` unique index makes fulfilment idempotent regardless.
68
+ */
69
+ start(): void;
70
+ onBecomeLeader(): void;
71
+ onLoseLeadership(): void;
72
+ stop(): void;
73
+ /** On-reconnect trigger (Platform WS re-authenticated). Leader-gated inside tick. */
74
+ triggerNow(): void;
75
+ tick(): Promise<void>;
76
+ /**
77
+ * Fulfil the targeted pending rows once and report counts. Used by the manual
78
+ * `kici-admin attestations retry` path (via the orchestrator admin API): the
79
+ * operator explicitly requested a drain, so this is NOT leader-gated — the
80
+ * `attestations` unique index keeps a concurrent leader tick idempotent.
81
+ */
82
+ runOnce(opts?: {
83
+ runId?: string;
84
+ includeRejected?: boolean;
85
+ }): Promise<{
86
+ minted: number;
87
+ stillPending: number;
88
+ rejected: number;
89
+ }>;
90
+ fulfilOne(row: PendingAttestationRow): Promise<'minted' | 'rejected' | 'deferred'>;
91
+ }
92
+ //# sourceMappingURL=attestation-retrier.d.ts.map
@@ -0,0 +1,41 @@
1
+ import type { OrchestratorToPlatformMessage } from '@kici-dev/engine';
2
+ /** The local `execution_runs` fields the backfill needs (production reads via Kysely). */
3
+ export interface BackfillRunRow {
4
+ run_id: string;
5
+ workflow_name: string;
6
+ status: string;
7
+ repo_identifier: string | null;
8
+ provider: string | null;
9
+ local_working_tree: boolean | null;
10
+ sha: string | null;
11
+ ref: string | null;
12
+ job_count: number | null;
13
+ started_at: Date | null;
14
+ completed_at: Date | null;
15
+ duration_ms: number | null;
16
+ }
17
+ /** The local `execution_jobs` fields the backfill needs. */
18
+ export interface BackfillJobRow {
19
+ run_id: string;
20
+ job_id: string;
21
+ job_name: string;
22
+ status: string;
23
+ started_at: Date | null;
24
+ completed_at: Date | null;
25
+ agent_id: string | null;
26
+ orchestrator_id: string | null;
27
+ }
28
+ export interface BackfillRunDeps {
29
+ send: (message: OrchestratorToPlatformMessage) => void;
30
+ loadRun: (runId: string) => Promise<BackfillRunRow | null>;
31
+ loadJobs: (runId: string) => Promise<BackfillJobRow[]>;
32
+ }
33
+ /**
34
+ * Send one `execution.status` (terminal) followed by one `job.status.forward`
35
+ * per job, populating exactly the fields the Platform's `handler.ts` upserts.
36
+ * Ordered execution.status FIRST so the run row exists before the job rows
37
+ * reference it. A no-op (throws) when the run is not found locally — the caller
38
+ * leaves the pending row `deferred` with a clear error.
39
+ */
40
+ export declare function backfillRunToPlatform(deps: BackfillRunDeps, runId: string): Promise<void>;
41
+ //# sourceMappingURL=backfill-run.d.ts.map
@@ -0,0 +1,64 @@
1
+ import { type Kysely } from 'kysely';
2
+ import type { AttestationOrigin } from '@kici-dev/engine';
3
+ import type { Database, PendingAttestationRow } from '../db/types.js';
4
+ /** The non-`live` AttestationOrigin values a pending row can carry. */
5
+ export type PendingAttestationOrigin = Exclude<AttestationOrigin, 'live'>;
6
+ export interface PendingAttestationInput {
7
+ id: string;
8
+ runId: string;
9
+ jobId: string;
10
+ subjectName: string;
11
+ subjectDigest: string;
12
+ audience: string;
13
+ dsseEnvelope: unknown;
14
+ publicKey: unknown;
15
+ mediaType: string;
16
+ statementHash: string;
17
+ originKind: PendingAttestationOrigin;
18
+ }
19
+ export type { PendingAttestationRow } from '../db/types.js';
20
+ /** Typed CRUD over the deferred-attestation outbox (shared orchestrator DB). */
21
+ export declare class PendingAttestationsRepo {
22
+ private readonly db;
23
+ constructor(db: Kysely<Database>);
24
+ /** Insert a pending row; re-deferring the same subject is a no-op. */
25
+ insert(row: PendingAttestationInput): Promise<void>;
26
+ /**
27
+ * List pending rows oldest-first, optionally scoped to a run. Terminally
28
+ * rejected rows (`rejected_at IS NOT NULL`) are excluded — the retrier never
29
+ * re-picks a row the Platform definitively cannot mint.
30
+ */
31
+ list(opts?: {
32
+ runId?: string;
33
+ limit?: number;
34
+ }): Promise<PendingAttestationRow[]>;
35
+ /** Bump the attempt counter + record the last error on a still-failing retry. */
36
+ recordAttempt(id: string, lastError: string | null): Promise<void>;
37
+ /** Remove a fulfilled pending row. */
38
+ delete(id: string): Promise<void>;
39
+ /**
40
+ * Outbox depth + oldest entry, feeding the metrics gauges. Terminally rejected
41
+ * rows are excluded so the pending gauge reflects only truly-pending rows.
42
+ */
43
+ countAndOldest(): Promise<{
44
+ count: number;
45
+ oldestCreatedAt: Date | null;
46
+ }>;
47
+ /**
48
+ * Terminally reject a pending row: the Platform definitively cannot mint it
49
+ * (run/job absent). Stamps rejected_at, records the reason, and bumps the
50
+ * attempt counter. The row stays for audit but is skipped by list()/counts.
51
+ */
52
+ markRejected(id: string, reason: string): Promise<void>;
53
+ /** Count terminally-rejected rows (feeds the rejected-attestations gauge). */
54
+ countRejected(): Promise<number>;
55
+ /**
56
+ * Re-arm terminally-rejected rows so the next drain re-attempts them (used
57
+ * after an operator fixes the Platform-side run/job). Scoped to a run when
58
+ * runId is given, else clears every rejected row. Returns the count re-armed.
59
+ */
60
+ clearRejected(opts?: {
61
+ runId?: string;
62
+ }): Promise<number>;
63
+ }
64
+ //# sourceMappingURL=pending-attestations-repo.d.ts.map
@@ -9,11 +9,18 @@ export interface AttestationVerdict {
9
9
  /**
10
10
  * Compute the verification verdict for a stored provenance bundle at ingest.
11
11
  *
12
+ * Reads the bundle, selects the signing-key set by the identity token's own
13
+ * `kid` (so a kid-miss triggers the trust root's single refetch — a token
14
+ * minted with a freshly-rotated key still verifies against a briefly-stale
15
+ * cached JWKS), then verifies offline.
16
+ *
12
17
  * Fail-closed: a missing trust root, unfetchable JWKS, missing storage, or an
13
18
  * unreadable bundle all yield `unverifiable` (never silently `verified`). A
14
- * bundle that verifies false yields `failed` with the first failure code. Any
15
- * thrown error is caught and recorded as `unverifiable` — verification never
16
- * fails the upload.
19
+ * signing key that is absent from the published JWKS even after the refetch
20
+ * yields `unverifiable` (`signing_key_not_published`) "couldn't verify", not
21
+ * "proved bad". A bundle that verifies false yields `failed` with the first
22
+ * failure code. Any thrown error is caught and recorded as `unverifiable` —
23
+ * verification never fails the upload.
17
24
  */
18
25
  export declare function computeAttestationVerdict(opts: {
19
26
  trustRoot: ProvenanceTrustRoot | undefined;
@@ -40,6 +40,8 @@ export interface ExecutionContext {
40
40
  originalRunId?: string | null;
41
41
  /** User identity that triggered this re-run (null/undefined for webhook-triggered). */
42
42
  triggeredBy?: string | null;
43
+ /** Agent provenance label when triggered through an agent credential. */
44
+ triggeredByAgentLabel?: string | null;
43
45
  /**
44
46
  * Provider login of the person who triggered the run (pusher / PR author).
45
47
  * Captured for all event types; forwarded to the Platform run projection and
@@ -221,6 +223,8 @@ export declare class ExecutionTracker {
221
223
  waveMaxParallel?: number;
222
224
  waveFailFast?: boolean;
223
225
  environments?: string[];
226
+ skippedEnvironments?: string[];
227
+ envWarning?: string;
224
228
  }>, routingKey?: string,
225
229
  /** Secret context names dispatched with jobs (for context-disable job lookup). */
226
230
  dispatchedContexts?: string[],
@@ -248,11 +252,16 @@ export declare class ExecutionTracker {
248
252
  /** Provider login of the triggering actor (pusher / PR author). */
249
253
  triggerActorUsername?: string | null,
250
254
  /** Immutable provider user id of the triggering actor. */
251
- triggerActorUserId?: string | null): Promise<void>;
255
+ triggerActorUserId?: string | null,
256
+ /** Agent provenance label when triggered through an agent credential. */
257
+ triggeredByAgentLabel?: string | null): Promise<void>;
252
258
  /**
253
- * Add additional jobs to an already-started execution run.
254
- * Used when build jobs are tracked early and regular jobs are dispatched later.
259
+ * Upsert one execution_jobs row per dispatched job (idempotent on
260
+ * (run_id, job_id) to tolerate a race with an early `onJobStatus`). The
261
+ * `routing_key` is denormalized (see migration 006) so cold-store archival
262
+ * partitions by it without joining execution_runs.
255
263
  */
264
+ private insertTrackedJobRows;
256
265
  /**
257
266
  * Find the synthetic needs-pending job ID for a given job name in a run.
258
267
  * Used by dispatchReadyJob to locate the placeholder entry before replacing it.
@@ -303,6 +312,8 @@ export declare class ExecutionTracker {
303
312
  variantKind?: string;
304
313
  variantLabel?: string;
305
314
  environments?: string[];
315
+ skippedEnvironments?: string[];
316
+ envWarning?: string;
306
317
  }>, dispatchedContexts?: string[],
307
318
  /** Synthetic job ID to replace (e.g. needs-pending-deploy-{uuid}). */
308
319
  replaceSyntheticId?: string): Promise<void>;
@@ -571,6 +582,7 @@ export declare class ExecutionTracker {
571
582
  parentRunId?: string | null;
572
583
  originalRunId?: string | null;
573
584
  triggeredBy?: string | null;
585
+ triggeredByAgentLabel?: string | null;
574
586
  failureReason?: string;
575
587
  jobCount: number;
576
588
  startedAt: number;
@@ -65,6 +65,8 @@ export interface RunDetailJobRow {
65
65
  error_message: string | null;
66
66
  runs_on_labels: unknown;
67
67
  environments: unknown;
68
+ skipped_environments: unknown;
69
+ env_warning: string | null;
68
70
  outputs: unknown;
69
71
  init_failure: unknown;
70
72
  }
@@ -100,7 +102,7 @@ export declare function buildRunDetailJobs(jobs: RunDetailJobRow[], lookups: Run
100
102
  errorMessage: string | null;
101
103
  }[];
102
104
  initFailure?: {
103
- scope: "run" | "job";
105
+ scope: "job" | "run";
104
106
  category: "secret_resolution" | "install_secrets" | "lock_resolution" | "build_coordination" | "environment_rules" | "dynamic_eval" | "no_agent" | "matrix_expansion";
105
107
  message: string;
106
108
  jobName?: string | undefined;
@@ -120,6 +122,8 @@ export declare function buildRunDetailJobs(jobs: RunDetailJobRow[], lookups: Run
120
122
  errorMessage: string | null;
121
123
  runsOnLabels: string[] | null;
122
124
  environments: string[] | null;
125
+ skippedEnvironments: string[] | null;
126
+ envWarning: string | null;
123
127
  outputs: any;
124
128
  secretOutputKeys: string[] | null;
125
129
  }[];
@@ -8,7 +8,8 @@
8
8
  *
9
9
  * GET /api/v1/admin/access-log
10
10
  * Filters: orgId, actorType, actorId, action, source, outcome,
11
- * targetType, targetId, from, to, limit, cursor
11
+ * targetType, targetId, from, to, q, agentLabel, agentOnly,
12
+ * limit, cursor
12
13
  * Requires: access_log.read
13
14
  *
14
15
  * GET /api/v1/admin/access-log/:id
@@ -113,6 +113,20 @@ export interface AdminRouteDeps {
113
113
  * write is best-effort, never gating.
114
114
  */
115
115
  accessLog?: AccessLogWriter;
116
+ /**
117
+ * Optional -- fulfil deferred attestations on demand (mints in the running
118
+ * orchestrator process, which owns the Platform WS). Backs
119
+ * `POST /api/v1/admin/attestations/retry` (the `kici-admin attestations retry`
120
+ * command). Unset on a WS-only / non-coordinator admin.
121
+ */
122
+ retryAttestations?: (opts: {
123
+ runId?: string;
124
+ includeRejected?: boolean;
125
+ }) => Promise<{
126
+ minted: number;
127
+ stillPending: number;
128
+ rejected: number;
129
+ }>;
116
130
  }
117
131
  /** Hono env type for admin routes with context variables. */
118
132
  type AdminEnv = {
@@ -0,0 +1,37 @@
1
+ import { Hono } from 'hono';
2
+ import type { WebhookInfo } from '../webhook/handler.js';
3
+ import type { SourceStore } from '../sources/source-store.js';
4
+ import { type VerifyInboundDeps } from '../webhook/verify-inbound.js';
5
+ import { WebhookIngestOutcome } from '../pipeline/process-webhook.js';
6
+ /** Dependencies for the direct GitHub webhook ingress route. */
7
+ export interface GithubWebhookRoutesDeps {
8
+ /** Local source lookup (by UUID). */
9
+ sourceStore: SourceStore;
10
+ /** Inbound verification deps (db + secret store + generic source manager). */
11
+ verifyDeps: VerifyInboundDeps;
12
+ /** Pipeline entry — owns the atomic dedup claim + dispatch. */
13
+ onWebhook: (info: WebhookInfo) => Promise<WebhookIngestOutcome>;
14
+ }
15
+ /**
16
+ * Whether the orchestrator serves the direct GitHub ingress route for a given
17
+ * operating mode. Hybrid + independent run local source config; platform mode
18
+ * is relay-only (no local GitHub source to serve).
19
+ */
20
+ export declare function shouldServeGithubIngress(mode: 'platform' | 'hybrid' | 'independent'): boolean;
21
+ /**
22
+ * Direct GitHub-App webhook ingress: `POST /webhook/:orgId/github/:sourceId`.
23
+ *
24
+ * Bypasses the Platform relay. Handles both operator topologies on one route:
25
+ * - App-level repoint: the App's single webhook URL points here, so GitHub
26
+ * sends `X-GitHub-Hook-Installation-Target-Type: integration` +
27
+ * `-Target-ID: <appId>` — validated against the source's routing key.
28
+ * - Classic per-repo webhook: a repo-level hook points here with no App
29
+ * target headers — `:sourceId` already identifies the source, so the
30
+ * App-header check is skipped.
31
+ *
32
+ * Verification is the existing local `verify-inbound.ts` GitHub path (secret
33
+ * read from the orchestrator secret store; rotation-aware). Dedup + dispatch
34
+ * are the universal `processWebhook` pipeline via `onWebhook`.
35
+ */
36
+ export declare function createGithubWebhookRoutes(deps: GithubWebhookRoutesDeps): Hono;
37
+ //# sourceMappingURL=github-webhook.d.ts.map
@@ -3,6 +3,15 @@ import type { Database } from '../db/types.js';
3
3
  export interface HealthRoutesDeps {
4
4
  /** Optional DB instance for readiness checks */
5
5
  db?: Kysely<Database>;
6
+ /**
7
+ * Optional warmth latch. Returns `true` only once the orchestrator boot
8
+ * sequence has finished (all subsystems started, HTTP server serving). When
9
+ * provided, `/ready` returns `503` until it flips `true`, so a caller can
10
+ * gate on the orchestrator being ready to serve rather than merely live.
11
+ * Absent → warm defaults to `true` (callers that don't wire the latch are
12
+ * unaffected).
13
+ */
14
+ isWarm?: () => boolean;
6
15
  }
7
16
  /**
8
17
  * Create orchestrator health routes with database readiness check.
@@ -1,17 +1,15 @@
1
1
  import { Hono } from 'hono';
2
- import type { DedupCache } from '../webhook/dedup.js';
3
2
  import type { WebhookInfo } from '../webhook/handler.js';
4
3
  import type { GenericSourceManager } from '../webhook/generic-sources.js';
4
+ import { WebhookIngestOutcome } from '../pipeline/process-webhook.js';
5
5
  /**
6
6
  * Dependencies for generic webhook routes.
7
7
  */
8
8
  export interface GenericWebhookRoutesDeps {
9
9
  /** Generic source manager for source lookup and validation */
10
10
  sourceManager: GenericSourceManager;
11
- /** Delivery ID deduplication cache */
12
- dedup: DedupCache;
13
11
  /** Processing callback -- connects to the trigger matching pipeline */
14
- onWebhook: (info: WebhookInfo) => Promise<void>;
12
+ onWebhook: (info: WebhookInfo) => Promise<WebhookIngestOutcome>;
15
13
  }
16
14
  /**
17
15
  * Create generic webhook routes for non-GitHub webhook ingestion.