@kici-dev/engine 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.
Files changed (81) hide show
  1. package/dist/audit/access-log-policy.js +1 -0
  2. package/dist/audit/retention-policy.js +2 -0
  3. package/dist/context/held-run-job-id.d.ts +26 -10
  4. package/dist/context/held-run-job-id.js +30 -11
  5. package/dist/context/host-match.js +2 -1
  6. package/dist/context/index.d.ts +1 -1
  7. package/dist/context/index.js +3 -3
  8. package/dist/context/types.d.ts +10 -1
  9. package/dist/context/types.js +10 -1
  10. package/dist/index.d.ts +6 -2
  11. package/dist/index.js +17 -12
  12. package/dist/labels/compile.d.ts +2 -7
  13. package/dist/labels/compile.js +1 -10
  14. package/dist/labels.d.ts +74 -12
  15. package/dist/labels.js +82 -17
  16. package/dist/mcp/held-run-resolve.d.ts +40 -0
  17. package/dist/mcp/held-run-resolve.js +68 -15
  18. package/dist/mcp/tool-schemas.d.ts +4 -0
  19. package/dist/mcp/tool-schemas.js +13 -1
  20. package/dist/metrics/catalog-policy.d.ts +17 -3
  21. package/dist/metrics/catalog-policy.js +44 -13
  22. package/dist/metrics/metric-catalog.generated.d.ts +140 -0
  23. package/dist/metrics/metric-catalog.generated.js +168 -0
  24. package/dist/protocol/dashboard-global-workflows.js +2 -2
  25. package/dist/protocol/event-log-payload.js +1 -1
  26. package/dist/protocol/messages/access-log.d.ts +5 -0
  27. package/dist/protocol/messages/access-log.js +1 -0
  28. package/dist/protocol/messages/actor.d.ts +13 -2
  29. package/dist/protocol/messages/actor.js +16 -5
  30. package/dist/protocol/messages/common.js +1 -1
  31. package/dist/protocol/messages/dashboard-global-workflows.d.ts +29 -3
  32. package/dist/protocol/messages/dashboard-global-workflows.js +48 -4
  33. package/dist/protocol/messages/dashboard.d.ts +48 -7
  34. package/dist/protocol/messages/dashboard.js +62 -6
  35. package/dist/protocol/messages/execution-status.d.ts +44 -0
  36. package/dist/protocol/messages/execution-status.js +69 -2
  37. package/dist/protocol/messages/git-credential-relay.d.ts +78 -0
  38. package/dist/protocol/messages/git-credential-relay.js +86 -0
  39. package/dist/protocol/messages/orchestrator-agent.d.ts +138 -0
  40. package/dist/protocol/messages/orchestrator-agent.js +131 -3
  41. package/dist/protocol/messages/peer.d.ts +7 -0
  42. package/dist/protocol/messages/peer.js +18 -1
  43. package/dist/protocol/messages/platform-orchestrator.d.ts +155 -2
  44. package/dist/protocol/messages/platform-orchestrator.js +178 -19
  45. package/dist/protocol/version.d.ts +19 -2
  46. package/dist/protocol/version.js +20 -3
  47. package/dist/provenance/verify.js +11 -10
  48. package/dist/provider/check-status-poster.d.ts +37 -2
  49. package/dist/provider/contributor-resolver.d.ts +11 -3
  50. package/dist/provider/file-contents-fetcher.d.ts +39 -0
  51. package/dist/provider/file-contents-fetcher.js +2 -0
  52. package/dist/provider/git-credential.d.ts +77 -0
  53. package/dist/provider/git-credential.js +10 -0
  54. package/dist/provider/index.d.ts +4 -0
  55. package/dist/provider/index.js +2 -1
  56. package/dist/provider/webhook-normalizer.d.ts +12 -12
  57. package/dist/repo/pattern-negation.d.ts +73 -0
  58. package/dist/repo/pattern-negation.js +86 -0
  59. package/dist/safe-regex.d.ts +16 -0
  60. package/dist/safe-regex.js +24 -0
  61. package/dist/scaler/registry-auth.d.ts +18 -0
  62. package/dist/scaler/registry-auth.js +28 -0
  63. package/dist/scaler/scaler-backend-type.d.ts +35 -0
  64. package/dist/scaler/scaler-backend-type.js +39 -2
  65. package/dist/scaler/scaler-events.d.ts +79 -0
  66. package/dist/scaler/scaler-events.js +87 -0
  67. package/dist/trigger/compiled-matchers.d.ts +21 -1
  68. package/dist/trigger/compiled-matchers.js +30 -3
  69. package/dist/trigger/content-requirements.d.ts +31 -0
  70. package/dist/trigger/content-requirements.js +125 -0
  71. package/dist/trigger/decision-trace.d.ts +168 -0
  72. package/dist/trigger/decision-trace.js +207 -4
  73. package/dist/trigger/jsonpath-matcher.js +5 -1
  74. package/dist/trigger/matcher.js +61 -10
  75. package/dist/trigger/text-match.d.ts +27 -0
  76. package/dist/trigger/text-match.js +86 -0
  77. package/dist/trigger/types.d.ts +265 -23
  78. package/dist/trigger/types.js +37 -5
  79. package/dist/ws/rate-limiter.js +3 -3
  80. package/package.json +20 -3
  81. package/sbom.spdx.json +40 -15
