@kici-dev/engine 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/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/index.d.ts +1 -1
  6. package/dist/context/index.js +3 -3
  7. package/dist/context/types.d.ts +10 -1
  8. package/dist/context/types.js +10 -1
  9. package/dist/index.d.ts +4 -2
  10. package/dist/index.js +14 -11
  11. package/dist/labels.d.ts +66 -12
  12. package/dist/labels.js +74 -17
  13. package/dist/mcp/held-run-resolve.d.ts +40 -0
  14. package/dist/mcp/held-run-resolve.js +68 -15
  15. package/dist/mcp/tool-schemas.d.ts +4 -0
  16. package/dist/mcp/tool-schemas.js +13 -1
  17. package/dist/metrics/catalog-policy.d.ts +6 -3
  18. package/dist/metrics/catalog-policy.js +31 -10
  19. package/dist/metrics/metric-catalog.generated.d.ts +110 -0
  20. package/dist/metrics/metric-catalog.generated.js +132 -0
  21. package/dist/protocol/dashboard-global-workflows.js +2 -2
  22. package/dist/protocol/event-log-payload.js +1 -1
  23. package/dist/protocol/messages/access-log.d.ts +5 -0
  24. package/dist/protocol/messages/access-log.js +1 -0
  25. package/dist/protocol/messages/actor.d.ts +13 -2
  26. package/dist/protocol/messages/actor.js +16 -5
  27. package/dist/protocol/messages/common.js +1 -1
  28. package/dist/protocol/messages/dashboard-global-workflows.d.ts +21 -0
  29. package/dist/protocol/messages/dashboard-global-workflows.js +28 -1
  30. package/dist/protocol/messages/dashboard.d.ts +46 -5
  31. package/dist/protocol/messages/dashboard.js +67 -7
  32. package/dist/protocol/messages/execution-status.d.ts +41 -0
  33. package/dist/protocol/messages/execution-status.js +53 -2
  34. package/dist/protocol/messages/git-credential-relay.d.ts +78 -0
  35. package/dist/protocol/messages/git-credential-relay.js +86 -0
  36. package/dist/protocol/messages/orchestrator-agent.d.ts +89 -0
  37. package/dist/protocol/messages/orchestrator-agent.js +98 -3
  38. package/dist/protocol/messages/peer.d.ts +7 -0
  39. package/dist/protocol/messages/peer.js +18 -1
  40. package/dist/protocol/messages/platform-orchestrator.d.ts +150 -0
  41. package/dist/protocol/messages/platform-orchestrator.js +164 -19
  42. package/dist/protocol/version.d.ts +19 -2
  43. package/dist/protocol/version.js +20 -3
  44. package/dist/provenance/verify.js +11 -10
  45. package/dist/provider/check-status-poster.d.ts +24 -3
  46. package/dist/provider/contributor-resolver.d.ts +11 -3
  47. package/dist/provider/git-credential.d.ts +77 -0
  48. package/dist/provider/git-credential.js +10 -0
  49. package/dist/provider/index.d.ts +2 -0
  50. package/dist/provider/index.js +2 -1
  51. package/dist/provider/webhook-normalizer.d.ts +12 -12
  52. package/dist/repo/pattern-negation.d.ts +73 -0
  53. package/dist/repo/pattern-negation.js +86 -0
  54. package/dist/scaler/registry-auth.d.ts +18 -0
  55. package/dist/scaler/registry-auth.js +28 -0
  56. package/dist/scaler/scaler-backend-type.d.ts +35 -0
  57. package/dist/scaler/scaler-backend-type.js +39 -2
  58. package/dist/scaler/scaler-events.d.ts +79 -0
  59. package/dist/scaler/scaler-events.js +87 -0
  60. package/dist/trigger/content-requirements.js +1 -1
  61. package/dist/trigger/decision-trace.d.ts +79 -0
  62. package/dist/trigger/decision-trace.js +116 -8
  63. package/dist/trigger/matcher.js +4 -2
  64. package/dist/trigger/types.d.ts +87 -7
  65. package/dist/trigger/types.js +6 -1
  66. package/dist/ws/rate-limiter.js +3 -3
  67. package/package.json +11 -3
  68. package/sbom.spdx.json +15 -15
