@kici-dev/orchestrator 0.1.23 → 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 (86) hide show
  1. package/dist/agent/token-store.d.ts +4 -0
  2. package/dist/app.d.ts +27 -0
  3. package/dist/audit/access-log.d.ts +4 -0
  4. package/dist/cache/pending-inits.d.ts +3 -2
  5. package/dist/cancel/cancel-run.d.ts +2 -0
  6. package/dist/cli/api-client.d.ts +10 -0
  7. package/dist/cli/commands/attestations-list.d.ts +49 -0
  8. package/dist/cli/commands/attestations-retry.d.ts +28 -0
  9. package/dist/cli/commands/attestations-reverify.d.ts +18 -0
  10. package/dist/cli/commands/attestations.d.ts +4 -0
  11. package/dist/cli/commands/cluster.d.ts +3 -0
  12. package/dist/cli/commands/shared/versioned-upgrade.d.ts +52 -0
  13. package/dist/cli/local-github-ingress-url.d.ts +8 -0
  14. package/dist/cli.js +3308 -1011
  15. package/dist/cluster/cluster-identity.d.ts +11 -0
  16. package/dist/cluster/reconcile-identity.d.ts +50 -0
  17. package/dist/cold-store/load-access-log-range.d.ts +4 -0
  18. package/dist/config.d.ts +4 -0
  19. package/dist/dashboard/attestation-filters.d.ts +131 -0
  20. package/dist/dashboard/handler.d.ts +56 -4
  21. package/dist/db/migrations/054_local_working_tree.d.ts +15 -0
  22. package/dist/db/migrations/055_agent_token_mandatory_labels.d.ts +18 -0
  23. package/dist/db/migrations/056_execution_jobs_environments.d.ts +17 -0
  24. package/dist/db/migrations/057_step_concurrency.d.ts +4 -0
  25. package/dist/db/migrations/058_access_log_agent_label.d.ts +4 -0
  26. package/dist/db/migrations/059_attestation_verdict.d.ts +4 -0
  27. package/dist/db/migrations/060_run_trigger_actor.d.ts +4 -0
  28. package/dist/db/migrations/061_execution_runs_environment_id.d.ts +4 -0
  29. package/dist/db/migrations/062_execution_runs_agent_label.d.ts +4 -0
  30. package/dist/db/migrations/063_access_log_agent_label_index.d.ts +10 -0
  31. package/dist/db/migrations/064_execution_jobs_skipped_environments.d.ts +17 -0
  32. package/dist/db/migrations/065_pending_attestations.d.ts +22 -0
  33. package/dist/db/migrations/066_pending_attestations_rejected.d.ts +13 -0
  34. package/dist/db/types.d.ts +113 -0
  35. package/dist/environments/protection/aggregate.d.ts +43 -0
  36. package/dist/environments/protection/satisfiability.d.ts +69 -0
  37. package/dist/events/circuit-breaker.d.ts +8 -5
  38. package/dist/events/event-router.d.ts +12 -4
  39. package/dist/events/event-store.d.ts +21 -3
  40. package/dist/events/trust-store.d.ts +6 -1
  41. package/dist/events/types.d.ts +6 -1
  42. package/dist/helpers/rate-limiter.d.ts +17 -2
  43. package/dist/index.js +11 -1
  44. package/dist/metrics/prometheus.d.ts +6 -0
  45. package/dist/orchestrator-core.d.ts +8 -0
  46. package/dist/pipeline/dispatch-matched-workflow.d.ts +8 -0
  47. package/dist/pipeline/flatten-lock-steps.d.ts +11 -0
  48. package/dist/pipeline/inline-eval.d.ts +2 -1
  49. package/dist/pipeline/job-environments.d.ts +71 -0
  50. package/dist/pipeline/manual-schedule.d.ts +23 -1
  51. package/dist/pipeline/process-webhook.d.ts +14 -1
  52. package/dist/pipeline/rerun.d.ts +1 -1
  53. package/dist/pipeline/test-pipeline.d.ts +15 -0
  54. package/dist/provenance/attestation-retrier.d.ts +92 -0
  55. package/dist/provenance/backfill-run.d.ts +41 -0
  56. package/dist/provenance/pending-attestations-repo.d.ts +64 -0
  57. package/dist/provenance/trust-root.d.ts +24 -0
  58. package/dist/provenance/verify-at-ingest.d.ts +31 -0
  59. package/dist/reporting/agent-failure-category.d.ts +27 -0
  60. package/dist/reporting/agent-run-result-mapper.d.ts +13 -0
  61. package/dist/reporting/execution-tracker.d.ts +38 -6
  62. package/dist/reporting/run-aggregator.d.ts +165 -0
  63. package/dist/reporting/step-log-reader.d.ts +39 -0
  64. package/dist/routes/admin-access-log.d.ts +2 -1
  65. package/dist/routes/admin-registrations.d.ts +8 -0
  66. package/dist/routes/admin-runs.d.ts +7 -0
  67. package/dist/routes/admin.d.ts +14 -0
  68. package/dist/routes/github-webhook.d.ts +37 -0
  69. package/dist/routes/health.d.ts +9 -0
  70. package/dist/routes/webhooks.d.ts +2 -4
  71. package/dist/secrets/index.d.ts +1 -1
  72. package/dist/server.js +5326 -2100
  73. package/dist/sources/source-store.d.ts +6 -0
  74. package/dist/stale-detector/workflow-deadline-detector.d.ts +25 -0
  75. package/dist/standalone.js +19787 -17304
  76. package/dist/storage/loopback-guard.d.ts +49 -0
  77. package/dist/webhook/dedup.d.ts +14 -0
  78. package/dist/ws/agent-handler.d.ts +38 -1
  79. package/dist/ws/oidc-token-relay.d.ts +31 -21
  80. package/dist/ws/orch-rpc.d.ts +9 -0
  81. package/dist/ws/platform-client.d.ts +53 -1
  82. package/dist/ws/test-relay-handlers.d.ts +2 -0
  83. package/installer-image-digests.json +3 -3
  84. package/package.json +6 -8
  85. package/sbom.spdx.json +62 -57
  86. package/dist/secrets/crypto.d.ts +0 -49