@@ -24,25 +24,133 @@ import { z } from "zod";
24
24
  * number cannot drift between the two packages that enforce it.
25
25
  */
26
26
  const DEFAULT_APPROVAL_EXPIRY_HOURS = 72;
27
+ /** Seconds in an hour — the one conversion between the two expiry spellings. */
28
+ const SECONDS_PER_HOUR = 3600;
29
+ /**
30
+ * The same documented default as {@link DEFAULT_APPROVAL_EXPIRY_HOURS}, in the
31
+ * granularity the policy actually stores.
32
+ */
33
+ const DEFAULT_APPROVAL_EXPIRY_SECONDS = 72 * SECONDS_PER_HOUR;
34
+ /**
35
+ * The shortest expressible security-hold window.
36
+ *
37
+ * One second, not a rounder-looking number: the floor exists to stop a zero or
38
+ * negative value minting an already-expired hold, and any strictly positive
39
+ * integer discharges that. A larger floor would be an invented usability
40
+ * opinion that also puts the window back out of reach of a test.
41
+ */
42
+ const MIN_APPROVAL_EXPIRY_SECONDS = 1;
43
+ /** The longest window the Platform accepts: one year, the bound already applied to hours. */
44
+ const MAX_APPROVAL_EXPIRY_HOURS = 8760;
45
+ /** {@link MAX_APPROVAL_EXPIRY_HOURS} in seconds, so neither spelling outranges the other. */
46
+ const MAX_APPROVAL_EXPIRY_SECONDS = MAX_APPROVAL_EXPIRY_HOURS * SECONDS_PER_HOUR;
47
+ /**
48
+ * The window a policy actually means, in seconds.
49
+ *
50
+ * `approvalExpirySeconds` is the authority and `approvalExpiryHours` its coarse
51
+ * spelling, so the more specific field wins whenever both are present — the
52
+ * only rule that never discards what an operator asked for. A policy carrying
53
+ * neither (an older peer that sent no window at all) falls back to the
54
+ * documented default.
55
+ */
56
+ function approvalExpirySecondsOf(policy) {
57
+ if (policy.approvalExpirySeconds != null) return policy.approvalExpirySeconds;
58
+ if (policy.approvalExpiryHours != null) return policy.approvalExpiryHours * SECONDS_PER_HOUR;
59
+ return DEFAULT_APPROVAL_EXPIRY_SECONDS;
60
+ }
61
+ /**
62
+ * The coarse `approvalExpiryHours` view of a window stored in seconds.
63
+ *
64
+ * Rounds UP, and never below one hour. A sub-hour window has no exact hours
65
+ * spelling, and the peer reading this field is one that cannot express the real
66
+ * value anyway — so the choice is which way to be wrong. Rounding up yields a
67
+ * longer hold than asked for, which stays approvable; rounding down yields zero,
68
+ * which is the already-expired hold `MIN_APPROVAL_EXPIRY_SECONDS` exists to
69
+ * prevent.
70
+ */
71
+ function approvalExpiryHoursOf(seconds) {
72
+ return Math.max(1, Math.ceil(seconds / SECONDS_PER_HOUR));
73
+ }
74
+ /**
75
+ * The org-level fork switch: the setting an org's trust policy carries for how
76
+ * fork pull requests are treated. Each value names what the operator is
77
+ * choosing; which values the orchestrator's trust-policy gate honours, and the
78
+ * mechanism it uses, are owned there rather than here.
79
+ *
80
+ * - `ignore` — the operator declines fork pull requests.
81
+ * - `hold` — the operator requires approval first; the policy's approval expiry
82
+ * bounds how long that approval stays open.
83
+ * - `allow` — the operator permits fork pull requests to run with reduced
84
+ * privilege.
85
+ * - `reject` — deprecated in favour of `ignore`; removed at v1.0.0.
86
+ */
87
+ const ForkPolicy = z.enum([
88
+ "ignore",
89
+ "hold",
90
+ "reject",
91
+ "allow"
92
+ ]);
93
+ /**
94
+ * How much CI authority one org member holds.
95
+ *
96
+ * The level the `/kici approve` comment path reads once it has resolved a
97
+ * commenter to a KiCI user id: `write` or `admin` may release a security hold,
98
+ * `read` and `none` may not. Named here rather than spelled inline so the wire
99
+ * schema, the orchestrator's admin route, and `kici-admin` all offer exactly
100
+ * the same four values.
101
+ */
102
+ const CiTrustLevel = z.enum([
103
+ "none",
104
+ "read",
105
+ "write",
106
+ "admin"
107
+ ]);
27
108
  const trustPolicySchema = z.object({
28
- forkPolicy: z.enum([
29
- "hold",
30
- "reject",
31
- "allow"
32
- ]),
109
+ forkPolicy: ForkPolicy,
110
+ /**
111
+ * @deprecated Accepted for wire compatibility and still stored and echoed
112
+ * back, so an orchestrator or CLI on an older build keeps seeing the value it
113
+ * expects. The orchestrator's trust-policy gate does not read it, so it
114
+ * changes no dispatch outcome. Removed at v1.0.0.
115
+ */
33
116
  unknownContributorPolicy: z.enum(["hold", "reject"]),
117
+ /**
118
+ * @deprecated Accepted for wire compatibility and still stored and echoed
119
+ * back, so an orchestrator or CLI on an older build keeps seeing the value it
120
+ * expects. The orchestrator's trust-policy gate does not read it, so it
121
+ * changes no dispatch outcome. Removed at v1.0.0.
122
+ */
34
123
  workflowChangePolicy: z.enum([
35
124
  "hold",
36
125
  "reject",
37
126
  "allow"
38
127
  ]),
39
128
  /**
40
- * Hours a security hold stays open. Integer and positive: the column is
41
- * INTEGER NOT NULL, so a fractional value throws inside the fire-and-forget
42
- * persist and the policy is then silently never stored, and a zero or
43
- * negative value mints an already-expired hold.
129
+ * Hours a security hold stays open the coarse spelling of
130
+ * `approvalExpirySeconds`, kept required so an orchestrator or CLI on an
131
+ * older build still receives a window it can read. Not deprecated: it is read,
132
+ * enforced whenever no seconds value accompanies it, and remains the
133
+ * ergonomic way to say "72 hours".
134
+ *
135
+ * Integer and positive: the column is INTEGER NOT NULL, so a fractional value
136
+ * throws inside the fire-and-forget persist and the policy is then silently
137
+ * never stored, and a zero or negative value mints an already-expired hold.
138
+ */
139
+ approvalExpiryHours: z.number().int().min(1),
140
+ /**
141
+ * Seconds a security hold stays open — the authoritative window, and the one
142
+ * granularity that can express a sub-hour hold.
143
+ *
144
+ * Optional because an older Platform sends only the hours field; a frame
145
+ * without it resolves through {@link approvalExpirySecondsOf}, which falls
146
+ * back to `approvalExpiryHours * SECONDS_PER_HOUR`. When both are present this
147
+ * one wins, at every layer.
148
+ *
149
+ * Integer and at least {@link MIN_APPROVAL_EXPIRY_SECONDS} for the same two
150
+ * reasons the hours field is: the column is INTEGER, and a non-positive window
151
+ * mints an already-expired hold.
44
152
  */
45
- approvalExpiryHours: z.number().int().min(1)
153
+ approvalExpirySeconds: z.number().int().min(1).optional()
46
154
  });