@@ -0,0 +1,86 @@
1
+ import "../rolldown-runtime-ClRpJifh.js";
2
+ //#region src/repo/pattern-negation.ts
3
+ /**
4
+ * The one classifier for "picomatch would read this repo pattern as a
5
+ * negation".
6
+ *
7
+ * Repo patterns are written on lists whose direction is already fixed by the
8
+ * list itself — a role's allowed repositories, a global-workflow allow list, a
9
+ * global-workflow deny list. A pattern picomatch reads as a negation inverts
10
+ * that direction inside a single entry, so an entry that reads as a restriction
11
+ * matches as its complement. On an allow list that grants almost everything; on
12
+ * a deny list it admits the one repository the entry named.
13
+ *
14
+ * This lives in the engine, and not beside either consumer, because two
15
+ * hand-maintained ban lists for one pattern language cannot be kept in step.
16
+ * The two ways they drift apart are both live hazards: a list that misses the
17
+ * regular-expression assertions accepts a real inversion, and a list that
18
+ * refuses `[!…]` turns away a genuine restriction. Every surface that stores a
19
+ * repo pattern reads its verdict from here, so neither can happen on one
20
+ * surface alone.
21
+ */
22
+ /**
23
+ * The regular-expression negations picomatch passes through into the compiled
24
+ * matcher: the negative lookahead `(?!…)` and the negative lookbehind `(?<!…)`.
25
+ * The optional `<` is what makes one pattern cover both; the positive `(?=…)`,
26
+ * `(?<=…)`, `(?:…)` and a plain capture group are deliberately not matched.
27
+ */
28
+ const REGEX_NEGATIVE_ASSERTION = /\(\?<?!/;
29
+ /**
30
+ * Why picomatch would read `pattern` as a negation, or null when it would not.
31
+ *
32
+ * Four arms, because picomatch reads four negation forms, each of which turns a
33
+ * pattern that reads as a restriction into a grant.
34
+ *
35
+ * A leading `!` negates the whole pattern. The extglob complement `!(…)`
36
+ * negates wherever it appears, so `org/!(secret)` covers every repository under
37
+ * `org/` except that one — the same defect in a prefix-scoped shape. The
38
+ * negated character class `[^…]` does it one character at a time: `org/[^s]*`
39
+ * covers every repository under `org/` whose name does not begin with `s`. The
40
+ * negative assertions are the widest of the four, because they can spell a
41
+ * whole repository identifier rather than one character: picomatch compiles a
42
+ * pattern to a regular expression and passes a group it does not recognise
43
+ * through verbatim, so `(?!org/secret)**` reaches the matcher as a real
44
+ * lookahead and matches every repository in every organization except the one
45
+ * it names.
46
+ *
47
+ * The extglob arm matches the two-character sequence `!(` and nothing wider:
48
+ * `*(`, `+(`, `@(` and `?(` are the non-complementing extglob heads and do not
49
+ * invert, so rejecting a bare `(` would refuse four harmless forms for no gain.
50
+ * A `(` cannot appear in a repository identifier, so no legitimate pattern
51
+ * contains `!(`. The assertion arm is narrow for the same reason: `(?=…)`,
52
+ * `(?<=…)`, `(?:…)` and a plain capture group match strictly what they name, so
53
+ * only the two negative forms are refused.
54
+ *
55
+ * The character-class arm matches `[^` and nothing wider: a `[` cannot appear
56
+ * in a repository identifier either, and a bracket that is not a negation —
57
+ * `org/[abc]*` — is a legitimate restriction. It matches `[^` and NOT `[!`:
58
+ * picomatch does not read `[!…]` as the POSIX negation. It reads it as a
59
+ * literal class containing `!` and the listed characters, so `org/[!s]*`
60
+ * matches exactly the repositories whose name begins with `!` or `s` — the
61
+ * exact inverse of `org/[^s]*`, and a genuine restriction. Rejecting it would
62
+ * refuse a pattern that grants strictly less than it names while leaving the
63
+ * real inversion open.
64
+ *
65
+ * A bare `!` is different — it *can* appear in a repository name — so the first
66
+ * arm stays anchored: `org/we!rd` is a legitimate literal, and an
67
+ * `includes('!')` check would silently reject a valid repository name.
68
+ */
69
+ function negatedPatternReason(pattern) {
70
+ if (pattern.startsWith("!")) return "negation ('!' prefix)";
71
+ if (pattern.includes("!(")) return "extglob negation ('!(…)')";
72
+ if (pattern.includes("[^")) return "character-class negation ('[^…]')";
73
+ if (REGEX_NEGATIVE_ASSERTION.test(pattern)) return "negative assertion ('(?!…)' / '(?<!…)')";
74
+ return null;
75
+ }
76
+ /**
77
+ * True when `pattern` is a picomatch negation — whole-pattern `!…`, extglob
78
+ * `!(…)`, negated character class `[^…]`, or a negative lookahead / lookbehind.
79
+ */
80
+ function isNegatedPattern(pattern) {
81
+ return negatedPatternReason(pattern) !== null;
82
+ }
83
+ //#endregion
84
+ export { REGEX_NEGATIVE_ASSERTION, isNegatedPattern, negatedPatternReason };
85
+
86
+ //# sourceMappingURL=pattern-negation.js.map
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Registry-host extraction for container image references.
3
+ *
4
+ * Lives in the engine because three sites need the same answer: the agent's
5
+ * container sandbox, the container scaler backend, and the bare-metal backend
6
+ * in container mode. A private pull authenticates against the registry HOST,
7
+ * which is not a field of the image ref — it has to be derived from it.
8
+ */
9
+ /**
10
+ * Registry host (a container runtime's `authconfig.serveraddress`) for an image ref.
11
+ *
12
+ * Docker's own rule: the first path segment is the registry only when it looks
13
+ * like a host — it contains a dot or a colon, or it is exactly `localhost`.
14
+ * Otherwise the ref is a Docker Hub short name (`nginx`, `acme/ci`) and the
15
+ * registry is `docker.io`.
16
+ */
17
+ export declare function registryHostFromImageRef(image: string): string;
18
+ //# sourceMappingURL=registry-auth.d.ts.map
@@ -0,0 +1,28 @@
1
+ import "../rolldown-runtime-ClRpJifh.js";
2
+ //#region src/scaler/registry-auth.ts
3
+ /**
4
+ * Registry-host extraction for container image references.
5
+ *
6
+ * Lives in the engine because three sites need the same answer: the agent's
7
+ * container sandbox, the container scaler backend, and the bare-metal backend
8
+ * in container mode. A private pull authenticates against the registry HOST,
9
+ * which is not a field of the image ref — it has to be derived from it.
10
+ */
11
+ /**
12
+ * Registry host (a container runtime's `authconfig.serveraddress`) for an image ref.
13
+ *
14
+ * Docker's own rule: the first path segment is the registry only when it looks
15
+ * like a host — it contains a dot or a colon, or it is exactly `localhost`.
16
+ * Otherwise the ref is a Docker Hub short name (`nginx`, `acme/ci`) and the
17
+ * registry is `docker.io`.
18
+ */
19
+ function registryHostFromImageRef(image) {
20
+ const firstSlash = image.indexOf("/");
21
+ if (firstSlash === -1) return "docker.io";
22
+ const candidate = image.slice(0, firstSlash);
23
+ return candidate.includes(".") || candidate.includes(":") || candidate === "localhost" ? candidate : "docker.io";
24
+ }
25
+ //#endregion
26
+ export { registryHostFromImageRef };
27
+
28
+ //# sourceMappingURL=registry-auth.js.map
@@ -9,8 +9,43 @@ import { z } from 'zod';
9
9
  export declare const ScalerBackendType: z.ZodEnum<{
10
10
  "bare-metal": "bare-metal";
11
11
  container: "container";
12
+ event: "event";
12
13
  firecracker: "firecracker";
13
14
  kubernetes: "kubernetes";
14
15
  }>;
15
16
  export type ScalerBackendType = z.infer<typeof ScalerBackendType>;
17
+ /**
18
+ * Reserved event-name prefix for KiCI-internal system events (today: the event
19
+ * scaler's scale-up / scale-down events). Custom events emitted from user
20
+ * workflow steps (`ctx.emit(...)`) MUST NOT use this prefix — both the SDK
21
+ * (client-side) and the orchestrator (authoritative) reject a name that starts
22
+ * with it, so a user step cannot forge a system event. The rate limiter also
23
+ * exempts it. Defined in `@kici-dev/engine` so the SDK and the orchestrator
24
+ * share one source of truth without the SDK importing the orchestrator.
25
+ */
26
+ export declare const KICI_EVENT_NAME_PREFIX = "kici.";
27
+ /**
28
+ * Reserved event-name prefix for the events the ORCHESTRATOR mints for itself
29
+ * (`__schedule_fire`, `__workflow_complete`, `__job_complete`,
30
+ * `__workflows_failed_batch`).
31
+ *
32
+ * Reserved for the same reason as {@link KICI_EVENT_NAME_PREFIX}, and more
33
+ * sharply: every name under this prefix is exempt from the event-storm rate
34
+ * limiter, and `__schedule_fire` is additionally classified as a TRUSTED ref —
35
+ * no run causes it, so nothing external shaped it. The other three ARE caused
36
+ * by runs and inherit the tier of the run (or, for the failure batch, the most
37
+ * restrictive tier across the runs) behind them, so they forge no privilege on
38
+ * their own. A user step that could emit any of them would forge the
39
+ * rate-limiter exemption, and `__schedule_fire` the trusted classification on
40
+ * top of it — so the same two-sided reservation applies to the whole prefix,
41
+ * SDK first and orchestrator authoritatively.
42
+ */
43
+ export declare const INTERNAL_EVENT_NAME_PREFIX = "__";
44
+ /**
45
+ * The reserved prefix `eventName` uses, or `undefined` when a user step may
46
+ * emit it. One definition of "reserved", so the SDK-side check and the
47
+ * orchestrator's authoritative backstop can never disagree about which names a
48
+ * workflow may emit.
49
+ */
50
+ export declare function reservedEventNamePrefix(eventName: string): string | undefined;
16
51
  //# sourceMappingURL=scaler-backend-type.d.ts.map
@@ -12,9 +12,46 @@ const ScalerBackendType = z.enum([
12
12
  "container",
13
13
  "bare-metal",
14
14
  "firecracker",
15
- "kubernetes"
15
+ "kubernetes",
16
+ "event"
16
17
  ]);
18
+ /**
19
+ * Reserved event-name prefix for KiCI-internal system events (today: the event
20
+ * scaler's scale-up / scale-down events). Custom events emitted from user
21
+ * workflow steps (`ctx.emit(...)`) MUST NOT use this prefix — both the SDK
22
+ * (client-side) and the orchestrator (authoritative) reject a name that starts
23
+ * with it, so a user step cannot forge a system event. The rate limiter also
24
+ * exempts it. Defined in `@kici-dev/engine` so the SDK and the orchestrator
25
+ * share one source of truth without the SDK importing the orchestrator.
26
+ */
27
+ const KICI_EVENT_NAME_PREFIX = "kici.";
28
+ /**
29
+ * Reserved event-name prefix for the events the ORCHESTRATOR mints for itself
30
+ * (`__schedule_fire`, `__workflow_complete`, `__job_complete`,
31
+ * `__workflows_failed_batch`).
32
+ *
33
+ * Reserved for the same reason as {@link KICI_EVENT_NAME_PREFIX}, and more
34
+ * sharply: every name under this prefix is exempt from the event-storm rate
35
+ * limiter, and `__schedule_fire` is additionally classified as a TRUSTED ref —
36
+ * no run causes it, so nothing external shaped it. The other three ARE caused
37
+ * by runs and inherit the tier of the run (or, for the failure batch, the most
38
+ * restrictive tier across the runs) behind them, so they forge no privilege on
39
+ * their own. A user step that could emit any of them would forge the
40
+ * rate-limiter exemption, and `__schedule_fire` the trusted classification on
41
+ * top of it — so the same two-sided reservation applies to the whole prefix,
42
+ * SDK first and orchestrator authoritatively.
43
+ */
44
+ const INTERNAL_EVENT_NAME_PREFIX = "__";
45
+ /**
46
+ * The reserved prefix `eventName` uses, or `undefined` when a user step may
47
+ * emit it. One definition of "reserved", so the SDK-side check and the
48
+ * orchestrator's authoritative backstop can never disagree about which names a
49
+ * workflow may emit.
50
+ */
51
+ function reservedEventNamePrefix(eventName) {
52
+ for (const prefix of [KICI_EVENT_NAME_PREFIX, "__"]) if (eventName.startsWith(prefix)) return prefix;
53
+ }
17
54
  //#endregion
18
- export { ScalerBackendType };
55
+ export { INTERNAL_EVENT_NAME_PREFIX, KICI_EVENT_NAME_PREFIX, ScalerBackendType, reservedEventNamePrefix };
19
56
 
20
57
  //# sourceMappingURL=scaler-backend-type.js.map
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Shared schemas and constants for the event scaler backend.
3
+ *
4
+ * The event scaler performs no local compute: its `spawn()` / `destroy()` emit
5
+ * reserved `kici.`-prefixed custom events (`kici.scaler.scale-up` /
6
+ * `kici.scaler.scale-down`) that a customer-authored provisioning / teardown
7
+ * workflow consumes via the `kiciEvent()` trigger. These schemas define the
8
+ * event payloads and the single source of truth for the reserved event names.
9
+ *
10
+ * They live here, not in the orchestrator, because both sides of the contract
11
+ * need them: the orchestrator emits, and a workflow file — which may import
12
+ * `@kici-dev/sdk` and nothing else — consumes. Engine is the only package both
13
+ * the SDK and the orchestrator depend on, so it is the shared floor. The SDK
14
+ * re-exports all four symbols; the orchestrator's `scaler/scaler-events.ts`
15
+ * re-exports them for its own call sites.
16
+ */
17
+ import { z } from 'zod';
18
+ /**
19
+ * Reserved event names the event scaler emits. Both start with
20
+ * `KICI_EVENT_NAME_PREFIX` (`./scaler-backend-type.ts`), so the rate limiter
21
+ * exempts them and user steps cannot forge them — `scaler-events.test.ts`
22
+ * asserts that relationship holds for both names.
23
+ */
24
+ export declare const SCALER_EVENT_NAMES: {
25
+ readonly scaleUp: 'kici.scaler.scale-up';
26
+ readonly scaleDown: 'kici.scaler.scale-down';
27
+ };
28
+ /**
29
+ * Why the scaler asked for an agent to be torn down. Carried on the
30
+ * `kici.scaler.scale-down` event so a teardown workflow (and the timeline) can
31
+ * distinguish an idle reap from a job-complete teardown or a spawn timeout.
32
+ */
33
+ export declare const ScaleDownReason: z.ZodEnum<{
34
+ drain: "drain";
35
+ "heartbeat-timeout": "heartbeat-timeout";
36
+ idle: "idle";
37
+ "job-complete": "job-complete";
38
+ shutdown: "shutdown";
39
+ "spawn-timeout": "spawn-timeout";
40
+ }>;
41
+ export type ScaleDownReason = z.infer<typeof ScaleDownReason>;
42
+ /**
43
+ * Payload of a `kici.scaler.scale-up` event. Everything a provisioning workflow
44
+ * needs to boot an instance whose agent registers back with `agentId` and claim
45
+ * its ephemeral credentials with `claimCode`. The ephemeral token itself is
46
+ * NEVER in this payload — it is delivered only via the
47
+ * `scaler.claim-credentials` RPC response.
48
+ */
49
+ export declare const ScalerScaleUpPayload: z.ZodObject<{
50
+ scalerName: z.ZodString;
51
+ agentId: z.ZodString;
52
+ labels: z.ZodArray<z.ZodString>;
53
+ mandatoryLabels: z.ZodDefault<z.ZodArray<z.ZodString>>;
54
+ resources: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
55
+ orchestratorUrl: z.ZodString;
56
+ claimCode: z.ZodString;
57
+ jobId: z.ZodOptional<z.ZodString>;
58
+ requestId: z.ZodString;
59
+ }, z.core.$strip>;
60
+ export type ScalerScaleUpPayload = z.infer<typeof ScalerScaleUpPayload>;
61
+ /**
62
+ * Payload of a `kici.scaler.scale-down` event. A teardown workflow deletes the
63
+ * instance registered under `agentId`.
64
+ */
65
+ export declare const ScalerScaleDownPayload: z.ZodObject<{
66
+ scalerName: z.ZodString;
67
+ agentId: z.ZodString;
68
+ reason: z.ZodEnum<{
69
+ drain: "drain";
70
+ "heartbeat-timeout": "heartbeat-timeout";
71
+ idle: "idle";
72
+ "job-complete": "job-complete";
73
+ shutdown: "shutdown";
74
+ "spawn-timeout": "spawn-timeout";
75
+ }>;
76
+ requestId: z.ZodString;
77
+ }, z.core.$strip>;
78
+ export type ScalerScaleDownPayload = z.infer<typeof ScalerScaleDownPayload>;
79
+ //# sourceMappingURL=scaler-events.d.ts.map
@@ -0,0 +1,87 @@
1
+ import "../rolldown-runtime-ClRpJifh.js";
2
+ import { z } from "zod";
3
+ //#region src/scaler/scaler-events.ts
4
+ /**
5
+ * Shared schemas and constants for the event scaler backend.
6
+ *
7
+ * The event scaler performs no local compute: its `spawn()` / `destroy()` emit
8
+ * reserved `kici.`-prefixed custom events (`kici.scaler.scale-up` /
9
+ * `kici.scaler.scale-down`) that a customer-authored provisioning / teardown
10
+ * workflow consumes via the `kiciEvent()` trigger. These schemas define the
11
+ * event payloads and the single source of truth for the reserved event names.
12
+ *
13
+ * They live here, not in the orchestrator, because both sides of the contract
14
+ * need them: the orchestrator emits, and a workflow file — which may import
15
+ * `@kici-dev/sdk` and nothing else — consumes. Engine is the only package both
16
+ * the SDK and the orchestrator depend on, so it is the shared floor. The SDK
17
+ * re-exports all four symbols; the orchestrator's `scaler/scaler-events.ts`
18
+ * re-exports them for its own call sites.
19
+ */
20
+ /**
21
+ * Reserved event names the event scaler emits. Both start with
22
+ * `KICI_EVENT_NAME_PREFIX` (`./scaler-backend-type.ts`), so the rate limiter
23
+ * exempts them and user steps cannot forge them — `scaler-events.test.ts`
24
+ * asserts that relationship holds for both names.
25
+ */
26
+ const SCALER_EVENT_NAMES = {
27
+ scaleUp: "kici.scaler.scale-up",
28
+ scaleDown: "kici.scaler.scale-down"
29
+ };
30
+ /**
31
+ * Why the scaler asked for an agent to be torn down. Carried on the
32
+ * `kici.scaler.scale-down` event so a teardown workflow (and the timeline) can
33
+ * distinguish an idle reap from a job-complete teardown or a spawn timeout.
34
+ */
35
+ const ScaleDownReason = z.enum([
36
+ "idle",
37
+ "job-complete",
38
+ "heartbeat-timeout",
39
+ "spawn-timeout",
40
+ "drain",
41
+ "shutdown"
42
+ ]);
43
+ /**
44
+ * Payload of a `kici.scaler.scale-up` event. Everything a provisioning workflow
45
+ * needs to boot an instance whose agent registers back with `agentId` and claim
46
+ * its ephemeral credentials with `claimCode`. The ephemeral token itself is
47
+ * NEVER in this payload — it is delivered only via the
48
+ * `scaler.claim-credentials` RPC response.
49
+ */
50
+ const ScalerScaleUpPayload = z.object({
51
+ /** Name of the scaler entry that emitted the event. */
52
+ scalerName: z.string(),
53
+ /** Agent id the provisioned instance must register with (correlates the spawn). */
54
+ agentId: z.string(),
55
+ /** Exact label set the pending job needs. */
56
+ labels: z.array(z.string()),
57
+ /** Mandatory (taint) labels the pool gates on, if any. */
58
+ mandatoryLabels: z.array(z.string()).default([]),
59
+ /** Resolved resource hints for the provision (e.g. cpus / memBytes). */
60
+ resources: z.record(z.string(), z.unknown()).default({}),
61
+ /** Orchestrator WS URL the provisioned agent connects back to. */
62
+ orchestratorUrl: z.string(),
63
+ /** Single-use code the workflow exchanges for ephemeral agent credentials. */
64
+ claimCode: z.string(),
65
+ /** Execution job id the spawn is bound to (absent for unbound / warm spawns). */
66
+ jobId: z.string().optional(),
67
+ /** Correlation id for this scale-up request. */
68
+ requestId: z.string()
69
+ });
70
+ /**
71
+ * Payload of a `kici.scaler.scale-down` event. A teardown workflow deletes the
72
+ * instance registered under `agentId`.
73
+ */
74
+ const ScalerScaleDownPayload = z.object({
75
+ /** Name of the scaler entry that emitted the event. */
76
+ scalerName: z.string(),
77
+ /** Agent id whose instance should be torn down. */
78
+ agentId: z.string(),
79
+ /** Why the teardown was requested. */
80
+ reason: ScaleDownReason,
81
+ /** Correlation id for this scale-down request. */
82
+ requestId: z.string()
83
+ });
84
+ //#endregion
85
+ export { SCALER_EVENT_NAMES, ScaleDownReason, ScalerScaleDownPayload, ScalerScaleUpPayload };
86
+
87
+ //# sourceMappingURL=scaler-events.js.map
@@ -19,7 +19,7 @@ import { parse } from "yaml";
19
19
  * of the browser-facing engine barrel (`src/index.ts`).
20
20
  */
21
21
  /** Hard byte cap enforced before any parse: an oversize file is indeterminate. */
22
- const MAX_CONTENT_BYTES = 1024 * 1024;
22
+ const MAX_CONTENT_BYTES = 1048576;
23
23
  /**
24
24
  * Anchor/alias expansion cap for YAML parsing. The `yaml` library's default of
25
25
  * 100 does not reject a small billion-laughs bomb; 50 rejects it while staying
@@ -31,8 +31,28 @@ export interface WorkflowDecision {
31
31
  /** Summary reason */
32
32
  summary: string;
33
33
  }