@@ -9,13 +9,35 @@
9
9
  * eventRouter fire-and-forget. The manual path needs request/response
10
10
  * correlation to return the newRunId to the dashboard.
11
11
  */
12
+ import type { LockWorkflow, MaterializedJob } from '@kici-dev/engine';
12
13
  import type { RerunDeps } from './rerun.js';
13
14
  import type { RegistrationIndex } from '../registration/registration-index.js';
14
15
  interface ManualScheduleDeps extends RerunDeps {
15
16
  registrationIndex: RegistrationIndex;
16
17
  }
17
- 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<{
18
19
  newRunId: string;
19
20
  }>;
21
+ export declare function buildManualJobConfig(workflow: LockWorkflow, mat: MaterializedJob): {
22
+ resolvedHashFiles?: string[] | undefined;
23
+ contentHash?: string | undefined;
24
+ dispatchInputs?: Record<string, unknown> | undefined;
25
+ steps: readonly import("@kici-dev/engine").LockStepEntry[];
26
+ needs: readonly (string | {
27
+ name: string;
28
+ runOn: ("success" | "pending" | "failed" | "recovering" | "running" | "queued" | "skipped" | "cancelled" | "cancelling" | "timed_out_stale" | "drift_dropped")[];
29
+ } | {
30
+ group: string;
31
+ runOn: ("success" | "pending" | "failed" | "recovering" | "running" | "queued" | "skipped" | "cancelled" | "cancelling" | "timed_out_stale" | "drift_dropped")[];
32
+ })[];
33
+ rules: readonly import("@kici-dev/engine").LockRule[] | undefined;
34
+ name: string;
35
+ baseJobName: string;
36
+ matrixValues?: import("@kici-dev/engine").MatrixValues;
37
+ fanoutIndex?: number;
38
+ fanoutTotal?: number;
39
+ source: import("@kici-dev/engine").LockSource | undefined;
40
+ workflowName: string;
41
+ };
20
42
  export {};
21
43
  //# sourceMappingURL=manual-schedule.d.ts.map
@@ -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,7 +92,22 @@ 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
  }