47
155
  /** Trust policy update pushed from Platform to orchestrator when policy or identity links change. */
48
156
  const trustPolicyUpdateSchema = z.object({
@@ -62,12 +170,7 @@ const trustPolicyUpdateSchema = z.object({
62
170
  */
63
171
  providerUserId: z.string().nullish()
64
172
  })),
65
- memberCiTrustLevels: z.record(z.string(), z.enum([
66
- "none",
67
- "read",
68
- "write",
69
- "admin"
70
- ])),
173
+ memberCiTrustLevels: z.record(z.string(), CiTrustLevel),
71
174
  /**
72
175
  * Operator-defined teams and their member user ids. The orchestrator has no
73
176
  * identity store, so team membership is delivered here (next to
@@ -141,13 +244,13 @@ const WebhookRelayResult = z.enum([
141
244
  * 25 MiB matches GitHub's own webhook payload cap. Senders that need higher must
142
245
  * connect their orchestrator directly (bypassing Platform).
143
246
  */
144
- const WEBHOOK_RELAY_MAX_BODY_BYTES = 25 * 1024 * 1024;
247
+ const WEBHOOK_RELAY_MAX_BODY_BYTES = 26214400;
145
248
  /**
146
249
  * Recommended raw chunk size for the chunked relay protocol. 64 KiB raw becomes
147
250
  * ~85 KiB base64 in JSON; permessage-deflate compresses it well. Sender (Platform)
148
251
  * picks the actual size; receiver (orchestrator) just enforces totalSize and chunkCount.
149
252
  */
150
- const WEBHOOK_RELAY_CHUNK_SIZE = 64 * 1024;
253
+ const WEBHOOK_RELAY_CHUNK_SIZE = 65536;
151
254
  /**
152
255
  * Start frame of the chunked webhook relay protocol (Platform -> orchestrator).
153
256
  * Carries metadata + signature inputs only; body bytes follow in subsequent
@@ -239,6 +342,20 @@ const staleCheckrunCleanupSchema = z.object({
239
342
  repoIdentifier: z.string(),
240
343
  sha: z.string(),
241
344
  workflowName: z.string(),
345
+ /**
346
+ * The repository that DEFINES the workflow, when that is not the
347
+ * repository the run acted on — an organization-wide workflow dispatched
348
+ * against another repository. The orchestrator qualifies the check-run
349
+ * names it looks for with it, so a cleanup cannot time out the acted-on
350
+ * repository's own same-named check; see the orchestrator's
351
+ * `workflowLabel`.
352
+ *
353
+ * Additive and optional. The Platform sends it on exactly the condition
354
+ * that writes the mirror column — only when the two repositories differ —
355
+ * so an absent field and a value equal to `repoIdentifier` mean the same
356
+ * thing and both name the unqualified check.
357
+ */
358
+ workflowRepoIdentifier: z.string().max(256).optional(),
242
359
  jobNames: z.array(z.string())
243
360
  }))
244
361
  });