34
+ /**
35
+ * Max characters of any free-text field a trace entry carries, so an
36
+ * essay-length input stays bounded.
37
+ *
38
+ * The entry count is capped separately, downstream, but a count bound alone
39
+ * bounds nothing: a single `paths` entry names every changed file in the push
40
+ * and a single `bodyMatch` entry quotes a comment body an outsider authored, so
41
+ * fifty entries can still be megabytes. The forwarded trace rides one WebSocket
42
+ * frame to the Platform, and a frame past the server's payload ceiling closes
43
+ * the connection — stalling every delivery for that organization until it
44
+ * reconnects. Bounding at the point each field is minted is what makes the
45
+ * downstream size guards a backstop rather than the only limit.
46
+ */
47
+ export declare const TRACE_TEXT_MAX = 200;
48
+ /** Clamp one free-text trace field, marking a clamped value with an ellipsis. */
49
+ export declare function truncateTraceText(text: string): string;
34
50
  /**
35
51
  * Create a new trace entry.
52
+ *
53
+ * Every free-text field is clamped here rather than at each call site: the
54
+ * fields are fed from event content of unbounded size, and one unclamped call
55
+ * site is enough to reintroduce an unbounded frame.
36
56
  */
37
57
  export declare function createTraceEntry(check: string, pattern: string, value: string, passed: boolean, reason?: string): TraceEntry;
