@kici-dev/engine 0.5.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.
- package/dist/audit/access-log-policy.js +1 -0
- package/dist/audit/retention-policy.js +2 -0
- package/dist/context/held-run-job-id.d.ts +26 -10
- package/dist/context/held-run-job-id.js +30 -11
- package/dist/context/index.d.ts +1 -1
- package/dist/context/index.js +3 -3
- package/dist/context/types.d.ts +10 -1
- package/dist/context/types.js +10 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +14 -11
- package/dist/labels.d.ts +66 -12
- package/dist/labels.js +74 -17
- package/dist/mcp/held-run-resolve.d.ts +40 -0
- package/dist/mcp/held-run-resolve.js +68 -15
- package/dist/mcp/tool-schemas.d.ts +4 -0
- package/dist/mcp/tool-schemas.js +13 -1
- package/dist/metrics/catalog-policy.d.ts +6 -3
- package/dist/metrics/catalog-policy.js +31 -10
- package/dist/metrics/metric-catalog.generated.d.ts +110 -0
- package/dist/metrics/metric-catalog.generated.js +132 -0
- package/dist/protocol/dashboard-global-workflows.js +2 -2
- package/dist/protocol/event-log-payload.js +1 -1
- package/dist/protocol/messages/access-log.d.ts +5 -0
- package/dist/protocol/messages/access-log.js +1 -0
- package/dist/protocol/messages/actor.d.ts +13 -2
- package/dist/protocol/messages/actor.js +16 -5
- package/dist/protocol/messages/common.js +1 -1
- package/dist/protocol/messages/dashboard-global-workflows.d.ts +21 -0
- package/dist/protocol/messages/dashboard-global-workflows.js +28 -1
- package/dist/protocol/messages/dashboard.d.ts +43 -5
- package/dist/protocol/messages/dashboard.js +55 -6
- package/dist/protocol/messages/execution-status.d.ts +41 -0
- package/dist/protocol/messages/execution-status.js +53 -2
- package/dist/protocol/messages/git-credential-relay.d.ts +78 -0
- package/dist/protocol/messages/git-credential-relay.js +86 -0
- package/dist/protocol/messages/orchestrator-agent.d.ts +89 -0
- package/dist/protocol/messages/orchestrator-agent.js +98 -3
- package/dist/protocol/messages/peer.d.ts +7 -0
- package/dist/protocol/messages/peer.js +18 -1
- package/dist/protocol/messages/platform-orchestrator.d.ts +150 -0
- package/dist/protocol/messages/platform-orchestrator.js +164 -19
- package/dist/protocol/version.d.ts +19 -2
- package/dist/protocol/version.js +20 -3
- package/dist/provenance/verify.js +11 -10
- package/dist/provider/check-status-poster.d.ts +24 -3
- package/dist/provider/contributor-resolver.d.ts +11 -3
- package/dist/provider/git-credential.d.ts +77 -0
- package/dist/provider/git-credential.js +10 -0
- package/dist/provider/index.d.ts +2 -0
- package/dist/provider/index.js +2 -1
- package/dist/provider/webhook-normalizer.d.ts +12 -12
- package/dist/repo/pattern-negation.d.ts +73 -0
- package/dist/repo/pattern-negation.js +86 -0
- package/dist/scaler/registry-auth.d.ts +18 -0
- package/dist/scaler/registry-auth.js +28 -0
- package/dist/scaler/scaler-backend-type.d.ts +35 -0
- package/dist/scaler/scaler-backend-type.js +39 -2
- package/dist/scaler/scaler-events.d.ts +79 -0
- package/dist/scaler/scaler-events.js +87 -0
- package/dist/trigger/content-requirements.js +1 -1
- package/dist/trigger/decision-trace.d.ts +79 -0
- package/dist/trigger/decision-trace.js +116 -8
- package/dist/trigger/matcher.js +4 -2
- package/dist/trigger/types.d.ts +87 -7
- package/dist/trigger/types.js +6 -1
- package/dist/ws/rate-limiter.js +3 -3
- package/package.json +11 -3
- package/sbom.spdx.json +15 -15
package/dist/labels.js
CHANGED
|
@@ -253,6 +253,39 @@ function capabilityLabel(name) {
|
|
|
253
253
|
*/
|
|
254
254
|
const SSH_TRANSPORT_CAPABILITY = "kici:capability:ssh-transport";
|
|
255
255
|
/**
|
|
256
|
+
* Runtime-fact label prefix — `kici:runtime:<name>` states something the AGENT
|
|
257
|
+
* discovered about its own host.
|
|
258
|
+
*
|
|
259
|
+
* Deliberately NOT `kici:capability:`. That prefix grants a privilege
|
|
260
|
+
* (`ssh-transport` custodies the bootstrap SSH key), so it must stay
|
|
261
|
+
* token-bound and an agent must never be able to self-assert it. A runtime fact
|
|
262
|
+
* grants nothing: it reports what the host can do, the way `kici:os:` reports
|
|
263
|
+
* what the host is, and it is self-reported for the same reason — the scaler
|
|
264
|
+
* cannot predict it at token-mint time.
|
|
265
|
+
*/
|
|
266
|
+
const RUNTIME_LABEL_PREFIX = "kici:runtime:";
|
|
267
|
+
/** Build a `kici:runtime:<name>` label. */
|
|
268
|
+
function runtimeLabel(name) {
|
|
269
|
+
return `${RUNTIME_LABEL_PREFIX}${name}`;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Runtime facts an agent reports about its own host at registration.
|
|
273
|
+
*
|
|
274
|
+
* - `docker` / `podman` — that container runtime's socket answered, so the
|
|
275
|
+
* agent can nest a job container.
|
|
276
|
+
* - `container-build` — a `docker` / `podman` CLI is on PATH, so the agent can
|
|
277
|
+
* build a job's image from a Dockerfile. Distinct from the two above: a host
|
|
278
|
+
* reachable only through a mounted socket can RUN containers but not BUILD
|
|
279
|
+
* one, because the build shells out to the CLI.
|
|
280
|
+
*/
|
|
281
|
+
const RuntimeFact = z.enum([
|
|
282
|
+
"docker",
|
|
283
|
+
"podman",
|
|
284
|
+
"container-build"
|
|
285
|
+
]);
|
|
286
|
+
/** The label a job's `runsOn` names to require a host that can build an image. */
|
|
287
|
+
const CONTAINER_BUILD_RUNTIME_LABEL = runtimeLabel(RuntimeFact.enum["container-build"]);
|
|
288
|
+
/**
|
|
256
289
|
* The `kici:init` lifecycle label carried by a temporary init-runner agent
|
|
257
290
|
* brought up on a fresh box for bootstrap. Marks it as ephemeral + privileged;
|
|
258
291
|
* it dies on reboot and is reaped. Distinct from `kici:role:init-runner`
|
|
@@ -297,32 +330,56 @@ function resolveRoleLabels(roles) {
|
|
|
297
330
|
}
|
|
298
331
|
/**
|
|
299
332
|
* The full label set a scaler injects into an agent it spawns: the base label
|
|
300
|
-
* set plus the scaler-assigned `kici:agent:`, `kici:scaler:`,
|
|
301
|
-
* labels. This is exactly what the scaler writes into
|
|
302
|
-
* the set an ephemeral agent token must be bound to —
|
|
303
|
-
* the self-reported platform facts (`kici:os:`,
|
|
304
|
-
* registration, which the orchestrator's
|
|
305
|
-
* `isSelfReportedLabel`). Keep this
|
|
306
|
-
* construction in
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
333
|
+
* set plus the scaler-assigned `kici:agent:`, `kici:scaler:`, `kici:role:`, and
|
|
334
|
+
* platform-taint labels. This is exactly what the scaler writes into
|
|
335
|
+
* `KICI_LABELS`, and it is the set an ephemeral agent token must be bound to —
|
|
336
|
+
* the agent then adds only the self-reported platform facts (`kici:os:`,
|
|
337
|
+
* `kici:arch:`, `kici:host:`) at registration, which the orchestrator's
|
|
338
|
+
* register-time scope gate exempts (see `isSelfReportedLabel`). Keep this
|
|
339
|
+
* aligned with the agent's wire-label construction in
|
|
340
|
+
* `orchestrator-client.ts#sendAgentRegister`.
|
|
341
|
+
*
|
|
342
|
+
* `platformTaints` are the plain tokens (`windows`, `macos`, `arm64`) derived
|
|
343
|
+
* from the pool's resolved platform. A taint is a routing GRANT, not a host
|
|
344
|
+
* fact: it is what a job's `runsOn` names to reach this pool, and the matcher
|
|
345
|
+
* gates on it. So the orchestrator asserts it here — bound into the agent's
|
|
346
|
+
* ephemeral token — rather than letting the agent self-report it. Adding these
|
|
347
|
+
* prefixes to `SELF_REPORTED_LABEL_PREFIXES` instead would let any agent claim
|
|
348
|
+
* any platform unchallenged, which is the opposite of a gate. The caller must
|
|
349
|
+
* derive them from the same source the gate uses
|
|
350
|
+
* (`ScalerManager.resolveScalerPlatform`) — deriving them twice is exactly the
|
|
351
|
+
* defect this parameter closes.
|
|
352
|
+
*
|
|
353
|
+
* The result is de-duplicated in first-seen order: a warm pool measures its
|
|
354
|
+
* readiness with a label set that already carries the taints, so it passes them
|
|
355
|
+
* as `labelSet` AND as `platformTaints`, and a label repeated in the set an
|
|
356
|
+
* ephemeral token is bound to is never meaningful.
|
|
357
|
+
*/
|
|
358
|
+
function scalerAgentLabels(labelSet, backendType, backendName, roles, platformTaints = []) {
|
|
359
|
+
return [.../* @__PURE__ */ new Set([
|
|
310
360
|
...labelSet,
|
|
311
361
|
agentTypeLabel(backendType),
|
|
312
362
|
scalerLabel(backendName),
|
|
313
|
-
...resolveRoleLabels(roles)
|
|
314
|
-
|
|
363
|
+
...resolveRoleLabels(roles),
|
|
364
|
+
...platformTaints
|
|
365
|
+
])];
|
|
315
366
|
}
|
|
316
367
|
/**
|
|
317
368
|
* Label-category prefixes the agent self-reports at registration from its own
|
|
318
|
-
* host
|
|
319
|
-
* platform facts, not authorization grants, so the orchestrator's
|
|
320
|
-
* label-scope gate does not require an ephemeral token to be
|
|
369
|
+
* host: operating system, CPU architecture, hostname, and runtime facts. They
|
|
370
|
+
* are platform facts, not authorization grants, so the orchestrator's
|
|
371
|
+
* register-time label-scope gate does not require an ephemeral token to be
|
|
372
|
+
* bound to them.
|
|
373
|
+
*
|
|
374
|
+
* Adding a prefix here lets any agent assert it unchallenged. That is correct
|
|
375
|
+
* for a fact about the host and WRONG for anything that grants a privilege —
|
|
376
|
+
* `kici:capability:` is absent from this list for exactly that reason.
|
|
321
377
|
*/
|
|
322
378
|
const SELF_REPORTED_LABEL_PREFIXES = [
|
|
323
379
|
"kici:os:",
|
|
324
380
|
"kici:arch:",
|
|
325
|
-
"kici:host:"
|
|
381
|
+
"kici:host:",
|
|
382
|
+
RUNTIME_LABEL_PREFIX
|
|
326
383
|
];
|
|
327
384
|
/**
|
|
328
385
|
* True if a label is a self-reported platform fact (os/arch/host) rather than a
|
|
@@ -360,6 +417,6 @@ function separateLabels(labels) {
|
|
|
360
417
|
};
|
|
361
418
|
}
|
|
362
419
|
//#endregion
|
|
363
|
-
export { CAPABILITY_LABEL_PREFIX, HOST_LABEL_PREFIX, INIT_LABEL, INIT_RUNNER_ROLE_LABEL, KNOWN_ROLES, PLATFORM_TAINT_LABELS, PRIVILEGED_ROOT_LABEL, RESERVED_LABEL_PREFIX, ROLE_LABEL_PREFIX, SELF_REPORTED_LABEL_PREFIXES, SSH_TRANSPORT_CAPABILITY, ScalerArch, ScalerOs, agentTypeLabel, capabilityLabel, deriveOsArchLabels, derivePlatformTaints, hostLabel, hostToScalerPlatform, isAutoLabel, isSelfReportedLabel, mergeAutoLabels, nodeArchToScalerArch, nodePlatformToScalerOs, normalizeRunsOn, parseHostLabel, platformToOsArchLabels, platformToTaints, resolveRoleLabels, roleToLabel, scalerAgentLabels, scalerLabel, scalerPlatformSchema, separateLabels, validateNoReservedLabels };
|
|
420
|
+
export { CAPABILITY_LABEL_PREFIX, CONTAINER_BUILD_RUNTIME_LABEL, HOST_LABEL_PREFIX, INIT_LABEL, INIT_RUNNER_ROLE_LABEL, KNOWN_ROLES, PLATFORM_TAINT_LABELS, PRIVILEGED_ROOT_LABEL, RESERVED_LABEL_PREFIX, ROLE_LABEL_PREFIX, RUNTIME_LABEL_PREFIX, RuntimeFact, SELF_REPORTED_LABEL_PREFIXES, SSH_TRANSPORT_CAPABILITY, ScalerArch, ScalerOs, agentTypeLabel, capabilityLabel, deriveOsArchLabels, derivePlatformTaints, hostLabel, hostToScalerPlatform, isAutoLabel, isSelfReportedLabel, mergeAutoLabels, nodeArchToScalerArch, nodePlatformToScalerOs, normalizeRunsOn, parseHostLabel, platformToOsArchLabels, platformToTaints, resolveRoleLabels, roleToLabel, runtimeLabel, scalerAgentLabels, scalerLabel, scalerPlatformSchema, separateLabels, validateNoReservedLabels };
|
|
364
421
|
|
|
365
422
|
//# sourceMappingURL=labels.js.map
|
|
@@ -15,6 +15,17 @@ export interface HeldRunSummary {
|
|
|
15
15
|
* orchestrator's response.
|
|
16
16
|
*/
|
|
17
17
|
holdType?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Bound context that requested the hold, when the row carries one. Null once
|
|
20
|
+
* the context is deleted, and absent from an older orchestrator's response.
|
|
21
|
+
*/
|
|
22
|
+
contextName?: string | null;
|
|
23
|
+
/** Persisted `held_runs.queue_type` (`context` / `security`), when present. */
|
|
24
|
+
queueType?: string;
|
|
25
|
+
/** Why the hold was requested, when the row carries a reason. */
|
|
26
|
+
reason?: string | null;
|
|
27
|
+
/** When the hold expires, as an ISO timestamp; null when it never does. */
|
|
28
|
+
expiresAt?: string | null;
|
|
18
29
|
/** Computed drift payload for a `when: 'drift'` step hold; absent otherwise. */
|
|
19
30
|
payload?: {
|
|
20
31
|
summaryMarkdown: string;
|
|
@@ -27,6 +38,26 @@ export interface HeldRunFilter {
|
|
|
27
38
|
job?: string;
|
|
28
39
|
/** Match a step-scoped hold by its step index (compared as a string). */
|
|
29
40
|
step?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Match one hold by its own id, ignoring every other filter.
|
|
43
|
+
*
|
|
44
|
+
* The escape hatch for an ambiguity nothing else can resolve. A job can carry
|
|
45
|
+
* more than one pending hold — an SDK `requireApproval` and a security-typed
|
|
46
|
+
* context gate produce two job-scoped rows under one job name — and while
|
|
47
|
+
* {@link holdType} separates that pair, nothing guarantees a future pair
|
|
48
|
+
* differs in type. An id always does, and the candidate list prints the ids
|
|
49
|
+
* whenever two holds are otherwise indistinguishable.
|
|
50
|
+
*/
|
|
51
|
+
holdId?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Narrow to holds of one type (`reviewer` / `timer` / `concurrency` /
|
|
54
|
+
* `security`), normalized through `normalizePersistedHoldType` on both sides
|
|
55
|
+
* so a legacy `approval` row answers to `reviewer`.
|
|
56
|
+
*
|
|
57
|
+
* Composes with {@link job} / {@link step} rather than replacing them: a
|
|
58
|
+
* matrix whose children each carry two holds needs both halves.
|
|
59
|
+
*/
|
|
60
|
+
holdType?: string;
|
|
30
61
|
}
|
|
31
62
|
/** Resolution result: either a held-run id or a user-facing error message. */
|
|
32
63
|
export type ResolveResult = {
|
|
@@ -40,10 +71,19 @@ export type ResolveResult = {
|
|
|
40
71
|
/**
|
|
41
72
|
* Resolve the held-run id matching the filter from a list of pending holds.
|
|
42
73
|
*
|
|
74
|
+
* - `holdId` names one hold outright and ignores every other filter.
|
|
43
75
|
* - `step` requires `job` and matches a `step`-scoped hold whose step index
|
|
44
76
|
* equals the given value.
|
|
45
77
|
* - `job` alone matches a `job`/`workflow`-scoped hold for that job.
|
|
78
|
+
* - `holdType` narrows whichever set the above produced, and may be used on its
|
|
79
|
+
* own.
|
|
46
80
|
* - With no filter, the sole pending hold is used; ambiguity is an error.
|
|
81
|
+
*
|
|
82
|
+
* One job can carry more than one pending hold: an SDK `requireApproval` paired
|
|
83
|
+
* with a security-typed context gate writes two job-scoped rows under one job
|
|
84
|
+
* name, because two independent requirements gate the job and both must be
|
|
85
|
+
* answered. `job` alone cannot separate those, which is what `holdType` and
|
|
86
|
+
* `holdId` are for.
|
|
47
87
|
*/
|
|
48
88
|
export declare function resolveHeldRunId(holds: readonly HeldRunSummary[], filter: HeldRunFilter): ResolveResult;
|
|
49
89
|
//# sourceMappingURL=held-run-resolve.d.ts.map
|
|
@@ -1,38 +1,86 @@
|
|
|
1
1
|
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
2
|
import { HeldRunStatus } from "../context/held-run-status.js";
|
|
3
|
+
import { normalizePersistedHoldType } from "../context/hold-type.js";
|
|
3
4
|
//#region src/mcp/held-run-resolve.ts
|
|
4
5
|
/**
|
|
5
6
|
* Shared held-run resolution for the `kici approve` / `kici reject` commands and
|
|
6
7
|
* the developer MCP `approve_run` / `reject_run` tools.
|
|
7
8
|
*
|
|
8
|
-
* Both surfaces first list the pending holds for a run, then resolve the one
|
|
9
|
-
*
|
|
10
|
-
* exactly one and no filter is given). The resolution is a
|
|
11
|
-
* can be unit-tested without HTTP, and it imports nothing
|
|
12
|
-
* held-run vocabulary, so it is safe to re-export from the
|
|
13
|
-
* engine barrel.
|
|
9
|
+
* Both surfaces first list the pending holds for a run, then resolve the one the
|
|
10
|
+
* caller named via `job` / `step` / `holdType` / `holdId` (or the sole pending
|
|
11
|
+
* hold when there is exactly one and no filter is given). The resolution is a
|
|
12
|
+
* pure function so it can be unit-tested without HTTP, and it imports nothing
|
|
13
|
+
* beyond the shared held-run vocabulary, so it is safe to re-export from the
|
|
14
|
+
* browser-facing engine barrel.
|
|
15
|
+
*
|
|
16
|
+
* A job name is not a unique key: two independent requirements can gate one job,
|
|
17
|
+
* and each writes its own pending row under that name. So every ambiguity path
|
|
18
|
+
* here has to end at a filter that can actually separate them, and the candidate
|
|
19
|
+
* list has to print enough for the caller to pick one.
|
|
14
20
|
*/
|
|
15
21
|
/**
|
|
16
22
|
* Name a hold the way the caller would type it: the literal `jobId` (so an
|
|
17
23
|
* `__install__…` sentinel or an opaque legacy id is reproduced verbatim), with
|
|
18
|
-
* the step index prefixed for a step-scoped hold.
|
|
24
|
+
* the step index prefixed for a step-scoped hold, and the hold type appended.
|
|
25
|
+
*
|
|
26
|
+
* The type is what makes the candidate list actionable when one job carries two
|
|
27
|
+
* holds — without it both rows rendered as the bare job name, so the list
|
|
28
|
+
* collapsed to a single entry and named a `--job` filter that could not tell
|
|
29
|
+
* them apart. It is omitted when the orchestrator's list response carried none
|
|
30
|
+
* (an older build), which renders exactly as it did before.
|
|
19
31
|
*/
|
|
20
32
|
function describeHold(hold) {
|
|
21
33
|
const job = hold.jobId || "(unnamed hold)";
|
|
22
|
-
|
|
23
|
-
return
|
|
34
|
+
const base = hold.holdScope === "step" && hold.stepIndex != null ? `step ${hold.stepIndex} of ${job}` : job;
|
|
35
|
+
return hold.holdType ? `${base} (${normalizePersistedHoldType(hold.holdType)})` : base;
|
|
24
36
|
}
|
|
25
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* The hold descriptions, in listing order, for an error message.
|
|
39
|
+
*
|
|
40
|
+
* A description that occurs once is listed once. One that occurs more than once
|
|
41
|
+
* is listed per hold WITH its id, because at that point the description alone
|
|
42
|
+
* cannot name which hold the caller means and `--hold <id>` is the only filter
|
|
43
|
+
* left that can. Silently de-duplicating those is what made the old message
|
|
44
|
+
* offer a disambiguator it had already discarded the information for.
|
|
45
|
+
*/
|
|
26
46
|
function listCandidates(holds) {
|
|
27
|
-
|
|
47
|
+
const counts = /* @__PURE__ */ new Map();
|
|
48
|
+
for (const hold of holds) {
|
|
49
|
+
const text = describeHold(hold);
|
|
50
|
+
counts.set(text, (counts.get(text) ?? 0) + 1);
|
|
51
|
+
}
|
|
52
|
+
const emitted = /* @__PURE__ */ new Set();
|
|
53
|
+
const out = [];
|
|
54
|
+
for (const hold of holds) {
|
|
55
|
+
const text = describeHold(hold);
|
|
56
|
+
if ((counts.get(text) ?? 0) > 1) {
|
|
57
|
+
out.push(`${text} [${hold.id}]`);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (emitted.has(text)) continue;
|
|
61
|
+
emitted.add(text);
|
|
62
|
+
out.push(text);
|
|
63
|
+
}
|
|
64
|
+
return out.join(", ");
|
|
28
65
|
}
|
|
66
|
+
/** The disambiguators a caller can reach for, named the way the CLI spells them. */
|
|
67
|
+
const DISAMBIGUATORS = "--job <name>, --step <index>, --hold-type <type> or --hold <id>";
|
|
29
68
|
/**
|
|
30
69
|
* Resolve the held-run id matching the filter from a list of pending holds.
|
|
31
70
|
*
|
|
71
|
+
* - `holdId` names one hold outright and ignores every other filter.
|
|
32
72
|
* - `step` requires `job` and matches a `step`-scoped hold whose step index
|
|
33
73
|
* equals the given value.
|
|
34
74
|
* - `job` alone matches a `job`/`workflow`-scoped hold for that job.
|
|
75
|
+
* - `holdType` narrows whichever set the above produced, and may be used on its
|
|
76
|
+
* own.
|
|
35
77
|
* - With no filter, the sole pending hold is used; ambiguity is an error.
|
|
78
|
+
*
|
|
79
|
+
* One job can carry more than one pending hold: an SDK `requireApproval` paired
|
|
80
|
+
* with a security-typed context gate writes two job-scoped rows under one job
|
|
81
|
+
* name, because two independent requirements gate the job and both must be
|
|
82
|
+
* answered. `job` alone cannot separate those, which is what `holdType` and
|
|
83
|
+
* `holdId` are for.
|
|
36
84
|
*/
|
|
37
85
|
function resolveHeldRunId(holds, filter) {
|
|
38
86
|
const pending = holds.filter((h) => h.status === HeldRunStatus.enum.pending);
|
|
@@ -40,17 +88,22 @@ function resolveHeldRunId(holds, filter) {
|
|
|
40
88
|
ok: false,
|
|
41
89
|
error: "No pending approval holds found for this run."
|
|
42
90
|
};
|
|
91
|
+
if (filter.holdId !== void 0) return pickSingle(pending.filter((h) => h.id === filter.holdId), `hold '${filter.holdId}'`, pending);
|
|
92
|
+
const wanted = filter.holdType && normalizePersistedHoldType(filter.holdType);
|
|
93
|
+
const byType = (candidates) => wanted === void 0 ? candidates : candidates.filter((h) => h.holdType && normalizePersistedHoldType(h.holdType) === wanted);
|
|
94
|
+
const typeLabel = wanted ? ` of type '${wanted}'` : "";
|
|
43
95
|
if (filter.step !== void 0) {
|
|
44
96
|
if (!filter.job) return {
|
|
45
97
|
ok: false,
|
|
46
98
|
error: "--step requires --job to identify the held step."
|
|
47
99
|
};
|
|
48
|
-
return pickSingle(pending.filter((h) => h.holdScope === "step" && h.jobId === filter.job && String(h.stepIndex ?? "") === filter.step), `step ${filter.step} of job '${filter.job}'`, pending);
|
|
100
|
+
return pickSingle(byType(pending.filter((h) => h.holdScope === "step" && h.jobId === filter.job && String(h.stepIndex ?? "") === filter.step)), `step ${filter.step} of job '${filter.job}'${typeLabel}`, pending);
|
|
49
101
|
}
|
|
50
|
-
if (filter.job !== void 0) return pickSingle(pending.filter((h) => h.jobId === filter.job && h.holdScope !== "step"), `job '${filter.job}'`, pending);
|
|
102
|
+
if (filter.job !== void 0) return pickSingle(byType(pending.filter((h) => h.jobId === filter.job && h.holdScope !== "step")), `job '${filter.job}'${typeLabel}`, pending);
|
|
103
|
+
if (wanted !== void 0) return pickSingle(byType(pending), `hold type '${wanted}'`, pending);
|
|
51
104
|
if (pending.length > 1) return {
|
|
52
105
|
ok: false,
|
|
53
|
-
error: `Multiple pending holds for this run. Use
|
|
106
|
+
error: `Multiple pending holds for this run. Use ${DISAMBIGUATORS} to choose one. Candidates: ${listCandidates(pending)}.`
|
|
54
107
|
};
|
|
55
108
|
return {
|
|
56
109
|
ok: true,
|
|
@@ -65,7 +118,7 @@ function pickSingle(matches, label, pending) {
|
|
|
65
118
|
};
|
|
66
119
|
if (matches.length > 1) return {
|
|
67
120
|
ok: false,
|
|
68
|
-
error: `Multiple pending holds match ${label}
|
|
121
|
+
error: `Multiple pending holds match ${label}. Narrow it with ${DISAMBIGUATORS}. Candidates: ${listCandidates(matches)}.`
|
|
69
122
|
};
|
|
70
123
|
return {
|
|
71
124
|
ok: true,
|
|
@@ -59,12 +59,16 @@ export declare const approveRunToolSchema: {
|
|
|
59
59
|
runId: z.ZodString;
|
|
60
60
|
job: z.ZodOptional<z.ZodString>;
|
|
61
61
|
step: z.ZodOptional<z.ZodString>;
|
|
62
|
+
holdType: z.ZodOptional<z.ZodString>;
|
|
63
|
+
holdId: z.ZodOptional<z.ZodString>;
|
|
62
64
|
};
|
|
63
65
|
export declare const rejectRunToolSchema: {
|
|
64
66
|
orgId: z.ZodOptional<z.ZodString>;
|
|
65
67
|
runId: z.ZodString;
|
|
66
68
|
job: z.ZodOptional<z.ZodString>;
|
|
67
69
|
step: z.ZodOptional<z.ZodString>;
|
|
70
|
+
holdType: z.ZodOptional<z.ZodString>;
|
|
71
|
+
holdId: z.ZodOptional<z.ZodString>;
|
|
68
72
|
reason: z.ZodString;
|
|
69
73
|
};
|
|
70
74
|
export declare const cancelRunsByBranchToolSchema: {
|
package/dist/mcp/tool-schemas.js
CHANGED
|
@@ -55,17 +55,29 @@ const triggerRunToolSchema = {
|
|
|
55
55
|
orgId: orgIdArg,
|
|
56
56
|
registrationId: z.string().min(1).describe("The workflow registration id to trigger.")
|
|
57
57
|
};
|
|
58
|
+
/**
|
|
59
|
+
* The two filters that separate holds a job name cannot. One job can carry more
|
|
60
|
+
* than one pending hold — an SDK `requireApproval` paired with a security-typed
|
|
61
|
+
* context gate writes two job-scoped rows under one job name — so `job` alone is
|
|
62
|
+
* ambiguous for that shape, and the error listing names these instead.
|
|
63
|
+
*/
|
|
64
|
+
const holdTypeArg = z.string().optional().describe("Hold type (reviewer, timer, concurrency, security), when one job carries more than one hold.");
|
|
65
|
+
const holdIdArg = z.string().optional().describe("A specific hold id, as printed in the candidate list when nothing else separates them.");
|
|
58
66
|
const approveRunToolSchema = {
|
|
59
67
|
orgId: orgIdArg,
|
|
60
68
|
runId: z.string().min(1).describe("The run whose held approval gate to approve."),
|
|
61
69
|
job: z.string().optional().describe("Job name, to disambiguate when the run has multiple holds."),
|
|
62
|
-
step: z.string().optional().describe("Zero-based step index (requires job) for a step-scoped hold.")
|
|
70
|
+
step: z.string().optional().describe("Zero-based step index (requires job) for a step-scoped hold."),
|
|
71
|
+
holdType: holdTypeArg,
|
|
72
|
+
holdId: holdIdArg
|
|
63
73
|
};
|
|
64
74
|
const rejectRunToolSchema = {
|
|
65
75
|
orgId: orgIdArg,
|
|
66
76
|
runId: z.string().min(1).describe("The run whose held approval gate to reject."),
|
|
67
77
|
job: z.string().optional().describe("Job name, to disambiguate when the run has multiple holds."),
|
|
68
78
|
step: z.string().optional().describe("Zero-based step index (requires job) for a step-scoped hold."),
|
|
79
|
+
holdType: holdTypeArg,
|
|
80
|
+
holdId: holdIdArg,
|
|
69
81
|
reason: z.string().min(1).describe("Why the gate is rejected (required).")
|
|
70
82
|
};
|
|
71
83
|
const cancelRunsByBranchToolSchema = {
|
|
@@ -55,10 +55,13 @@ export declare const ORCH_PUSHED_METRIC_NAMES: ReadonlySet<MetricName>;
|
|
|
55
55
|
/**
|
|
56
56
|
* Closed enum of scaler-backend TYPE values the orchestrator's own
|
|
57
57
|
* resource-usage gauges carry on their `scalerType` rollup label.
|
|
58
|
-
* `__global__` is the orchestrator-wide rollup row;
|
|
59
|
-
* `
|
|
58
|
+
* `__global__` is the orchestrator-wide rollup row; every other value comes
|
|
59
|
+
* from `ScalerManager`'s own `backends` map, so the enum is `ScalerBackendType`
|
|
60
|
+
* itself — NOT `AGENT_SCALER_VALUES`, which additionally carries the
|
|
61
|
+
* agent-only `stateful` and predates the `event` backend. Exported so the
|
|
62
|
+
* policy tests can assert against it.
|
|
60
63
|
*/
|
|
61
|
-
export declare const ORCH_SCALER_VALUES: readonly ["__global__", "
|
|
64
|
+
export declare const ORCH_SCALER_VALUES: readonly ["__global__", ...("bare-metal" | "container" | "event" | "firecracker" | "kubernetes")[]];
|
|
62
65
|
/**
|
|
63
66
|
* Closed enum of the scheduled jobs the orchestrator runs. MUST mirror
|
|
64
67
|
* `OrchestratorScheduledJobName` (`packages/orchestrator/src/queue/scheduled-job.ts`)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
2
|
import { ExecutionRunStatus } from "../protocol/messages/execution-status.js";
|
|
3
|
+
import { ScalerBackendType } from "../scaler/scaler-backend-type.js";
|
|
3
4
|
import { MetricNames, MetricService } from "./metric-catalog.generated.js";
|
|
4
5
|
//#region src/metrics/catalog-policy.ts
|
|
5
6
|
/**
|
|
@@ -41,20 +42,26 @@ const OVERFLOW_LABEL_VALUE = "__overflow__";
|
|
|
41
42
|
* so they are valid push payloads alongside `kici_orch_*`.
|
|
42
43
|
*/
|
|
43
44
|
const ORCH_PUSHED_METRIC_NAMES = new Set(Object.entries(MetricService).filter(([, svc]) => svc === "orchestrator" || svc === "agent" || svc === "runtime").map(([key]) => MetricNames[key]));
|
|
44
|
-
/**
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Closed enum of the `scaler` label values an agent can carry: every scaler
|
|
47
|
+
* backend type the orchestrator can resolve for a managed agent, plus
|
|
48
|
+
* `stateful` for a static agent no scaler manages (the fallback
|
|
49
|
+
* `AgentMetricsAggregator.resolveScalerLabel` stamps when the lookup returns
|
|
50
|
+
* null). Derived from `ScalerBackendType` rather than restated, so adding a
|
|
51
|
+
* backend cannot silently drop every `kici_agent_*` series that backend's
|
|
52
|
+
* agents emit as `bad_label_value`.
|
|
53
|
+
*/
|
|
54
|
+
const AGENT_SCALER_VALUES = ["stateful", ...ScalerBackendType.options];
|
|
51
55
|
/**
|
|
52
56
|
* Closed enum of scaler-backend TYPE values the orchestrator's own
|
|
53
57
|
* resource-usage gauges carry on their `scalerType` rollup label.
|
|
54
|
-
* `__global__` is the orchestrator-wide rollup row;
|
|
55
|
-
* `
|
|
58
|
+
* `__global__` is the orchestrator-wide rollup row; every other value comes
|
|
59
|
+
* from `ScalerManager`'s own `backends` map, so the enum is `ScalerBackendType`
|
|
60
|
+
* itself — NOT `AGENT_SCALER_VALUES`, which additionally carries the
|
|
61
|
+
* agent-only `stateful` and predates the `event` backend. Exported so the
|
|
62
|
+
* policy tests can assert against it.
|
|
56
63
|
*/
|
|
57
|
-
const ORCH_SCALER_VALUES = ["__global__", ...
|
|
64
|
+
const ORCH_SCALER_VALUES = ["__global__", ...ScalerBackendType.options];
|
|
58
65
|
/**
|
|
59
66
|
* Closed enum of the scheduled jobs the orchestrator runs. MUST mirror
|
|
60
67
|
* `OrchestratorScheduledJobName` (`packages/orchestrator/src/queue/scheduled-job.ts`)
|
|
@@ -146,6 +153,20 @@ const METRIC_LABEL_POLICY = {
|
|
|
146
153
|
scalerType: { values: ORCH_SCALER_VALUES },
|
|
147
154
|
machinePool: { maxUniqueValues: 50 }
|
|
148
155
|
},
|
|
156
|
+
kici_orch_scaler_warm_pool_target: {
|
|
157
|
+
scaler: { maxUniqueValues: 50 },
|
|
158
|
+
labelSet: { maxUniqueValues: 100 }
|
|
159
|
+
},
|
|
160
|
+
kici_orch_scaler_warm_pool_ready: {
|
|
161
|
+
scaler: { maxUniqueValues: 50 },
|
|
162
|
+
labelSet: { maxUniqueValues: 100 }
|
|
163
|
+
},
|
|
164
|
+
kici_orch_scaler_warm_pool_in_flight: {
|
|
165
|
+
scaler: { maxUniqueValues: 50 },
|
|
166
|
+
labelSet: { maxUniqueValues: 100 }
|
|
167
|
+
},
|
|
168
|
+
kici_orch_scaler_warm_pool_spawns_total: { scaler: { maxUniqueValues: 50 } },
|
|
169
|
+
kici_orch_scaler_warm_pool_reaped_total: { scaler: { maxUniqueValues: 50 } },
|
|
149
170
|
kici_orch_install_secrets_decisions_total: {
|
|
150
171
|
decision: { values: [
|
|
151
172
|
"pass",
|