@@ -292,6 +409,46 @@ const orchMetricsSchema = z.object({
292
409
  })).max(2e3),
293
410
  timestamp: z.number()
294
411
  });
412
+ /** Largest worker-peer snapshot accepted on one frame. */
413
+ const CLUSTER_MEMBERSHIP_MAX_WORKERS = 512;
414
+ /**
415
+ * Worker-peer membership snapshot, sent by a coordinator to the Platform.
416
+ *
417
+ * A snapshot rather than a delta: a dropped or reordered frame self-heals on
418
+ * the next send instead of corrupting a running tally. The Platform stores the
419
+ * reported size on `platform_connections` and aggregates it into the org's
420
+ * combined orchestrator count.
421
+ *
422
+ * The array bound is the first line of defence, for the same reason
423
+ * `orchMetricsSchema` bounds its own arrays: a malformed or hostile push must
424
+ * fail Zod parse at the WS edge and never reach the aggregate.
425
+ */
426
+ const clusterMembershipSchema = z.object({
427
+ type: z.literal("cluster.membership"),
428
+ workers: z.array(z.object({ instanceId: z.string().min(1).max(128) })).max(512),
429
+ timestamp: z.number()
430
+ });
431
+ /**
432
+ * Per-coordinator orchestrator ceiling, pushed by the Platform.
433
+ *
434
+ * `maxWorkerPeers` is an ABSOLUTE ceiling on this coordinator's connected
435
+ * worker peers, not a remaining allowance. The Platform computes it by
436
+ * excluding this connection's own workers from the org total, so it does not
437
+ * move when a local worker joins or leaves — the coordinator can enforce
438
+ * against its live peer count with no round trip and no staleness window.
439
+ *
440
+ * `orgLimit` and `orgTotal` are informational: they let the coordinator's
441
+ * rejection reason say why, rather than closing opaquely. `evictExcess` is set
442
+ * once the org has been over its limit for longer than the grace window, and
443
+ * asks the coordinator to drain its newest workers down to the ceiling.
444
+ */
445
+ const planHeadroomSchema = z.object({
446
+ type: z.literal("plan.headroom"),
447
+ maxWorkerPeers: z.number().int().min(0),
448
+ orgLimit: z.number().int().min(0),
449
+ orgTotal: z.number().int().min(0),
450
+ evictExcess: z.boolean()
451
+ });
295
452
  /**
296
453
  * Acknowledgment that a webhook was received and processing started.
297
454
  *
@@ -421,6 +578,7 @@ const platformToOrchestratorMessageSchema = z.discriminatedUnion("type", [
421
578
  staleCheckrunCleanupSchema,
422
579
  oidcMintResponseSchema,
423
580
  platformCapabilitiesMessageSchema,
581
+ planHeadroomSchema,
424
582
  nackSchema,
425
583
  ...dashboardPlatformToOrchSchema.options
426
584
  ]);
@@ -450,6 +608,7 @@ const orchestratorToPlatformMessageSchema = z.discriminatedUnion("type", [
450
608
  jobContextMessageSchema,
451
609
  orchMetricsSchema,
452
610
  oidcMintRequestSchema,
611
+ clusterMembershipSchema,
453
612
  nackSchema
454
613
  ]);
455
614
  /**
@@ -479,6 +638,6 @@ const ORCH_TO_PLATFORM_RECOGNIZED_TYPES = new Set(collectDiscriminatorTypes(orch
479
638
  */
480
639
  const PLATFORM_TO_ORCH_RECOGNIZED_TYPES = new Set(collectDiscriminatorTypes(platformToOrchestratorMessageSchema));
481
640
  //#endregion
482
- export { DEFAULT_APPROVAL_EXPIRY_HOURS, ORCH_TO_PLATFORM_RECOGNIZED_TYPES, OrchLogPhase, PLATFORM_TO_ORCH_RECOGNIZED_TYPES, WEBHOOK_RELAY_CHUNK_SIZE, WEBHOOK_RELAY_MAX_BODY_BYTES, WebhookRelayResult, cacheStatsSchema, collectDiscriminatorTypes, executionEventSchema, logChunkSchema, orchCapabilitiesUpdateSchema, orchLogChunkSchema, orchMetricsSchema, orchestratorToPlatformMessageSchema, peerDiscoverSchema, peerUpdateSchema, platformCapabilitiesMessageSchema, platformToOrchestratorMessageSchema, staleCheckrunCleanupSchema, trustPolicySchema, trustPolicyUpdateSchema, webhookAckSchema, webhookRelayChunkSchema, webhookRelaySchema, webhookRelayStartSchema };
641
+ export { CLUSTER_MEMBERSHIP_MAX_WORKERS, CiTrustLevel, DEFAULT_APPROVAL_EXPIRY_HOURS, DEFAULT_APPROVAL_EXPIRY_SECONDS, ForkPolicy, MAX_APPROVAL_EXPIRY_HOURS, MAX_APPROVAL_EXPIRY_SECONDS, MIN_APPROVAL_EXPIRY_SECONDS, ORCH_TO_PLATFORM_RECOGNIZED_TYPES, OrchLogPhase, PLATFORM_TO_ORCH_RECOGNIZED_TYPES, SECONDS_PER_HOUR, WEBHOOK_RELAY_CHUNK_SIZE, WEBHOOK_RELAY_MAX_BODY_BYTES, WebhookRelayResult, approvalExpiryHoursOf, approvalExpirySecondsOf, cacheStatsSchema, clusterMembershipSchema, collectDiscriminatorTypes, executionEventSchema, logChunkSchema, orchCapabilitiesUpdateSchema, orchLogChunkSchema, orchMetricsSchema, orchestratorToPlatformMessageSchema, peerDiscoverSchema, peerUpdateSchema, planHeadroomSchema, platformCapabilitiesMessageSchema, platformToOrchestratorMessageSchema, staleCheckrunCleanupSchema, trustPolicySchema, trustPolicyUpdateSchema, webhookAckSchema, webhookRelayChunkSchema, webhookRelaySchema, webhookRelayStartSchema };
483
642
 