38
58
  /**
@@ -54,6 +74,8 @@ export declare const TraceCheck: {
54
74
  readonly GlobalFilter: 'filter';
55
75
  /** Tier-0 declarative `commitMessage` filter, read from the normalized event. */
56
76
  readonly CommitMessage: 'commitMessage';
77
+ /** Materialization of a matched workflow's jobs, on the way to dispatch. */
78
+ readonly Dispatch: 'dispatch';
57
79
  };
58
80
  export type TraceCheck = (typeof TraceCheck)[keyof typeof TraceCheck];
59
81
  /**
@@ -96,6 +118,16 @@ export declare function createGlobalFilterTraceEntry(args: {
96
118
  indeterminate?: boolean;
97
119
  reason?: string;
98
120
  }): TraceEntry;
121
+ /**
122
+ * Record that a matched workflow could not be materialized into jobs.
123
+ *
124
+ * A workflow whose triggers matched and whose build then threw is absent from
125
+ * every other record: no run row is created and no job is queued. Omitting it
126
+ * from the trace as well leaves its author unable to tell it apart from a
127
+ * workflow that was never registered — the exact indistinguishability the trace
128
+ * exists to remove.
129
+ */
130
+ export declare function createDispatchFailureTraceEntry(reason: string): TraceEntry;
99
131
  /**
100
132
  * Return a copy of `decision` with `entries` appended to its checks.
101
133
  *
@@ -123,6 +155,53 @@ export declare function createCommitMessageTraceEntry(args: {
123
155
  indeterminate?: boolean;
124
156
  reason?: string;
125
157
  }): TraceEntry;
158
+ /**
159
+ * What a withheld trace field is replaced with when the reader does not hold
160
+ * `event_log:read_payload`.
161
+ *
162
+ * Lives here rather than beside the Platform's redactor because three packages
163
+ * read it: the Platform writes it, the dashboard renders it, and the E2E suite
164
+ * asserts the permission boundary against it. A literal repeated at each site
165
+ * would drift into a marker one of them no longer recognizes.
166
+ */
167
+ export declare const REDACTED_TRACE_FIELD = "[redacted \u2014 requires event_log:read_payload]";
168
+ /**
169
+ * Workflow name the truncation marker carries.
170
+ *
171
+ * A reserved sentinel rather than a real workflow: it is written by one package
172
+ * and read back by two others, so a literal repeated at each site would drift
173
+ * into a marker nobody recognizes.
174
+ */
175
+ export declare const TRACE_TRUNCATION_WORKFLOW_NAME = "(trace truncated)";
176
+ /**
177
+ * Build the marker that stands in for the decisions a size budget dropped.
178
+ *
179
+ * The trace is truncated rather than discarded: a reader who is told nothing
180
+ * cannot tell "matching never ran" from "the trace was too large to keep", and
181
+ * those two have opposite answers to "why did my workflow not fire".
182
+ */
183
+ export declare function createTraceTruncationMarker(omitted: number): Record<string, unknown>;
184
+ /**
185
+ * UTF-8 byte length of `text`.
186
+ *
187
+ * Byte length, never `String.length`: the latter counts UTF-16 code units, so a
188
+ * CJK-heavy comment body measures at roughly a third of the bytes it actually
189
+ * costs on the wire and in the stored row.
190
+ */
191
+ export declare function utf8ByteLength(text: string): number;
192
+ /**
193
+ * Drop trailing decisions until the serialized array fits `maxBytes`, appending
194
+ * a marker naming how many were dropped.
195
+ *
196
+ * Shared by the orchestrator, which bounds what it puts on the wire, and the
197
+ * Platform, which bounds what it stores. Two independent budgets over one
198
+ * shape: keeping one implementation is what stops them from disagreeing about
199
+ * what a truncated trace looks like.
200
+ */
201
+ export declare function truncateDecisionsToByteBudget<T>(decisions: readonly T[], maxBytes: number): {
202
+ decisions: Array<T | Record<string, unknown>>;
203
+ omitted: number;
204
+ };
126
205
  /**
127
206
  * Create a workflow decision record.
128
207
  */