102
+ /**
103
+ * Repo identity for an inline-lock (local working tree) run. Derived from the
104
+ * event payload the CLI stamps -- NOT the relay routing key, which is the
105
+ * Platform-internal `remote:<orgId>` anchor and is meaningless as a repo.
106
+ */
107
+ export declare function repoIdentityFromInlineInput(input: TestTriggerInput): {
108
+ repoIdentifier: string;
109
+ provider: string;
110
+ };
96
111
  /**
97
112
  * Process a test trigger through the shared dispatch core.
98
113
  *
@@ -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
@@ -0,0 +1,24 @@
1
+ import type { JSONWebKeySet } from 'jose';
2
+ /**
3
+ * Derive the JWKS URI for a provenance OIDC issuer. Identical to the Platform's
4
+ * own derivation (`packages/platform/src/dashboard/routes/runs.ts`) so a bundle
5
+ * verified client-side and at ingest resolve the same key set.
6
+ */
7
+ export declare function deriveJwksUri(issuer: string): string;
8
+ /**
9
+ * The orchestrator's view of the provenance trust root. The issuer arrives over
10
+ * the `auth.success` connect message for the live process, or from config/env
11
+ * (`KICI_PROVENANCE_ISSUER`) for the CLI backfill which has no live handshake.
12
+ * The JWKS is fetched lazily and cached with a single refetch-on-`kid`-miss.
13
+ */
14
+ export interface ProvenanceTrustRoot {
15
+ getIssuer(): string | null;
16
+ getJwks(kid?: string): Promise<JSONWebKeySet | null>;
17
+ setIssuer(issuer: string | null): void;
18
+ }
19
+ export declare function createProvenanceTrustRoot(opts?: {
20
+ issuer?: string | null;
21
+ fetchImpl?: typeof fetch;
22
+ ttlMs?: number;
23
+ }): ProvenanceTrustRoot;
24
+ //# sourceMappingURL=trust-root.d.ts.map
@@ -0,0 +1,31 @@
1
+ import { type AttestationVerifyStatus } from '@kici-dev/engine';
2
+ import type { CacheStorage } from '../storage/types.js';
3
+ import type { ProvenanceTrustRoot } from './trust-root.js';
4
+ export interface AttestationVerdict {
5
+ verifyStatus: AttestationVerifyStatus;
6
+ verifyReason: string | null;
7
+ verifiedAt: Date | null;
8
+ }
9
+ /**
10
+ * Compute the verification verdict for a stored provenance bundle at ingest.
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
+ *
17
+ * Fail-closed: a missing trust root, unfetchable JWKS, missing storage, or an
18
+ * unreadable bundle all yield `unverifiable` (never silently `verified`). A
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.
24
+ */
25
+ export declare function computeAttestationVerdict(opts: {
26
+ trustRoot: ProvenanceTrustRoot | undefined;
27
+ storage: CacheStorage | undefined;
28
+ storageKey: string;
29
+ logWarn?: (reason: string) => void;
30
+ }): Promise<AttestationVerdict>;
31
+ //# sourceMappingURL=verify-at-ingest.d.ts.map
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Derive a coarse, trusted run-failure classification from existing signals.
3
+ *
4
+ * Pure derivation — no heuristics, no flaky/regression detection (that's a
5
+ * deferred phase). KiCI computes this from data it already records, so the
6
+ * resulting category is a trusted field on the agent run-result.
7
+ */
8
+ import { AgentFailureCategory } from '@kici-dev/engine';
9
+ export interface FailureSignals {
10
+ /** `execution_runs.status`. */
11
+ runStatus: string;
12
+ /** A run- or job-scoped init failure was recorded. */
13
+ hasInitFailure: boolean;
14
+ /** The init-failure category (`InitFailureCategory`), if any. */
15
+ initFailureCategory: string | null;
16
+ /** Any job ended in `timed_out_stale` (or a workflow/job timeout fired). */
17
+ timedOut: boolean;
18
+ /** Any step recorded a non-zero exit code. */
19
+ anyStepNonZeroExit: boolean;
20
+ }
21
+ /**
22
+ * Returns the failure category, or null for a non-failed run (success, or still
23
+ * in flight). Precedence: init failure → timeout → cancelled → step exit →
24
+ * unknown.
25
+ */
26
+ export declare function deriveFailureCategory(s: FailureSignals): AgentFailureCategory | null;
27
+ //# sourceMappingURL=agent-failure-category.d.ts.map
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Map the canonical run detail into the provenance-tagged `AgentRunResult`.
3
+ *
4
+ * Trusted (plain): ids, enum statuses, exit codes, durations, timestamps,
5
+ * hashes, the derived failure category. Untrusted (enveloped): every name, ref,
6
+ * error string, contributor, job output value — anything sourced from the user's
7
+ * repo / contributor / process output. Secret output *values* are never emitted;
8
+ * only their key names appear.
9
+ */
10
+ import { type AgentRunResult } from '@kici-dev/engine';
11
+ import type { CanonicalRunDetail } from './run-aggregator.js';
12
+ export declare function mapToAgentRunResult(d: CanonicalRunDetail): AgentRunResult;
13
+ //# sourceMappingURL=agent-run-result-mapper.d.ts.map
@@ -23,6 +23,8 @@ export interface ExecutionContext {
23
23
  provider: string;
24
24
  repoIdentifier: string;
25
25
  sha: string;
26
+ /** True when the run executed an uploaded local working tree (`kici run remote`). */
27
+ localWorkingTree?: boolean;
26
28
  installationId?: number;
27
29
  requestId?: string;
28
30
  routingKey?: string;
@@ -38,6 +40,16 @@ export interface ExecutionContext {
38
40
  originalRunId?: string | null;
39
41
  /** User identity that triggered this re-run (null/undefined for webhook-triggered). */
40
42
  triggeredBy?: string | null;
43
+ /** Agent provenance label when triggered through an agent credential. */
44
+ triggeredByAgentLabel?: string | null;
45
+ /**
46
+ * Provider login of the person who triggered the run (pusher / PR author).
47
+ * Captured for all event types; forwarded to the Platform run projection and
48
+ * used to resolve actor-scope notifications.
49
+ */
50
+ triggerActorUsername?: string | null;
51
+ /** Immutable provider user id of the triggering actor (preferred for resolution). */
52
+ triggerActorUserId?: string | null;
41
53
  /** Workflow-level concurrency config from the lock file. */
42
54
  concurrency?: {
43
55
  cancelInProgress?: boolean;
@@ -112,7 +124,9 @@ export interface ExecutionTrackerDeps {
112
124
  * Structured init-failure signal. Set for synthetic rejected-* / init-failed-*
113
125
  * jobs that never started. Persisted in execution_jobs.init_failure.
114
126
  */
115
- initFailure?: InitFailure) => void;
127
+ initFailure?: InitFailure,
128
+ /** Ordered bound deployment-environment names for this job (multi-env jobs). */
129
+ environments?: string[]) => void;
116
130
  /**
117
131
  * Optional callback to emit run.event messages to Platform.
118
132
  * Fires at orchestrator lifecycle points (dispatch, agent assignment, job start/complete).
@@ -208,6 +222,9 @@ export declare class ExecutionTracker {
208
222
  waveGated?: boolean;
209
223
  waveMaxParallel?: number;
210
224
  waveFailFast?: boolean;
225
+ environments?: string[];
226
+ skippedEnvironments?: string[];
227
+ envWarning?: string;
211
228
  }>, routingKey?: string,
212
229
  /** Secret context names dispatched with jobs (for context-disable job lookup). */
213
230
  dispatchedContexts?: string[],
@@ -229,11 +246,22 @@ export declare class ExecutionTracker {
229
246
  /** Workflow-level wall-clock timeout in ms from the lock file. Sets the run deadline. */
230
247
  workflowTimeoutMs?: number,
231
248
  /** Run mode for idempotent steps; non-apply labels the run a check-mode preview. */
232
- checkMode?: string): Promise<void>;
233
- /**
234
- * Add additional jobs to an already-started execution run.
235
- * Used when build jobs are tracked early and regular jobs are dispatched later.
236
- */
249
+ checkMode?: string,
250
+ /** True when the run executes an uploaded local working tree (`kici run remote`). */
251
+ localWorkingTree?: boolean,
252
+ /** Provider login of the triggering actor (pusher / PR author). */
253
+ triggerActorUsername?: string | null,
254
+ /** Immutable provider user id of the triggering actor. */
255
+ triggerActorUserId?: string | null,
256
+ /** Agent provenance label when triggered through an agent credential. */
257
+ triggeredByAgentLabel?: string | null): Promise<void>;
258
+ /**
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.
263
+ */
264
+ private insertTrackedJobRows;
237
265
  /**
238
266
  * Find the synthetic needs-pending job ID for a given job name in a run.
239
267
  * Used by dispatchReadyJob to locate the placeholder entry before replacing it.
@@ -283,6 +311,9 @@ export declare class ExecutionTracker {
283
311
  baseJobName?: string;
284
312
  variantKind?: string;
285
313
  variantLabel?: string;
314
+ environments?: string[];
315
+ skippedEnvironments?: string[];
316
+ envWarning?: string;
286
317
  }>, dispatchedContexts?: string[],
287
318
  /** Synthetic job ID to replace (e.g. needs-pending-deploy-{uuid}). */
288
319
  replaceSyntheticId?: string): Promise<void>;