484
643
  //# sourceMappingURL=platform-orchestrator.js.map
@@ -1,12 +1,29 @@
1
1
  /**
2
2
  * Protocol version. Sent during WebSocket handshake.
3
- * Increment on breaking changes to message schemas.
3
+ *
4
+ * Increment when a message schema gains something an older peer cannot parse,
5
+ * and pair the bump with a named floor (below) so a sender can gate on the
6
+ * version a peer negotiated instead of guessing.
4
7
  */
5
- export declare const PROTOCOL_VERSION = 1;
8
+ export declare const PROTOCOL_VERSION = 2;
6
9
  /**
7
10
  * Minimum protocol version accepted.
8
11
  * Connections below this are rejected.
9
12
  * Capabilities handle per-feature negotiation above this baseline.
10
13
  */
11
14
  export declare const MIN_PROTOCOL_VERSION = 1;
15
+ /**
16
+ * First protocol version whose `trust_policy.update` reader accepts
17
+ * `forkPolicy: 'ignore'`.
18
+ *
19
+ * A peer below this version may validate the pushed policy against a
20
+ * `forkPolicy` enum with no `ignore` member. `trust_policy.update` is a member
21
+ * of a discriminated union, so that value fails the WHOLE frame rather than one
22
+ * field: the org's identity links, member CI trust levels and team memberships
23
+ * are dropped with it. The Platform therefore rewrites `ignore` to the
24
+ * deprecated `reject` before sending to such a peer — the value that peer's
25
+ * enum does carry, that denies dispatch the same way, and that this build's own
26
+ * fork switch already resolves through the same arm as `ignore`.
27
+ */
28
+ export declare const FORK_POLICY_IGNORE_MIN_PROTOCOL_VERSION = 2;
12
29
  //# sourceMappingURL=version.d.ts.map
@@ -2,16 +2,33 @@ import "../rolldown-runtime-ClRpJifh.js";
2
2
  //#region src/protocol/version.ts
3
3
  /**
4
4
  * Protocol version. Sent during WebSocket handshake.
5
- * Increment on breaking changes to message schemas.
5
+ *
6
+ * Increment when a message schema gains something an older peer cannot parse,
7
+ * and pair the bump with a named floor (below) so a sender can gate on the
8
+ * version a peer negotiated instead of guessing.
6
9
  */
7
- const PROTOCOL_VERSION = 1;
10
+ const PROTOCOL_VERSION = 2;
8
11
  /**
9
12
  * Minimum protocol version accepted.
10
13
  * Connections below this are rejected.
11
14
  * Capabilities handle per-feature negotiation above this baseline.
12
15
  */
13
16
  const MIN_PROTOCOL_VERSION = 1;
17
+ /**
18
+ * First protocol version whose `trust_policy.update` reader accepts
19
+ * `forkPolicy: 'ignore'`.
20
+ *
21
+ * A peer below this version may validate the pushed policy against a
22
+ * `forkPolicy` enum with no `ignore` member. `trust_policy.update` is a member
23
+ * of a discriminated union, so that value fails the WHOLE frame rather than one
24
+ * field: the org's identity links, member CI trust levels and team memberships
25
+ * are dropped with it. The Platform therefore rewrites `ignore` to the
26
+ * deprecated `reject` before sending to such a peer — the value that peer's
27
+ * enum does carry, that denies dispatch the same way, and that this build's own
28
+ * fork switch already resolves through the same arm as `ignore`.
29
+ */
30
+ const FORK_POLICY_IGNORE_MIN_PROTOCOL_VERSION = 2;
14
31
  //#endregion
15
- export { MIN_PROTOCOL_VERSION, PROTOCOL_VERSION };
32
+ export { FORK_POLICY_IGNORE_MIN_PROTOCOL_VERSION, MIN_PROTOCOL_VERSION, PROTOCOL_VERSION };
16
33
 
17
34
  //# sourceMappingURL=version.js.map
@@ -107,17 +107,18 @@ async function verifyKiciBundle(opts) {
107
107
  failures.push("dsse_signature_invalid");
108
108
  }
109
109
  const attestationOrigin = resolveAttestationOrigin(claims, statement);
110
- if (statement && claims && statementBytes) if (attestationOrigin === AttestationOrigin.enum.live) {
111
- const ok = crossCheckBuildContext(statement, claims);
112
- checks.buildContext = ok ? "pass" : "fail";
113
- if (!ok) failures.push("build_context_mismatch");
110
+ if (statement && claims && statementBytes) {
111
+ if (attestationOrigin === AttestationOrigin.enum.live) {
112
+ const ok = crossCheckBuildContext(statement, claims);
113
+ checks.buildContext = ok ? "pass" : "fail";
114
+ if (!ok) failures.push("build_context_mismatch");
115
+ } else {
116
+ const actual = await computeStatementHash(statementBytes);
117
+ const ok = typeof claims.statement_hash === "string" && claims.statement_hash === actual;
118
+ checks.buildContext = ok ? "pass" : "fail";
119
+ if (!ok) failures.push("statement_hash_mismatch");
120
+ }
114
121
  } else {
115
- const actual = await computeStatementHash(statementBytes);
116
- const ok = typeof claims.statement_hash === "string" && claims.statement_hash === actual;
117
- checks.buildContext = ok ? "pass" : "fail";
118
- if (!ok) failures.push("statement_hash_mismatch");
119
- }
120
- else {
121
122
  checks.buildContext = "fail";
122
123
  failures.push("build_context_uncheckable");
123
124
  }
@@ -4,9 +4,20 @@
4
4
  * Used by the CI security system to post approval/hold status checks
5
5
  * on PRs, enabling visibility into trust-tier gating decisions.
6
6
  */
7
+ import type { CheckRunConclusion } from './check-run-conclusion.js';
7
8
  import type { ProviderType } from './types.js';
8
- /** Status values for a check run. */
9
- export type CheckStatus = 'pending' | 'success' | 'failure' | 'neutral';
9
+ /**
10
+ * Status values for a check run: still running, or one of the terminal
11
+ * conclusions.
12
+ *
13
+ * The terminal half is `CheckRunConclusion` — the same vocabulary the workflow
14
+ * and per-job `kici/…` check runs conclude with — plus `neutral`, which those
15
+ * runs never use and the informational security checks do. One vocabulary means
16
+ * a hold that ends is reported the same way on the security check and on the
17
+ * `kici/…` checks of the same event: `cancelled` for a rejection, `timed_out`
18
+ * for an elapsed approval window.
19
+ */
20
+ export type CheckStatus = 'pending' | 'neutral' | CheckRunConclusion;
10
21
  /** A single workflow-file change detected between base and head lock files. */
11
22
  export interface WorkflowModificationInfo {
12
23
  changeType: string;
@@ -34,5 +45,29 @@ export interface CheckStatusPoster {
34
45
  * unblocking a branch protection rule that requires the security check.
35
46
  */
36
47
  postGlobalWorkflowsSkippedCheck(repoIdentifier: string, commitSha: string, summary: string, credentials: unknown): Promise<void>;
48
+ /**
49
+ * Post a failing check recording that the pre-run evaluation of the
50
+ * organization's global workflows could not be completed, so none of the
51
+ * workflows it was deciding on ran for this commit.
52
+ *
53
+ * Its own check name, for the same reason as the two notices above: the
54
+ * security-hold check is a single named run per commit, so posting this
55
+ * through `postCheckStatus` would UPDATE that run and replace a pending
56
+ * "Held for approval" state with a completed conclusion.
57
+ *
58
+ * Optional so a provider bundle that has no notion of commit checks — or a
59
+ * hand-built one — is silent rather than failing the delivery.
60
+ */
61
+ postGlobalEvalFailedCheck?(repoIdentifier: string, commitSha: string, summary: string, credentials: unknown): Promise<void>;
62
+ /**
63
+ * Post the success conclusion on the organization-workflow-evaluation check —
64
+ * the same check name {@link postGlobalEvalFailedCheck} writes — after a
65
+ * re-run of the failed round completes cleanly, so a bot gating on all-green
66
+ * is unblocked without a new commit.
67
+ *
68
+ * Optional for the same reason as the failure poster: a bundle with no notion
69
+ * of commit checks is silent rather than failing the re-run.
70
+ */
71
+ postGlobalEvalSucceededCheck?(repoIdentifier: string, commitSha: string, summary: string, credentials: unknown): Promise<void>;
37
72
  }
38
73
  //# sourceMappingURL=check-status-poster.d.ts.map
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * ContributorResolver interface for determining contributor permissions.
3
3
  *
4
- * Used by the CI security system to check a contributor's permission level
5
- * on a repository, enabling trust-tier decisions for PR execution gating.
4
+ * Deprecated in favour of ref-based trust: the whole interface, including the
5
+ * `ContributorPermission` / `ContributorInfo` shapes it carries, is retained
6
+ * for compatibility and has no caller in this repo.
6
7
  */
7
8
  import type { ProviderType } from './types.js';
8
9
  /** Contributor's permission level on a repository. */
@@ -14,7 +15,14 @@ export interface ContributorInfo {
14
15
  /** Whether the PR comes from a fork */
15
16
  isForkPR: boolean;
16
17
  }
17
- /** Resolves contributor information from a git hosting provider. */
18
+ /**
19
+ * Resolves contributor information from a git hosting provider.
20
+ *
21
+ * @deprecated Trust is derived from the git ref, not from the contributor's
22
+ * permission level, so nothing in the webhook pipeline calls a resolver. The
23
+ * type stays exported for wire compatibility with external implementations and
24
+ * is removed at v1.0.0.
25
+ */
18
26
  export interface ContributorResolver {
19
27
  readonly provider: ProviderType;
20
28
  resolveContributor(repoIdentifier: string, username: string, credentials: unknown): Promise<ContributorInfo>;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * FileContentsFetcher interface.
3
+ *
4
+ * Fetches the contents of an arbitrary repository file at a specific ref via a
5
+ * provider's repository API. Used by the orchestrator to evaluate content-match
6
+ * triggers without cloning the repo.
7
+ */
8
+ import type { ProviderType } from './types.js';
9
+ export interface FileContentsFetcher {
10
+ readonly provider: ProviderType;
11
+ /**
12
+ * Fetch the contents of a file in a repository at a specific ref.
13
+ *
14
+ * Result shape:
15
+ * - `{ present: false }` -- the file does not exist (a 404 from the provider,
16
+ * or the path resolves to a directory rather than a file).
17
+ * - `{ present: true, bytes }` -- the file exists and the provider returned
18
+ * its inline content, decoded to a string.
19
+ * - `{ present: true }` (no `bytes`) -- the file exists but the provider did
20
+ * not return inline content (e.g. GitHub's Contents API omits content for
21
+ * files larger than 1 MiB). A caller MUST NOT read a missing `bytes` as an
22
+ * absent file.
23
+ *
24
+ * Implementations do not cap the returned content -- any size limit is the
25
+ * caller's concern (the orchestrator's content matcher enforces a
26
+ * post-materialization cap). Implementations MUST NOT fall back to a raw
27
+ * blob download to defeat a provider's inline-content size limit.
28
+ *
29
+ * @param owner - Repository owner (e.g. "my-org")
30
+ * @param repo - Repository name (e.g. "my-app")
31
+ * @param path - Repo-relative file path (e.g. ".kici/workflows/ci.ts")
32
+ * @param ref - Git ref (branch name, tag, or SHA)
33
+ */
34
+ getFileContents(owner: string, repo: string, path: string, ref: string): Promise<{
35
+ present: boolean;
36
+ bytes?: string;
37
+ }>;
38
+ }
39
+ //# sourceMappingURL=file-contents-fetcher.d.ts.map
@@ -0,0 +1,2 @@
1
+ import "../rolldown-runtime-ClRpJifh.js";
2
+ export {};
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Git credential types shared by the orchestrator broker, the agent helper,
3
+ * and the SDK surface.
4
+ *
5
+ * Browser-safe by construction: these types sit on the `provider/` barrel that
6
+ * the dashboard transitively imports, so this module declares plain TypeScript
7
+ * and imports no Zod. The validating mirror lives in
8
+ * `protocol/messages/git-credential-relay.ts`.
9
+ */
10
+ import type { ProviderType } from './types.js';
11
+ /**
12
+ * A forge that can back a git credential. Derived from `ProviderType` rather
13
+ * than declared alongside it so the two cannot drift. `'local'` is excluded —
14
+ * it is an execution mode, not a forge.
15
+ */
16
+ export type ForgeName = Exclude<ProviderType, 'local'>;
17
+ /**
18
+ * One half of a credential field pair. Exactly one form is set: a qualified
19
+ * `<context>:<secret-name>` reference resolved from the secrets backend, or
20
+ * material supplied at runtime.
21
+ *
22
+ * The field NAME is the discriminator, so neither a reader nor the broker ever
23
+ * has to guess whether a value is a key or a credential. This follows the
24
+ * convention `packages/sdk/src/workflow.ts` already sets with
25
+ * `registries[].tokenSecret` and `isQualifiedSecretRef`.
26
+ */
27
+ export type Sourced<Name extends string> = {
28
+ [K in `${Name}Secret`]: string;
29
+ } | {
30
+ [K in `${Name}Value`]: string;
31
+ };
32
+ /** Where credential material comes from, for each supported credential shape. */
33
+ export type GitCredentialRef = ({
34
+ kind: 'app';
35
+ } & Sourced<'appId'> & Sourced<'installationId'> & Sourced<'privateKey'>) | ({
36
+ kind: 'token';
37
+ user?: string;
38
+ } & Sourced<'token'>) | ({
39
+ kind: 'ssh';
40
+ } & Sourced<'privateKey'>);
41
+ /**
42
+ * What a credential may actually do. Reported by the broker; never an echo of
43
+ * what the caller requested.
44
+ *
45
+ * `scoped: false` means the credential could not be narrowed at all — every
46
+ * static credential, where the key is read-write or it is not. `scoped: true`
47
+ * carries the permission map the forge actually granted.
48
+ */
49
+ export type GitCredentialGrant = {
50
+ scoped: false;
51
+ } | {
52
+ scoped: true;
53
+ permissions: Readonly<Record<string, string>>;
54
+ };
55
+ /** A broker request: which repo, on whose behalf, with what asked for. */
56
+ export interface GitCredentialRequest {
57
+ /** Repository identifier, e.g. `'kici-dev/kici-forge-app-token-tester'`. */
58
+ repository: string;
59
+ /** Omit for the source-scoped default credential. */
60
+ ref?: GitCredentialRef;
61
+ /**
62
+ * Requested permissions. Meaningful only for a minted shape; ignored for a
63
+ * static one, which reports `scoped: false`.
64
+ */
65
+ permissions?: Readonly<Record<string, string>>;
66
+ }
67
+ /** What the broker returns. `expiresAt` is null for a credential that does not expire. */
68
+ export interface GitCredentialResult {
69
+ kind: 'basic' | 'ssh';
70
+ user?: string;
71
+ secret: string;
72
+ grant: GitCredentialGrant;
73
+ expiresAt: string | null;
74
+ }
75
+ /** True when the ref requires minting (and therefore per-operation refresh). */
76
+ export declare function isMintedRef(ref: GitCredentialRef): boolean;
77
+ //# sourceMappingURL=git-credential.d.ts.map
@@ -0,0 +1,10 @@
1
+ import "../rolldown-runtime-ClRpJifh.js";
2
+ //#region src/provider/git-credential.ts
3
+ /** True when the ref requires minting (and therefore per-operation refresh). */
4
+ function isMintedRef(ref) {
5
+ return ref.kind === "app";
6
+ }
7
+ //#endregion
8
+ export { isMintedRef };
9
+
10
+ //# sourceMappingURL=git-credential.js.map
@@ -8,6 +8,7 @@
8
8
  * - WebhookNormalizer: Webhook ingestion and event normalization
9
9
  * - LockFileFetcher: Repository API access for lock files
10
10
  * - ChangedFilesFetcher: Repository API access for changed files
11
+ * - FileContentsFetcher: Repository API access for arbitrary file contents
11
12
  * - CloneTokenProvider: Auth token generation for agent clone
12
13
  * - RepoUrlBuilder: Provider-specific URL construction
13
14
  */
@@ -16,7 +17,10 @@ export type { WebhookNormalizer, AccessCacheInvalidation } from './webhook-norma
16
17
  export type { LockFileFetcher } from './lock-file-fetcher.js';
17
18
  export { LockFileParseError } from './lock-file-parse-error.js';
18
19
  export type { ChangedFilesFetcher, ChangedFilesResult } from './changed-files-fetcher.js';
20
+ export type { FileContentsFetcher } from './file-contents-fetcher.js';
19
21
  export type { CloneTokenProvider, ProviderGitAuth } from './clone-token-provider.js';
22
+ export type { ForgeName, Sourced, GitCredentialRef, GitCredentialGrant, GitCredentialRequest, GitCredentialResult, } from './git-credential.js';
23
+ export { isMintedRef } from './git-credential.js';
20
24
  export type { RepoUrlBuilder } from './repo-url-builder.js';
21
25
  export type { ContributorResolver, ContributorInfo, ContributorPermission, } from './contributor-resolver.js';
22
26
  export type { CheckStatusPoster, CheckStatus, WorkflowModificationInfo, } from './check-status-poster.js';
@@ -1,4 +1,5 @@
1
1
  import "../rolldown-runtime-ClRpJifh.js";
2
2
  import { LockFileParseError } from "./lock-file-parse-error.js";
3
+ import { isMintedRef } from "./git-credential.js";
3
4
  import { CheckRunConclusion } from "./check-run-conclusion.js";
4
- export { CheckRunConclusion, LockFileParseError };
5
+ export { CheckRunConclusion, LockFileParseError, isMintedRef };
@@ -8,8 +8,13 @@
8
8
  import type { SimulatedEvent } from '../trigger/types.js';
9
9
  import type { ProviderType } from './types.js';
10
10
  /**
11
- * Discriminated union describing which `ContributorCache` entries a webhook
12
- * event invalidates. Returned by `WebhookNormalizer.getAccessCacheInvalidations`.
11
+ * Discriminated union describing which cached provider-permission entries a
12
+ * webhook event invalidates. Returned by
13
+ * `WebhookNormalizer.getAccessCacheInvalidations`.
14
+ *
15
+ * @deprecated Trust is derived from the git ref, so the orchestrator holds no
16
+ * contributor-permission cache and nothing consumes these entries. Removed at
17
+ * v1.0.0.
13
18
  *
14
19
  * Three scopes:
15
20
  * - `repo-user`: a single `{repo, user}` permission changed (e.g. GitHub
@@ -142,17 +147,12 @@ export interface WebhookNormalizer {
142
147
  */
143
148
  extractDefaultBranch?(payload: unknown): string | null;
144
149
  /**
145
- * Map a webhook event to the `ContributorCache` entries it should drop.
146
- *
147
- * Optional. When implemented, the orchestrator calls this before
148
- * `normalizeEvent` and invalidates every returned entry so the next
149
- * permission check hits the provider API instead of relying on stale
150
- * cached data. Events that do not imply a permission shift (most events,
151
- * including `push` / `pull_request` / etc.) should return `[]`.
150
+ * Map a webhook event to the cached provider-permission entries it implies a
151
+ * shift in. Events that do not imply a permission shift (most events,
152
+ * including `push` / `pull_request` / etc.) return `[]`.
152
153
  *
153
- * The caller is responsible for pairing the returned entries with the
154
- * `provider` field of the matched bundle — this method receives no
155
- * provider argument.
154
+ * @deprecated Trust is derived from the git ref, so the orchestrator holds no
155
+ * contributor-permission cache and never calls this. Removed at v1.0.0.
156
156
  *
157
157
  * @param eventType Provider-specific event type (from extractEventType).
158
158
  * @param action Event action/sub-type, if applicable.