@@ -551,6 +582,7 @@ export declare class ExecutionTracker {
551
582
  parentRunId?: string | null;
552
583
  originalRunId?: string | null;
553
584
  triggeredBy?: string | null;
585
+ triggeredByAgentLabel?: string | null;
554
586
  failureReason?: string;
555
587
  jobCount: number;
556
588
  startedAt: number;
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Shared run-detail aggregator.
3
+ *
4
+ * One source of truth for "DB rows → canonical nested run detail (jobs with
5
+ * nested steps + needs + outputs)". The dashboard handler and the agent-facing
6
+ * structured run-result route both build on the same `buildRunDetailJobs` /
7
+ * `mapRunDetailStep` mapping, so the row→jobs shape never diverges between the
8
+ * two read surfaces.
9
+ *
10
+ * `aggregateRunDetail` is the warm-path convenience used by the agent route: it
11
+ * runs the run-header + per-run batch queries and returns the canonical detail.
12
+ * (Cold-store fallback stays a dashboard-handler concern — the agent read path
13
+ * is warm-only in v1.)
14
+ */
15
+ import type { Kysely } from 'kysely';
16
+ import type { ExecutionJobStatus, InitFailure } from '@kici-dev/engine';
17
+ import type { Database } from '../db/types.js';
18
+ /** A run-detail step row as queried from execution_steps (warm + cold paths). */
19
+ export interface RunDetailStepRow {
20
+ step_index: number;
21
+ step_name: string;
22
+ status: string;
23
+ started_at: Date | null;
24
+ completed_at: Date | null;
25
+ duration_ms: number | null;
26
+ exit_code: number | null;
27
+ error_message: string | null;
28
+ step_type: string;
29
+ secrets_accessed: string | null;
30
+ check_outcome: string | null;
31
+ drift_summary: string | null;
32
+ concurrency_kind: string | null;
33
+ group_id: string | null;
34
+ }
35
+ /** Map a step row to the dashboard run-detail step shape (epoch-ms timestamps). */
36
+ export declare function mapRunDetailStep(step: RunDetailStepRow): {
37
+ groupId?: string | undefined;
38
+ concurrencyKind?: string | undefined;
39
+ driftSummary?: string | undefined;
40
+ checkOutcome?: string | undefined;
41
+ secretsAccessed: string | null;
42
+ stepType?: string | undefined;
43
+ stepIndex: number;
44
+ stepName: string;
45
+ status: string;
46
+ startedAt: number | null;
47
+ completedAt: number | null;
48
+ durationMs: number | null;
49
+ exitCode: number | null;
50
+ errorMessage: string | null;
51
+ };
52
+ /** A run-detail job row as queried from execution_jobs (warm + cold paths). */
53
+ export interface RunDetailJobRow {
54
+ job_id: string;
55
+ job_name: string;
56
+ status: string;
57
+ matrix_values: unknown;
58
+ base_job_name: string | null;
59
+ variant_kind: string | null;
60
+ variant_label: string | null;
61
+ started_at: Date | null;
62
+ completed_at: Date | null;
63
+ duration_ms: number | null;
64
+ agent_id: string | null;
65
+ error_message: string | null;
66
+ runs_on_labels: unknown;
67
+ environments: unknown;
68
+ skipped_environments: unknown;
69
+ env_warning: string | null;
70
+ outputs: unknown;
71
+ init_failure: unknown;
72
+ }
73
+ /** Lookups threaded into {@link buildRunDetailJobs} from the per-run batch queries. */
74
+ export interface RunDetailJobLookups {
75
+ stepsByJob: Map<string, RunDetailStepRow[]>;
76
+ secretKeysByJob: Map<string, string[]>;
77
+ needsByJob: Map<string, Array<{
78
+ upstreamName: string;
79
+ runOn: ExecutionJobStatus[];
80
+ }>>;
81
+ }
82
+ /** Map queried job + step rows into the dashboard run-detail job DTO shape. */
83
+ export declare function buildRunDetailJobs(jobs: RunDetailJobRow[], lookups: RunDetailJobLookups): {
84
+ needs: {
85
+ upstreamName: string;
86
+ runOn: ExecutionJobStatus[];
87
+ }[] | null;
88
+ steps: {
89
+ groupId?: string | undefined;
90
+ concurrencyKind?: string | undefined;
91
+ driftSummary?: string | undefined;
92
+ checkOutcome?: string | undefined;
93
+ secretsAccessed: string | null;
94
+ stepType?: string | undefined;
95
+ stepIndex: number;
96
+ stepName: string;
97
+ status: string;
98
+ startedAt: number | null;
99
+ completedAt: number | null;
100
+ durationMs: number | null;
101
+ exitCode: number | null;
102
+ errorMessage: string | null;
103
+ }[];
104
+ initFailure?: {
105
+ scope: "job" | "run";
106
+ category: "secret_resolution" | "install_secrets" | "lock_resolution" | "build_coordination" | "environment_rules" | "dynamic_eval" | "no_agent" | "matrix_expansion";
107
+ message: string;
108
+ jobName?: string | undefined;
109
+ } | undefined;
110
+ jobId: string;
111
+ jobName: string;
112
+ status: string;
113
+ matrixValues: Record<string, unknown> | null;
114
+ baseJobName: string | null;
115
+ variantKind: string | null;
116
+ variantLabel: string | null;
117
+ startedAt: number | null;
118
+ completedAt: number | null;
119
+ durationMs: number | null;
120
+ agentId: string | null;
121
+ orchestratorId: null;
122
+ errorMessage: string | null;
123
+ runsOnLabels: string[] | null;
124
+ environments: string[] | null;
125
+ skippedEnvironments: string[] | null;
126
+ envWarning: string | null;
127
+ outputs: any;
128
+ secretOutputKeys: string[] | null;
129
+ }[];
130
+ /** A dashboard-shaped run-detail job (epoch-ms timestamps). */
131
+ export type CanonicalRunDetailJob = ReturnType<typeof buildRunDetailJobs>[number];
132
+ /**
133
+ * Canonical run detail: the run header (raw `Date` timestamps) plus the
134
+ * dashboard-shaped nested jobs. The agent mapper formats timestamps to ISO and
135
+ * wraps untrusted fields; the dashboard handler consumes `buildRunDetailJobs`
136
+ * directly.
137
+ */
138
+ export interface CanonicalRunDetail {
139
+ runId: string;
140
+ workflowName: string;
141
+ status: string;
142
+ provider: string;
143
+ repoIdentifier: string;
144
+ ref: string;
145
+ sha: string;
146
+ /** Best-effort base commit from provider context; null when unavailable. */
147
+ baseSha: string | null;
148
+ startedAt: Date | null;
149
+ completedAt: Date | null;
150
+ durationMs: number | null;
151
+ trustTier: string | null;
152
+ contributorUsername: string | null;
153
+ triggeredBy: string | null;
154
+ failureReason: string | null;
155
+ /** Run-scoped init failure (the run never executed a step). */
156
+ initFailure: InitFailure | null;
157
+ routingKey: string | null;
158
+ jobs: CanonicalRunDetailJob[];
159
+ }
160
+ /**
161
+ * Aggregate a run into the canonical nested detail (warm path). Returns null
162
+ * when the run row is absent in PostgreSQL.
163
+ */
164
+ export declare function aggregateRunDetail(db: Kysely<Database>, runId: string): Promise<CanonicalRunDetail | null>;
165
+ //# sourceMappingURL=run-aggregator.d.ts.map