@kici-dev/agent 0.1.24 → 0.1.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -216,12 +216,16 @@ export interface StepApprovalRequestIpc {
216
216
  };
217
217
  }
218
218
  /** Which provenance upload operation to relay. */
219
- export type ProvenanceRequestOp = 'requestUploadUrl' | 'complete';
219
+ export type ProvenanceRequestOp = 'requestUploadUrl' | 'complete' | 'defer';
220
220
  /**
221
221
  * Request a provenance bundle upload operation (runner -> agent). The agent
222
- * relays it over the WS as a `provenance.upload.request` / `.complete` and pipes
223
- * the response back as a {@link ProvenanceResponseIpc}. Mirrors the
224
- * {@link CacheRequestIpc} relay pattern.
222
+ * relays it over the WS as a `provenance.upload.request` / `.complete` /
223
+ * `.defer` and pipes the response back as a {@link ProvenanceResponseIpc}.
224
+ * Mirrors the {@link CacheRequestIpc} relay pattern.
225
+ *
226
+ * `defer` captures a frozen, DSSE-signed statement for later minting (the
227
+ * transient mint-failure path): no upload happens; the orchestrator persists
228
+ * the envelope in its deferred-attestation outbox instead.
225
229
  */
226
230
  export interface ProvenanceRequestIpc {
227
231
  type: 'provenance.request';
@@ -231,10 +235,18 @@ export interface ProvenanceRequestIpc {
231
235
  op: ProvenanceRequestOp;
232
236
  /** Primary subject digest (lowercase hex) — the storage-key discriminator. */
233
237
  subjectDigest: string;
234
- /** Caller-supplied artifact name. `complete` only. */
238
+ /** Caller-supplied artifact name. `complete` + `defer` only. */
235
239
  subjectName?: string;
236
- /** Bundle media type. `complete` only. */
240
+ /** Bundle media type. `complete` + `defer` only. */
237
241
  mediaType?: string;
242
+ /** Requested token audience. `defer` only. */
243
+ audience?: string;
244
+ /** SHA-256 of the frozen DSSE statement payload. `defer` only. */
245
+ statementHash?: string;
246
+ /** Frozen, DSSE-signed statement envelope. `defer` only. */
247
+ dsseEnvelope?: unknown;
248
+ /** Ephemeral public key JWK the envelope was signed with. `defer` only. */
249
+ publicKey?: unknown;
238
250
  }
239
251
  export type RunnerToAgentMessage = ReadyMessage | StepStartMessage | StepCompleteMessage | LogLineMessage | StepSecretMountMessage | JobCompleteMessage | EventEmitRequest | ConcurrencyReportMessage | AgentApiRequestIpc | CacheRequestIpc | ProvenanceRequestIpc | StepApprovalRequestIpc;
240
252
  /** Instruct the workflow runner to execute a job. */
@@ -466,6 +478,15 @@ export interface JobExecutionRequest {
466
478
  event?: Record<string, unknown>;
467
479
  /** Git provider that originated the triggering event (e.g. 'github', 'forgejo'). */
468
480
  provider?: string;
481
+ /**
482
+ * Platform provenance issuer, threaded from the orchestrator for a deferred
483
+ * attestation's frozen `builder.id`. Best-effort: absent when the orchestrator
484
+ * has no issuer wired (e.g. never authenticated to the Platform), in which
485
+ * case the frozen statement records an unknown issuer honestly. Not
486
+ * verification load-bearing — a deferred bundle's later token binds to the
487
+ * frozen statement by hash, and the authoritative org id lives in that token.
488
+ */
489
+ provenanceIssuer?: string;
469
490
  /** Whether to checkout the repo (default: true). */
470
491
  checkout?: boolean;
471
492
  /** Whether this job is part of a developer-initiated run triggered by `kici run`. */
package/dist/index.js CHANGED
@@ -20,7 +20,14 @@ import https from "node:https";
20
20
  import http from "node:http";
21
21
  import "node:url";
22
22
  var __defProp = Object.defineProperty;
23
- var __esmMin = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
23
+ var __esmMin = (fn, res, err) => () => {
24
+ if (err) throw err[0];
25
+ try {
26
+ return fn && (res = fn(fn = 0)), res;
27
+ } catch (e) {
28
+ throw err = [e], e;
29
+ }
30
+ };
24
31
  var __exportAll = (all, no_symbols) => {
25
32
  let target = {};
26
33
  for (var name in all) __defProp(target, name, {
@@ -30,7 +37,6 @@ var __exportAll = (all, no_symbols) => {
30
37
  if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
31
38
  return target;
32
39
  };
33
- import.meta.url;
34
40
  //#endregion
35
41
  //#region src/config.ts
36
42
  /** Execution mode for the agent's sandbox backend. Mirrors the runtime enum. */
@@ -1,13 +1,31 @@
1
1
  import { type KiciBundle } from '@kici-dev/engine/provenance/bundle';
2
2
  import type { OidcTokenResult } from '@kici-dev/engine/protocol/messages/oidc-token-relay';
3
- import { type ProvenanceSubject } from './statement-builder.js';
3
+ import { type LocalBuildContext, type ProvenanceSubject } from './statement-builder.js';
4
+ /** The frozen, DSSE-signed attestation the agent reports for later fulfilment. */
5
+ export interface DeferredAttestationReport {
6
+ subjectName: string;
7
+ subjectDigest: string;
8
+ audience: string;
9
+ mediaType: string;
10
+ statementHash: string;
11
+ dsseEnvelope: KiciBundle['dsseEnvelope'];
12
+ publicKey: Record<string, unknown>;
13
+ }
4
14
  export interface AttestDeps {
5
- /** P1.4 relay: returns a KiCI ID token bound to the current job. */
15
+ /** P1.4 relay: returns a minted KiCI ID token or a transient `deferred` signal. */
6
16
  getIdToken: (opts: {
7
17
  audience: string;
8
18
  }) => Promise<OidcTokenResult>;
9
19
  /** Upload the serialized bundle; returns the storage key it was written to. */
10
20
  persist: (bundle: KiciBundle, subjectDigest: string) => Promise<string>;
21
+ /**
22
+ * Report a frozen, DSSE-signed statement for later minting (the transient
23
+ * mint-failure path). Required only when the relay may defer; the live path
24
+ * never calls it.
25
+ */
26
+ reportDeferred?: (report: DeferredAttestationReport) => Promise<void>;
27
+ /** Agent-local job facts used to freeze a statement when the mint defers. */
28
+ localContext?: LocalBuildContext;
11
29
  builderVersions: {
12
30
  'kici-agent': string;
13
31
  'kici-orchestrator': string;
@@ -19,11 +37,16 @@ export interface AttestInput {
19
37
  subject: ProvenanceSubject;
20
38
  audience?: string;
21
39
  }
22
- export interface AttestResult {
40
+ /** A minted-and-uploaded attestation, or a deferred one captured for later. */
41
+ export type AttestResult = {
23
42
  storageKey: string;
24
43
  bundle: KiciBundle;
25
44
  subjectDigest: string;
26
- }
45
+ } | {
46
+ deferred: true;
47
+ statementHash: string;
48
+ subjectDigest: string;
49
+ };
27
50
  export declare function attestProvenance(deps: AttestDeps, input: AttestInput): Promise<AttestResult>;
28
51
  /**
29
52
  * Pick the primary digest (`sha256` preferred) as the storage-key discriminator.
@@ -5,6 +5,7 @@
5
5
  * statement's identity equals the token's identity by construction.
6
6
  */
7
7
  import { type KiciProvenanceStatement } from '@kici-dev/engine/provenance/schema';
8
+ import type { SourceOrigin } from '@kici-dev/engine';
8
9
  /** The KiCI identity-token claims the builder reads (Platform server-truth). */
9
10
  export interface ProvenanceTokenClaims {
10
11
  iss: string;
@@ -15,6 +16,12 @@ export interface ProvenanceTokenClaims {
15
16
  kici_run_id: string;
16
17
  kici_job_id: string;
17
18
  orchestrator_id?: string | null;
19
+ /** Authoritative origin: the customer's public org id (Platform-asserted). */
20
+ org_id?: string;
21
+ /** Source-origin brand: triggered vs run-remote (local working-tree overlay). */
22
+ source_origin?: SourceOrigin;
23
+ /** Informational source provider (github / gitlab / bitbucket / local). */
24
+ provider?: string | null;
18
25
  }
19
26
  /** Caller-supplied artifact subject: a name plus a lowercase-hex digest map. */
20
27
  export interface ProvenanceSubject {
@@ -33,6 +40,49 @@ export interface BuildStatementInput {
33
40
  /** ISO-8601 timestamp with offset. */
34
41
  finishedOn: string;
35
42
  }
43
+ /**
44
+ * Agent-local job context used to freeze a provenance statement for a deferred
45
+ * attestation. When the Platform mint fails transiently there is no identity
46
+ * token to read claims from, so the statement is built from facts the agent
47
+ * already holds about the job it just ran. Only the identity token is deferred;
48
+ * these attested facts are sealed (DSSE-signed) at build time.
49
+ */
50
+ export interface LocalBuildContext {
51
+ repository: string;
52
+ ref: string;
53
+ sha: string | null;
54
+ workflowRef: string;
55
+ runId: string;
56
+ jobId: string;
57
+ orgId?: string;
58
+ sourceOrigin?: SourceOrigin;
59
+ /**
60
+ * Platform provenance issuer for the `builder.id`. The agent does not always
61
+ * know it at build time (the orchestrator may be disconnected — that is why
62
+ * the mint deferred), so it is best-effort; an empty string yields a
63
+ * `/orchestrator/unknown` builder id. This field is not verification
64
+ * load-bearing for a deferred bundle: the later token binds to the frozen
65
+ * statement by hash, not by field-for-field cross-check.
66
+ */
67
+ issuer: string;
68
+ }
69
+ /**
70
+ * Build a frozen SLSA v1.0 provenance statement from agent-local job context,
71
+ * for a deferred attestation (no minted identity token yet). Marks
72
+ * `attestationOrigin: 'deferred'` in the internal parameters. The caller
73
+ * DSSE-signs the returned statement immediately and computes its statement hash
74
+ * — the binding the later OIDC mint commits to (truth-contract property 2).
75
+ */
76
+ export declare function buildLocalProvenanceStatement(input: {
77
+ context: LocalBuildContext;
78
+ subject: ProvenanceSubject;
79
+ builderVersions: {
80
+ 'kici-agent': string;
81
+ 'kici-orchestrator': string;
82
+ };
83
+ startedOn: string;
84
+ finishedOn: string;
85
+ }): KiciProvenanceStatement;
36
86
  /** Build a KiCI SLSA v1.0 provenance statement (validates against the P1.1 schema). */
37
87
  export declare function buildProvenanceStatement(input: BuildStatementInput): KiciProvenanceStatement;
38
88
  //# sourceMappingURL=statement-builder.d.ts.map
package/dist/server.js CHANGED
@@ -15,7 +15,7 @@ import { LOGGER_ENV_VARS, defineEnv, validateUnknownKiciVars } from "@kici-dev/s
15
15
  import { ALLOWED_SYSTEM_VARS, CacheRunEventType, CacheStepType, ExecutionJobStatus, ExecutionStepStatus, InitFailureCategory, KNOWN_ROLES, PROTOCOL_VERSION, SANDBOX_DEFAULT_VARS, WS_CLOSE_AGENT_AUTH_FAILED, WS_MAX_PAYLOAD_BYTES, applyIncludeExclude, deriveOsArchLabels, expandMatrix, heartbeatSchema, hostLabel, mergeAutoLabels, orchestratorToAgentMessageSchema, parseHostPropertyAssignments, resolveRoleLabels, resolveWhenToRunOn, validateNoReservedLabels } from "@kici-dev/engine";
16
16
  import { execFile, execFileSync, execSync, fork, spawn } from "node:child_process";
17
17
  import WebSocket from "ws";
18
- import * as fs$2 from "node:fs";
18
+ import * as fs$1 from "node:fs";
19
19
  import fs, { existsSync } from "node:fs";
20
20
  import { ZipArchive } from "archiver";
21
21
  import path, { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
@@ -23,7 +23,7 @@ import { fileURLToPath, pathToFileURL } from "node:url";
23
23
  import { AsyncLocalStorage } from "node:async_hooks";
24
24
  import { format, promisify } from "node:util";
25
25
  import { gcStaleTmpDirs } from "@kici-dev/core/tmp-gc";
26
- import fs$1, { access, lstat, mkdir, mkdtemp, readFile, readdir, realpath, rm, unlink, writeFile } from "node:fs/promises";
26
+ import fsPromises, { access, lstat, mkdir, mkdtemp, readFile, readdir, realpath, rm, unlink, writeFile } from "node:fs/promises";
27
27
  import Docker from "dockerode";
28
28
  import { buildKiciApi, buildNeedsContext, isDynamicGroupRef, isDynamicJobFn, isParallelGroup, isStaticArray, isStaticObject } from "@kici-dev/sdk";
29
29
  import { c, x } from "tar";
@@ -36,7 +36,14 @@ import { PNPM_IGNORE_BUILD_GATE_ARG, PackageManager, YarnFlavor, detectPackageMa
36
36
  import { parse, stringify } from "yaml";
37
37
  import { createInterface } from "node:readline";
38
38
  var __defProp = Object.defineProperty;
39
- var __esmMin = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
39
+ var __esmMin = (fn, res, err) => () => {
40
+ if (err) throw err[0];
41
+ try {
42
+ return fn && (res = fn(fn = 0)), res;
43
+ } catch (e) {
44
+ throw err = [e], e;
45
+ }
46
+ };
40
47
  var __exportAll = (all, no_symbols) => {
41
48
  let target = {};
42
49
  for (var name in all) __defProp(target, name, {
@@ -46,7 +53,6 @@ var __exportAll = (all, no_symbols) => {
46
53
  if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
47
54
  return target;
48
55
  };
49
- import.meta.url;
50
56
  //#endregion
51
57
  //#region src/config.ts
52
58
  /** Execution mode for the agent's sandbox backend. Mirrors the runtime enum. */
@@ -222,7 +228,7 @@ async function buildAgentMiniBundle(opts) {
222
228
  uptime: os$1.uptime()
223
229
  }, null, 2), { name: "system/info.json" });
224
230
  if (opts.metricsText) archive.append(opts.metricsText, { name: "system/metrics.txt" });
225
- if (opts.logDir && fs$2.existsSync(opts.logDir)) await addLogsToArchive(archive, opts.logDir, opts.logWindowHours);
231
+ if (opts.logDir && fs$1.existsSync(opts.logDir)) await addLogsToArchive(archive, opts.logDir, opts.logWindowHours);
226
232
  await archive.finalize();
227
233
  await done;
228
234
  return Buffer.concat(chunks);
@@ -722,6 +728,13 @@ var OrchestratorClient = class OrchestratorClient {
722
728
  requestId: request.requestId
723
729
  };
724
730
  }
731
+ if (request.op === "defer") {
732
+ this.sendProvenanceUploadDefer(jobId, request);
733
+ return {
734
+ type: "provenance.response",
735
+ requestId: request.requestId
736
+ };
737
+ }
725
738
  const uploadUrl = await this.requestProvenanceUploadUrl(jobId, request.subjectDigest);
726
739
  return {
727
740
  type: "provenance.response",
@@ -730,6 +743,25 @@ var OrchestratorClient = class OrchestratorClient {
730
743
  };
731
744
  }
732
745
  /**
746
+ * Capture a frozen, DSSE-signed attestation for later minting (transient
747
+ * mint-failure path). Fire-and-forget: the orchestrator persists it into the
748
+ * deferred-attestation outbox and the job stays green.
749
+ */
750
+ sendProvenanceUploadDefer(jobId, request) {
751
+ this.sendDirect({
752
+ type: "provenance.upload.defer",
753
+ messageId: randomUUID(),
754
+ jobId,
755
+ subjectName: request.subjectName,
756
+ subjectDigest: request.subjectDigest,
757
+ audience: request.audience,
758
+ mediaType: request.mediaType,
759
+ statementHash: request.statementHash,
760
+ dsseEnvelope: request.dsseEnvelope,
761
+ publicKey: request.publicKey
762
+ });
763
+ }
764
+ /**
733
765
  * Relay a step-level approval request to the orchestrator. Sends a
734
766
  * `step.approval-request` WS message and resolves with the orchestrator's
735
767
  * `step.approval-resolved` mapped onto the IPC response shape. No client-side
@@ -1310,14 +1342,14 @@ var init_console_capture = __esmMin((() => {
1310
1342
  init_console_capture();
1311
1343
  function safe(name, fallback = "unknown") {
1312
1344
  switch (name) {
1313
- case "version": return "0.1.24";
1314
- case "buildCommit": return "73592f67f";
1315
- case "sdkVersion": return "0.1.24";
1316
- case "sdkBundleHash": return "031712cf8cd02f483365113aad7e946da982110d544b177eb05edcc0d4421253";
1317
- case "sharedVersion": return "0.1.24";
1318
- case "sharedBundleHash": return "b977224129c767c4851458a795fa470264b2fc51255baf06cf14640e29e2f44c";
1319
- case "engineVersion": return "0.1.24";
1320
- case "engineBundleHash": return "734acca885cd70eed07a1a9426b08c04c07a9bf99484c18100d3d797b3eb8f39";
1345
+ case "version": return "0.1.25";
1346
+ case "buildCommit": return "15e8e4155";
1347
+ case "sdkVersion": return "0.1.25";
1348
+ case "sdkBundleHash": return "e06bd1acf349ba365bdb357f3d865e0cbb4ad4604a0270b707a681a4f440954e";
1349
+ case "sharedVersion": return "0.1.25";
1350
+ case "sharedBundleHash": return "2394db0d8560b2cebf220c0e2d8993c75083aaf70a5917c35099b24feb3a22ba";
1351
+ case "engineVersion": return "0.1.25";
1352
+ case "engineBundleHash": return "c3320e812b8593d692f3fbf5eafe83507270028f7a1174c607881105dd702654";
1321
1353
  default: return fallback;
1322
1354
  }
1323
1355
  }
@@ -1951,7 +1983,7 @@ async function buildAssetDigestFromResolvedPaths(workDir, resolvedPaths) {
1951
1983
  for (const rel of resolvedPaths) {
1952
1984
  const abs = path.join(workDir, rel);
1953
1985
  try {
1954
- const content = await fs$1.readFile(abs, "utf-8");
1986
+ const content = await fsPromises.readFile(abs, "utf-8");
1955
1987
  parts.push(`${rel}\n${content}`);
1956
1988
  } catch {
1957
1989
  parts.push(`${rel}\n`);
@@ -1976,7 +2008,7 @@ async function loadWorkflowSource(workDir, sourceFile, expectedContentHash, reso
1976
2008
  ensureLoaderHookRegistered();
1977
2009
  const filePath = path.join(workDir, sourceFile);
1978
2010
  if (expectedContentHash) {
1979
- const rawSource = await fs$1.readFile(filePath, "utf-8");
2011
+ const rawSource = await fsPromises.readFile(filePath, "utf-8");
1980
2012
  let assetDigest;
1981
2013
  if (resolvedHashFiles?.length) assetDigest = await buildAssetDigestFromResolvedPaths(workDir, resolvedHashFiles);
1982
2014
  const actualHash = computeContentHash(rawSource, assetDigest);
@@ -2080,8 +2112,8 @@ async function extractStepsFromDynamicJob(workflow, dynamicIndex, jobName, event
2080
2112
  }
2081
2113
  var AGENT_SDK_VERSION, AGENT_SDK_BUNDLE_HASH, hookRegistered;
2082
2114
  var init_workflow_loader = __esmMin((() => {
2083
- AGENT_SDK_VERSION = "0.1.24";
2084
- AGENT_SDK_BUNDLE_HASH = "031712cf8cd02f483365113aad7e946da982110d544b177eb05edcc0d4421253";
2115
+ AGENT_SDK_VERSION = "0.1.25";
2116
+ AGENT_SDK_BUNDLE_HASH = "e06bd1acf349ba365bdb357f3d865e0cbb4ad4604a0270b707a681a4f440954e";
2085
2117
  hookRegistered = false;
2086
2118
  }));
2087
2119
  //#endregion
@@ -2247,24 +2279,24 @@ function resolveOrchestratorUrl(url) {
2247
2279
  * have nothing to race; the defensive `rm` covers re-runs.
2248
2280
  */
2249
2281
  async function moveScratchIntoRepo(scratchDir, workDir) {
2250
- for (const child of await fs$1.readdir(scratchDir)) if (child === ".kici") {
2282
+ for (const child of await fsPromises.readdir(scratchDir)) if (child === ".kici") {
2251
2283
  const kiciScratch = join(scratchDir, ".kici");
2252
- for (const sub of await fs$1.readdir(kiciScratch)) await moveInto(join(kiciScratch, sub), join(workDir, ".kici", sub));
2284
+ for (const sub of await fsPromises.readdir(kiciScratch)) await moveInto(join(kiciScratch, sub), join(workDir, ".kici", sub));
2253
2285
  } else await moveInto(join(scratchDir, child), join(workDir, child));
2254
2286
  }
2255
2287
  /** Move `src` to `dest`, creating the parent and clearing any stale dest. */
2256
2288
  async function moveInto(src, dest) {
2257
2289
  await mkdir(dirname(dest), { recursive: true });
2258
- await fs$1.rm(dest, {
2290
+ await fsPromises.rm(dest, {
2259
2291
  recursive: true,
2260
2292
  force: true
2261
2293
  });
2262
- await fs$1.rename(src, dest);
2294
+ await fsPromises.rename(src, dest);
2263
2295
  }
2264
2296
  /** Best-effort cleanup of a settled scratch dir; logs and continues on failure. */
2265
2297
  async function cleanupScratch(scratchDir) {
2266
2298
  try {
2267
- await fs$1.rm(scratchDir, {
2299
+ await fsPromises.rm(scratchDir, {
2268
2300
  recursive: true,
2269
2301
  force: true
2270
2302
  });
@@ -2298,7 +2330,7 @@ async function restoreDeps(workDir, depsUrl, depsHash) {
2298
2330
  const kiciDir = join(workDir, ".kici");
2299
2331
  if (depsUrl.startsWith("file://")) {
2300
2332
  const localPath = fileURLToPath(depsUrl);
2301
- const data = await fs$1.readFile(localPath);
2333
+ const data = await fsPromises.readFile(localPath);
2302
2334
  if (depsHash) {
2303
2335
  const actualHash = computeHash(data);
2304
2336
  if (actualHash !== depsHash) throw new Error(`Dep tarball hash mismatch: expected ${depsHash}, got ${actualHash}`);
@@ -2457,7 +2489,7 @@ async function restoreSource(workDir, sourceTarUrl) {
2457
2489
  let data;
2458
2490
  if (sourceTarUrl.startsWith("file://")) {
2459
2491
  const localPath = fileURLToPath(sourceTarUrl);
2460
- data = await fs$1.readFile(localPath);
2492
+ data = await fsPromises.readFile(localPath);
2461
2493
  } else if (sourceTarUrl.startsWith("http://") || sourceTarUrl.startsWith("https://")) data = await downloadUrl(sourceTarUrl);
2462
2494
  else throw new Error(`Unsupported source tarball URL scheme: ${sourceTarUrl}`);
2463
2495
  await extractSourceTarball(data, workDir);
@@ -2665,37 +2697,53 @@ async function sshPush(reach, privateKey, localBytes, remotePath, opts = {}, dep
2665
2697
  /**
2666
2698
  * Start a per-call ephemeral ssh-agent, load the key via stdin (never a file),
2667
2699
  * run `body` with `SSH_AUTH_SOCK` in env, and kill the agent in `finally`.
2700
+ *
2701
+ * The agent is bound to a socket inside a private `kici-bootstrap-ssh-*`
2702
+ * directory (`ssh-agent -a <dir>/agent.sock`) rather than the default
2703
+ * `/tmp/ssh-XXXX`. `ssh-agent` daemonizes into its own session, so it does NOT
2704
+ * die with this process — a SIGKILL of the agent (routine when an ephemeral
2705
+ * bring-up runner is torn down) skips the `finally` and orphans the daemon.
2706
+ * The namespaced socket path is how `kici-leak-sweep` reaps such orphans
2707
+ * precisely: it can distinguish a KiCI bring-up agent from an operator's login
2708
+ * agent, which a bare `/tmp/ssh-XXXX` socket cannot. The private dir is removed
2709
+ * in an outer `finally` so the normal path leaves nothing behind.
2668
2710
  */
2669
2711
  async function withEphemeralAgent(privateKey, spawnFn, body) {
2670
2712
  const baseEnv = { ...process.env };
2671
- const start = await spawnFn("ssh-agent", ["-s"], { env: baseEnv });
2672
- if (start.exitCode !== 0) throw new Error(`ssh-agent start failed: exit ${start.exitCode}\n${start.stderr}`);
2673
- const sock = parseAgentSocket(start.stdout);
2674
- const pid = parseAgentPid(start.stdout);
2675
- const agentEnv = {
2676
- ...baseEnv,
2677
- SSH_AUTH_SOCK: sock,
2678
- ...pid ? { SSH_AGENT_PID: pid } : {},
2679
- SSH_ASKPASS: "/bin/false",
2680
- DISPLAY: ""
2681
- };
2713
+ const agentDir = await mkdtemp(join(tmpdir(), "kici-bootstrap-ssh-"));
2714
+ const sock = join(agentDir, "agent.sock");
2682
2715
  try {
2683
- const add = await spawnFn("ssh-add", ["-"], {
2684
- env: agentEnv,
2685
- stdin: privateKey.endsWith("\n") ? privateKey : `${privateKey}\n`
2686
- });
2687
- if (add.exitCode !== 0) throw new Error(`ssh-add failed: exit ${add.exitCode}\n${add.stderr}`);
2688
- return await body(agentEnv);
2716
+ const start = await spawnFn("ssh-agent", [
2717
+ "-a",
2718
+ sock,
2719
+ "-s"
2720
+ ], { env: baseEnv });
2721
+ if (start.exitCode !== 0) throw new Error(`ssh-agent start failed: exit ${start.exitCode}\n${start.stderr}`);
2722
+ const pid = parseAgentPid(start.stdout);
2723
+ const agentEnv = {
2724
+ ...baseEnv,
2725
+ SSH_AUTH_SOCK: sock,
2726
+ ...pid ? { SSH_AGENT_PID: pid } : {},
2727
+ SSH_ASKPASS: "/bin/false",
2728
+ DISPLAY: ""
2729
+ };
2730
+ try {
2731
+ const add = await spawnFn("ssh-add", ["-"], {
2732
+ env: agentEnv,
2733
+ stdin: privateKey.endsWith("\n") ? privateKey : `${privateKey}\n`
2734
+ });
2735
+ if (add.exitCode !== 0) throw new Error(`ssh-add failed: exit ${add.exitCode}\n${add.stderr}`);
2736
+ return await body(agentEnv);
2737
+ } finally {
2738
+ await spawnFn("ssh-agent", ["-k"], { env: agentEnv }).catch(() => {});
2739
+ }
2689
2740
  } finally {
2690
- await spawnFn("ssh-agent", ["-k"], { env: agentEnv }).catch(() => {});
2741
+ await rm(agentDir, {
2742
+ recursive: true,
2743
+ force: true
2744
+ }).catch(() => {});
2691
2745
  }
2692
2746
  }
2693
- /** Extract `SSH_AUTH_SOCK=<path>;` from `ssh-agent -s` output. */
2694
- function parseAgentSocket(out) {
2695
- const m = out.match(/SSH_AUTH_SOCK=([^;\n]+)/);
2696
- if (!m) throw new Error("ssh-agent -s did not emit SSH_AUTH_SOCK");
2697
- return m[1];
2698
- }
2699
2747
  /** Extract `SSH_AGENT_PID=<n>;` from `ssh-agent -s` output (best-effort). */
2700
2748
  function parseAgentPid(out) {
2701
2749
  return out.match(/SSH_AGENT_PID=([^;\n]+)/)?.[1];
@@ -2921,7 +2969,7 @@ async function serializeJob(job, generatedNames, ctx, staticNames, allowedGroups
2921
2969
  */
2922
2970
  function resolveNeeds(needs, generatedNames, staticNames, allowedGroups) {
2923
2971
  if (!needs || needs.length === 0) return [];
2924
- const allNames = new Set([...generatedNames, ...staticNames]);
2972
+ const allNames = /* @__PURE__ */ new Set([...generatedNames, ...staticNames]);
2925
2973
  return needs.map((dep) => {
2926
2974
  if (typeof dep === "string") {
2927
2975
  if (!allNames.has(dep)) throw new Error(`Job dependency '${dep}' not found in workflow jobs (checked: ${generatedNames.size} generated, ${staticNames.size} static)`);
@@ -3318,7 +3366,7 @@ function decryptBuffer(encrypted, aesKey) {
3318
3366
  */
3319
3367
  async function applyOverlay(config) {
3320
3368
  const { tarballUrl, cliPublicKey, orchestratorPrivateKey, repoDir } = config;
3321
- const tmpDir = await fs$1.mkdtemp(path.join(os.tmpdir(), "kici-overlay-"));
3369
+ const tmpDir = await fsPromises.mkdtemp(path.join(os.tmpdir(), "kici-overlay-"));
3322
3370
  try {
3323
3371
  logger$7.info("Downloading overlay tarball", { url: tarballUrl.replace(/\?.*$/, "?[redacted]") });
3324
3372
  let encryptedData;
@@ -3332,7 +3380,7 @@ async function applyOverlay(config) {
3332
3380
  const decryptedData = decryptBuffer(encryptedData, aesKey);
3333
3381
  logger$7.info("Extracting overlay tarball", { size: decryptedData.length });
3334
3382
  const extractDir = path.join(tmpDir, "extracted");
3335
- await fs$1.mkdir(extractDir, { recursive: true });
3383
+ await fsPromises.mkdir(extractDir, { recursive: true });
3336
3384
  try {
3337
3385
  const readable = Readable.from(decryptedData);
3338
3386
  await new Promise((resolve, reject) => {
@@ -3347,7 +3395,7 @@ async function applyOverlay(config) {
3347
3395
  const manifestPath = path.join(extractDir, ".kici-overlay-tmp", "manifest.json");
3348
3396
  let manifestContent;
3349
3397
  try {
3350
- manifestContent = await fs$1.readFile(manifestPath, "utf-8");
3398
+ manifestContent = await fsPromises.readFile(manifestPath, "utf-8");
3351
3399
  } catch {
3352
3400
  throw new Error("Overlay manifest not found: expected .kici-overlay-tmp/manifest.json in tarball");
3353
3401
  }
@@ -3368,15 +3416,15 @@ async function applyOverlay(config) {
3368
3416
  for (const file of checksumFiles) {
3369
3417
  const srcPath = path.join(extractDir, file);
3370
3418
  const destPath = path.join(repoDir, file);
3371
- await fs$1.mkdir(path.dirname(destPath), { recursive: true });
3372
- await fs$1.copyFile(srcPath, destPath);
3419
+ await fsPromises.mkdir(path.dirname(destPath), { recursive: true });
3420
+ await fsPromises.copyFile(srcPath, destPath);
3373
3421
  filesApplied++;
3374
3422
  }
3375
3423
  let filesDeleted = 0;
3376
3424
  for (const file of manifest.deletions) {
3377
3425
  const targetPath = path.join(repoDir, file);
3378
3426
  try {
3379
- await fs$1.unlink(targetPath);
3427
+ await fsPromises.unlink(targetPath);
3380
3428
  filesDeleted++;
3381
3429
  } catch {
3382
3430
  logger$7.debug("Deletion target not found, skipping", { file });
@@ -3392,7 +3440,7 @@ async function applyOverlay(config) {
3392
3440
  verified: true
3393
3441
  };
3394
3442
  } finally {
3395
- await fs$1.rm(tmpDir, {
3443
+ await fsPromises.rm(tmpDir, {
3396
3444
  recursive: true,
3397
3445
  force: true
3398
3446
  }).catch(() => {});
@@ -5814,7 +5862,7 @@ var job_runner_exports = /* @__PURE__ */ __exportAll({
5814
5862
  */
5815
5863
  async function fileExists(p) {
5816
5864
  try {
5817
- await fs$1.access(p);
5865
+ await fsPromises.access(p);
5818
5866
  return true;
5819
5867
  } catch {
5820
5868
  return false;
@@ -5930,11 +5978,11 @@ var init_job_runner = __esmMin((() => {
5930
5978
  async execute(dispatch) {
5931
5979
  const { runId: _runId, jobId, jobConfig: _jobConfig } = dispatch;
5932
5980
  const abortController = new AbortController();
5933
- const workDir = await fs$1.mkdtemp(join(tmpdir(), "kici-"));
5981
+ const workDir = await fsPromises.mkdtemp(join(tmpdir(), "kici-"));
5934
5982
  const completionPromise = this.runJob(dispatch, workDir, abortController).finally(async () => {
5935
5983
  this.activeJobs.delete(jobId);
5936
5984
  this.activeSandbox = null;
5937
- await fs$1.rm(workDir, {
5985
+ await fsPromises.rm(workDir, {
5938
5986
  recursive: true,
5939
5987
  force: true
5940
5988
  }).catch(() => {});
@@ -6896,14 +6944,14 @@ var init_job_runner = __esmMin((() => {
6896
6944
  */
6897
6945
  init_console_capture();
6898
6946
  init_npm_resolver();
6899
- const AGENT_VERSION = "0.1.24";
6900
- const BUILD_COMMIT = "73592f67f";
6901
- const SDK_VERSION = "0.1.24";
6902
- const SDK_BUNDLE_HASH = "031712cf8cd02f483365113aad7e946da982110d544b177eb05edcc0d4421253";
6903
- const SHARED_VERSION = "0.1.24";
6904
- const SHARED_BUNDLE_HASH = "b977224129c767c4851458a795fa470264b2fc51255baf06cf14640e29e2f44c";
6905
- const ENGINE_VERSION = "0.1.24";
6906
- const ENGINE_BUNDLE_HASH = "734acca885cd70eed07a1a9426b08c04c07a9bf99484c18100d3d797b3eb8f39";
6947
+ const AGENT_VERSION = "0.1.25";
6948
+ const BUILD_COMMIT = "15e8e4155";
6949
+ const SDK_VERSION = "0.1.25";
6950
+ const SDK_BUNDLE_HASH = "e06bd1acf349ba365bdb357f3d865e0cbb4ad4604a0270b707a681a4f440954e";
6951
+ const SHARED_VERSION = "0.1.25";
6952
+ const SHARED_BUNDLE_HASH = "2394db0d8560b2cebf220c0e2d8993c75083aaf70a5917c35099b24feb3a22ba";
6953
+ const ENGINE_VERSION = "0.1.25";
6954
+ const ENGINE_BUNDLE_HASH = "c3320e812b8593d692f3fbf5eafe83507270028f7a1174c607881105dd702654";
6907
6955
  initTelemetry({
6908
6956
  serviceName: "kici-agent",
6909
6957
  otlpEndpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT
@@ -13,6 +13,7 @@ import { OIDC_TOKEN_REQUEST_METHOD } from "@kici-dev/engine/protocol/messages/oi
13
13
  import { computeBackoffDelay, sha256File as sha256File$1 } from "@kici-dev/core";
14
14
  import { calculateJwkThumbprint, decodeJwt, exportJWK, generateKeyPair } from "jose";
15
15
  import { IN_TOTO_PAYLOAD_TYPE, KICI_PROVENANCE_AUDIENCE, KICI_PROVENANCE_BUNDLE_MEDIA_TYPE } from "@kici-dev/engine/provenance/bundle";
16
+ import { computeStatementHash } from "@kici-dev/engine/provenance/statement-hash";
16
17
  import { IN_TOTO_STATEMENT_TYPE, KICI_WORKFLOW_BUILD_TYPE, SLSA_PROVENANCE_PREDICATE_TYPE } from "@kici-dev/engine/provenance/schema";
17
18
  import { buildDsseEnvelope, dssePae } from "@kici-dev/engine/provenance/dsse";
18
19
  import https from "node:https";
@@ -29,7 +30,14 @@ import { promisify } from "node:util";
29
30
  import { PNPM_IGNORE_BUILD_GATE_ARG, PackageManager, YarnFlavor, detectPackageManagerFromManifests, detectYarnFlavor } from "@kici-dev/shared/package-manager";
30
31
  import { parse, stringify } from "yaml";
31
32
  var __defProp = Object.defineProperty;
32
- var __esmMin = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
33
+ var __esmMin = (fn, res, err) => () => {
34
+ if (err) throw err[0];
35
+ try {
36
+ return fn && (res = fn(fn = 0)), res;
37
+ } catch (e) {
38
+ throw err = [e], e;
39
+ }
40
+ };
33
41
  var __exportAll = (all, no_symbols) => {
34
42
  let target = {};
35
43
  for (var name in all) __defProp(target, name, {
@@ -39,7 +47,6 @@ var __exportAll = (all, no_symbols) => {
39
47
  if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
40
48
  return target;
41
49
  };
42
- import.meta.url;
43
50
  //#endregion
44
51
  //#region src/provenance/statement-builder.ts
45
52
  /**
@@ -48,6 +55,53 @@ import.meta.url;
48
55
  * entirely from the JWT claims (Platform-minted, unforgeable), so the
49
56
  * statement's identity equals the token's identity by construction.
50
57
  */
58
+ /**
59
+ * Build a frozen SLSA v1.0 provenance statement from agent-local job context,
60
+ * for a deferred attestation (no minted identity token yet). Marks
61
+ * `attestationOrigin: 'deferred'` in the internal parameters. The caller
62
+ * DSSE-signs the returned statement immediately and computes its statement hash
63
+ * — the binding the later OIDC mint commits to (truth-contract property 2).
64
+ */
65
+ function buildLocalProvenanceStatement(input) {
66
+ const c = input.context;
67
+ return {
68
+ _type: IN_TOTO_STATEMENT_TYPE,
69
+ subject: [{
70
+ name: input.subject.name,
71
+ digest: input.subject.digest
72
+ }],
73
+ predicateType: SLSA_PROVENANCE_PREDICATE_TYPE,
74
+ predicate: {
75
+ buildDefinition: {
76
+ buildType: KICI_WORKFLOW_BUILD_TYPE,
77
+ externalParameters: { workflow: {
78
+ repository: c.repository,
79
+ ref: c.ref,
80
+ path: c.workflowRef
81
+ } },
82
+ internalParameters: {
83
+ ...c.sha ? { commit: c.sha } : {},
84
+ runId: c.runId,
85
+ jobId: c.jobId,
86
+ ...c.orgId ? { orgId: c.orgId } : {},
87
+ ...c.sourceOrigin ? { sourceOrigin: c.sourceOrigin } : {},
88
+ attestationOrigin: "deferred"
89
+ }
90
+ },
91
+ runDetails: {
92
+ builder: {
93
+ id: `${c.issuer}/orchestrator/unknown`,
94
+ version: input.builderVersions
95
+ },
96
+ metadata: {
97
+ invocationId: c.runId,
98
+ startedOn: input.startedOn,
99
+ finishedOn: input.finishedOn
100
+ }
101
+ }
102
+ }
103
+ };
104
+ }
51
105
  /** Build a KiCI SLSA v1.0 provenance statement (validates against the P1.1 schema). */
52
106
  function buildProvenanceStatement(input) {
53
107
  const c = input.tokenClaims;
@@ -61,15 +115,20 @@ function buildProvenanceStatement(input) {
61
115
  predicate: {
62
116
  buildDefinition: {
63
117
  buildType: KICI_WORKFLOW_BUILD_TYPE,
64
- externalParameters: { workflow: {
65
- repository: c.repository ?? "",
66
- ref: c.ref ?? "",
67
- path: c.workflow_ref ?? ""
68
- } },
118
+ externalParameters: {
119
+ workflow: {
120
+ repository: c.repository ?? "",
121
+ ref: c.ref ?? "",
122
+ path: c.workflow_ref ?? ""
123
+ },
124
+ ...c.provider ? { provider: c.provider } : {}
125
+ },
69
126
  internalParameters: {
70
127
  ...c.sha ? { commit: c.sha } : {},
71
128
  runId: c.kici_run_id,
72
- jobId: c.kici_job_id
129
+ jobId: c.kici_job_id,
130
+ ...c.org_id ? { orgId: c.org_id } : {},
131
+ ...c.source_origin ? { sourceOrigin: c.source_origin } : {}
73
132
  }
74
133
  },
75
134
  runDetails: {
@@ -127,7 +186,9 @@ async function signStatementDsse(payloadType, statementBytes) {
127
186
  async function attestProvenance(deps, input) {
128
187
  const audience = input.audience ?? KICI_PROVENANCE_AUDIENCE;
129
188
  const subjectDigest = subjectDigestString(input.subject);
130
- const { token } = await deps.getIdToken({ audience });
189
+ const tokenResult = await deps.getIdToken({ audience });
190
+ if ("deferred" in tokenResult) return deferAttestation(deps, input, subjectDigest, audience);
191
+ const { token } = tokenResult;
131
192
  const claims = decodeJwt(token);
132
193
  const now = (deps.now ?? (() => (/* @__PURE__ */ new Date()).toISOString()))();
133
194
  const statement = buildProvenanceStatement({
@@ -153,6 +214,39 @@ async function attestProvenance(deps, input) {
153
214
  };
154
215
  }
155
216
  /**
217
+ * Freeze + DSSE-sign the provenance statement from agent-local job facts and
218
+ * report it for later minting. The step does NOT throw — the job completes
219
+ * green and the attestation surfaces as `deferred`.
220
+ */
221
+ async function deferAttestation(deps, input, subjectDigest, audience) {
222
+ if (!deps.reportDeferred || !deps.localContext) throw new Error("provenance mint deferred but no reportDeferred/localContext wired to capture it");
223
+ const now = (deps.now ?? (() => (/* @__PURE__ */ new Date()).toISOString()))();
224
+ const statement = buildLocalProvenanceStatement({
225
+ context: deps.localContext,
226
+ subject: input.subject,
227
+ builderVersions: deps.builderVersions,
228
+ startedOn: now,
229
+ finishedOn: now
230
+ });
231
+ const statementBytes = new TextEncoder().encode(JSON.stringify(statement));
232
+ const { envelope, publicJwk } = await signStatementDsse(IN_TOTO_PAYLOAD_TYPE, statementBytes);
233
+ const statementHash = await computeStatementHash(statementBytes);
234
+ await deps.reportDeferred({
235
+ subjectName: input.subject.name,
236
+ subjectDigest,
237
+ audience,
238
+ mediaType: KICI_PROVENANCE_BUNDLE_MEDIA_TYPE,
239
+ statementHash,
240
+ dsseEnvelope: envelope,
241
+ publicKey: publicJwk
242
+ });
243
+ return {
244
+ deferred: true,
245
+ statementHash,
246
+ subjectDigest
247
+ };
248
+ }
249
+ /**
156
250
  * Pick the primary digest (`sha256` preferred) as the storage-key discriminator.
157
251
  * Throws when the subject carries no digest: an empty digest set would otherwise
158
252
  * yield an `undefined` storage-key segment (`provenance/<run>/<job>/undefined.kici.json`)
@@ -3399,8 +3493,8 @@ function logSubprocessStreams(e, tokens) {
3399
3493
  * no Rolldown step at runtime. `@kici-dev/sdk` and host-repo deps resolve via
3400
3494
  * Node's normal ESM lookup against `.kici/node_modules/`.
3401
3495
  */
3402
- const AGENT_SDK_VERSION = "0.1.24";
3403
- const AGENT_SDK_BUNDLE_HASH = "031712cf8cd02f483365113aad7e946da982110d544b177eb05edcc0d4421253";
3496
+ const AGENT_SDK_VERSION = "0.1.25";
3497
+ const AGENT_SDK_BUNDLE_HASH = "e06bd1acf349ba365bdb357f3d865e0cbb4ad4604a0270b707a681a4f440954e";
3404
3498
  /**
3405
3499
  * Register the `@kici-dev/core/ts-loader-hook` oxc-transform ESM loader hook so
3406
3500
  * subsequent dynamic `import()` calls for `.ts` / `.tsx` files transform on the
@@ -3762,7 +3856,7 @@ async function applyOverlay(config) {
3762
3856
  */
3763
3857
  init_download();
3764
3858
  init_dep_restore();
3765
- const AGENT_VERSION = "0.1.24";
3859
+ const AGENT_VERSION = "0.1.25";
3766
3860
  process.on("uncaughtException", (err) => {
3767
3861
  process.stderr.write(`[workflow-runner] UNCAUGHT EXCEPTION: ${err.message}\n`);
3768
3862
  if (err.stack) process.stderr.write(`[workflow-runner] Stack: ${err.stack}\n`);
@@ -4269,9 +4363,20 @@ async function relayProvenanceIpc(request) {
4269
4363
  return response;
4270
4364
  }
4271
4365
  /**
4366
+ * Extract an `owner/repo` identifier from a git clone URL for a deferred
4367
+ * statement's `externalParameters.workflow.repository` (the live path reads this
4368
+ * from the minted token claim; the deferred path has no token yet).
4369
+ */
4370
+ function extractRepoIdentifier(repoUrl) {
4371
+ const match = repoUrl.match(/(?:github|gitlab|bitbucket)\.\w+\/([^/]+\/[^/.]+)/);
4372
+ return match ? match[1] : "unknown/unknown";
4373
+ }
4374
+ /**
4272
4375
  * Build the `ctx.attestProvenance` step helper. Resolves a `path` subject to a
4273
4376
  * SHA-256 digest, threads the identity token via the supplied OIDC getter, and
4274
- * persists the bundle over the IPC -> WS provenance-upload relay.
4377
+ * persists the bundle over the IPC -> WS provenance-upload relay. On a transient
4378
+ * mint failure the statement is frozen + reported for later minting (deferred);
4379
+ * the step still completes.
4275
4380
  */
4276
4381
  function buildAttestProvenanceFn(request, workDir, getIdToken) {
4277
4382
  return async (opts) => {
@@ -4288,6 +4393,27 @@ function buildAttestProvenanceFn(request, workDir, getIdToken) {
4288
4393
  "kici-agent": AGENT_VERSION,
4289
4394
  "kici-orchestrator": "unknown"
4290
4395
  },
4396
+ localContext: {
4397
+ repository: extractRepoIdentifier(request.repoUrl),
4398
+ ref: request.ref,
4399
+ sha: request.sha || null,
4400
+ workflowRef: request.workflowRef ?? request.workflowName,
4401
+ runId: request.runId,
4402
+ jobId: request.jobId,
4403
+ issuer: request.provenanceIssuer ?? ""
4404
+ },
4405
+ reportDeferred: async (report) => {
4406
+ await relayProvenanceIpc({
4407
+ op: "defer",
4408
+ subjectDigest: report.subjectDigest,
4409
+ subjectName: report.subjectName,
4410
+ mediaType: report.mediaType,
4411
+ audience: report.audience,
4412
+ statementHash: report.statementHash,
4413
+ dsseEnvelope: report.dsseEnvelope,
4414
+ publicKey: report.publicKey
4415
+ });
4416
+ },
4291
4417
  persist: async (bundle, subjectDigest) => {
4292
4418
  const urlResponse = await relayProvenanceIpc({
4293
4419
  op: "requestUploadUrl",
@@ -4307,6 +4433,11 @@ function buildAttestProvenanceFn(request, workDir, getIdToken) {
4307
4433
  subject,
4308
4434
  ...opts.audience !== void 0 && { audience: opts.audience }
4309
4435
  });
4436
+ if ("deferred" in result) return {
4437
+ deferred: true,
4438
+ subjectDigest: result.subjectDigest,
4439
+ statementHash: result.statementHash
4440
+ };
4310
4441
  return {
4311
4442
  storageKey: result.storageKey,
4312
4443
  subjectDigest: result.subjectDigest,
@@ -250,6 +250,12 @@ export declare class OrchestratorClient {
250
250
  * returns the result on the IPC response shape.
251
251
  */
252
252
  relayProvenance(jobId: string, request: ProvenanceRequestIpc): Promise<ProvenanceResponseIpc>;
253
+ /**
254
+ * Capture a frozen, DSSE-signed attestation for later minting (transient
255
+ * mint-failure path). Fire-and-forget: the orchestrator persists it into the
256
+ * deferred-attestation outbox and the job stays green.
257
+ */
258
+ sendProvenanceUploadDefer(jobId: string, request: ProvenanceRequestIpc): void;
253
259
  /**
254
260
  * Relay a step-level approval request to the orchestrator. Sends a
255
261
  * `step.approval-request` WS message and resolves with the orchestrator's
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kici-dev/agent",
3
- "version": "0.1.24",
3
+ "version": "0.1.25",
4
4
  "description": "Customer-deployable agent for the KiCI CI/CD stack. Connects to an orchestrator, clones the workflow repo, executes steps, and streams logs back.",
5
5
  "keywords": [
6
6
  "ci",
@@ -64,10 +64,10 @@
64
64
  "yaml": "^2.9.0",
65
65
  "zod": "^4.4.3",
66
66
  "zx": "^8.8.5",
67
- "@kici-dev/engine": "0.1.24",
68
- "@kici-dev/shared": "0.1.24",
69
- "@kici-dev/core": "0.1.24",
70
- "@kici-dev/sdk": "0.1.24"
67
+ "@kici-dev/core": "0.1.25",
68
+ "@kici-dev/engine": "0.1.25",
69
+ "@kici-dev/sdk": "0.1.25",
70
+ "@kici-dev/shared": "0.1.25"
71
71
  },
72
72
  "kici": {
73
73
  "metrics": {
package/sbom.spdx.json CHANGED
@@ -2,10 +2,10 @@
2
2
  "spdxVersion": "SPDX-2.3",
3
3
  "dataLicense": "CC0-1.0",
4
4
  "SPDXID": "SPDXRef-DOCUMENT",
5
- "name": "@kici-dev/agent@0.1.24",
6
- "documentNamespace": "https://kici.dev/sbom/%40kici-dev%2Fagent/0.1.24/d46ec25f-cf71-414d-a647-8164545d1cff",
5
+ "name": "@kici-dev/agent@0.1.25",
6
+ "documentNamespace": "https://kici.dev/sbom/%40kici-dev%2Fagent/0.1.25/0c3ee967-8ee9-47da-bc53-89a597d05c47",
7
7
  "creationInfo": {
8
- "created": "2026-06-28T16:24:54Z",
8
+ "created": "2026-07-05T08:32:53Z",
9
9
  "creators": [
10
10
  "Tool: kici-sbom-generator"
11
11
  ]
@@ -816,7 +816,7 @@
816
816
  {
817
817
  "SPDXID": "SPDXRef-RootPackage",
818
818
  "name": "@kici-dev/agent",
819
- "versionInfo": "0.1.24",
819
+ "versionInfo": "0.1.25",
820
820
  "downloadLocation": "NOASSERTION",
821
821
  "filesAnalyzed": false,
822
822
  "licenseConcluded": "NOASSERTION",
@@ -827,16 +827,16 @@
827
827
  {
828
828
  "referenceCategory": "PACKAGE-MANAGER",
829
829
  "referenceType": "purl",
830
- "referenceLocator": "pkg:npm/%40kici-dev/agent@0.1.24"
830
+ "referenceLocator": "pkg:npm/%40kici-dev/agent@0.1.25"
831
831
  }
832
832
  ],
833
833
  "description": "Customer-deployable agent for the KiCI CI/CD stack. Connects to an orchestrator, clones the workflow repo, executes steps, and streams logs back.",
834
834
  "homepage": "https://kici.dev"
835
835
  },
836
836
  {
837
- "SPDXID": "SPDXRef-Package--kici-dev-core-0.1.24",
837
+ "SPDXID": "SPDXRef-Package--kici-dev-core-0.1.25",
838
838
  "name": "@kici-dev/core",
839
- "versionInfo": "0.1.24",
839
+ "versionInfo": "0.1.25",
840
840
  "downloadLocation": "NOASSERTION",
841
841
  "filesAnalyzed": false,
842
842
  "licenseConcluded": "NOASSERTION",
@@ -847,16 +847,16 @@
847
847
  {
848
848
  "referenceCategory": "PACKAGE-MANAGER",
849
849
  "referenceType": "purl",
850
- "referenceLocator": "pkg:npm/%40kici-dev/core@0.1.24"
850
+ "referenceLocator": "pkg:npm/%40kici-dev/core@0.1.25"
851
851
  }
852
852
  ],
853
853
  "description": "Light shared utilities for the KiCI stack (logging, errors, formatting, crypto, zx init, the TypeScript ESM loader hook). No server-side dependencies.",
854
854
  "homepage": "https://kici.dev"
855
855
  },
856
856
  {
857
- "SPDXID": "SPDXRef-Package--kici-dev-engine-0.1.24",
857
+ "SPDXID": "SPDXRef-Package--kici-dev-engine-0.1.25",
858
858
  "name": "@kici-dev/engine",
859
- "versionInfo": "0.1.24",
859
+ "versionInfo": "0.1.25",
860
860
  "downloadLocation": "NOASSERTION",
861
861
  "filesAnalyzed": false,
862
862
  "licenseConcluded": "NOASSERTION",
@@ -867,16 +867,16 @@
867
867
  {
868
868
  "referenceCategory": "PACKAGE-MANAGER",
869
869
  "referenceType": "purl",
870
- "referenceLocator": "pkg:npm/%40kici-dev/engine@0.1.24"
870
+ "referenceLocator": "pkg:npm/%40kici-dev/engine@0.1.25"
871
871
  }
872
872
  ],
873
873
  "description": "Shared business logic for the KiCI CI/CD stack: protocol, triggers, state machine, and provider interfaces used by the Platform relay, orchestrator, and compiler.",
874
874
  "homepage": "https://kici.dev"
875
875
  },
876
876
  {
877
- "SPDXID": "SPDXRef-Package--kici-dev-sdk-0.1.24",
877
+ "SPDXID": "SPDXRef-Package--kici-dev-sdk-0.1.25",
878
878
  "name": "@kici-dev/sdk",
879
- "versionInfo": "0.1.24",
879
+ "versionInfo": "0.1.25",
880
880
  "downloadLocation": "NOASSERTION",
881
881
  "filesAnalyzed": false,
882
882
  "licenseConcluded": "NOASSERTION",
@@ -887,16 +887,16 @@
887
887
  {
888
888
  "referenceCategory": "PACKAGE-MANAGER",
889
889
  "referenceType": "purl",
890
- "referenceLocator": "pkg:npm/%40kici-dev/sdk@0.1.24"
890
+ "referenceLocator": "pkg:npm/%40kici-dev/sdk@0.1.25"
891
891
  }
892
892
  ],
893
893
  "description": "TypeScript SDK for defining KiCI workflows. Import into `.kici/workflows/*.ts` to declare workflows, jobs, steps, triggers, rules, and matrix configurations.",
894
894
  "homepage": "https://kici.dev"
895
895
  },
896
896
  {
897
- "SPDXID": "SPDXRef-Package--kici-dev-shared-0.1.24",
897
+ "SPDXID": "SPDXRef-Package--kici-dev-shared-0.1.25",
898
898
  "name": "@kici-dev/shared",
899
- "versionInfo": "0.1.24",
899
+ "versionInfo": "0.1.25",
900
900
  "downloadLocation": "NOASSERTION",
901
901
  "filesAnalyzed": false,
902
902
  "licenseConcluded": "NOASSERTION",
@@ -907,7 +907,7 @@
907
907
  {
908
908
  "referenceCategory": "PACKAGE-MANAGER",
909
909
  "referenceType": "purl",
910
- "referenceLocator": "pkg:npm/%40kici-dev/shared@0.1.24"
910
+ "referenceLocator": "pkg:npm/%40kici-dev/shared@0.1.25"
911
911
  }
912
912
  ],
913
913
  "description": "Shared utilities for the KiCI CI/CD stack — logging, zx setup, crypto, telemetry, health and metrics routes. No business logic.",
@@ -6209,22 +6209,22 @@
6209
6209
  },
6210
6210
  {
6211
6211
  "spdxElementId": "SPDXRef-RootPackage",
6212
- "relatedSpdxElement": "SPDXRef-Package--kici-dev-core-0.1.24",
6212
+ "relatedSpdxElement": "SPDXRef-Package--kici-dev-core-0.1.25",
6213
6213
  "relationshipType": "DEPENDS_ON"
6214
6214
  },
6215
6215
  {
6216
6216
  "spdxElementId": "SPDXRef-RootPackage",
6217
- "relatedSpdxElement": "SPDXRef-Package--kici-dev-engine-0.1.24",
6217
+ "relatedSpdxElement": "SPDXRef-Package--kici-dev-engine-0.1.25",
6218
6218
  "relationshipType": "DEPENDS_ON"
6219
6219
  },
6220
6220
  {
6221
6221
  "spdxElementId": "SPDXRef-RootPackage",
6222
- "relatedSpdxElement": "SPDXRef-Package--kici-dev-sdk-0.1.24",
6222
+ "relatedSpdxElement": "SPDXRef-Package--kici-dev-sdk-0.1.25",
6223
6223
  "relationshipType": "DEPENDS_ON"
6224
6224
  },
6225
6225
  {
6226
6226
  "spdxElementId": "SPDXRef-RootPackage",
6227
- "relatedSpdxElement": "SPDXRef-Package--kici-dev-shared-0.1.24",
6227
+ "relatedSpdxElement": "SPDXRef-Package--kici-dev-shared-0.1.25",
6228
6228
  "relationshipType": "DEPENDS_ON"
6229
6229
  },
6230
6230
  {
@@ -6278,192 +6278,192 @@
6278
6278
  "relationshipType": "DEPENDS_ON"
6279
6279
  },
6280
6280
  {
6281
- "spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.24",
6281
+ "spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.25",
6282
6282
  "relatedSpdxElement": "SPDXRef-Package-oxc-transform-0.135.0",
6283
6283
  "relationshipType": "DEPENDS_ON"
6284
6284
  },
6285
6285
  {
6286
- "spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.24",
6286
+ "spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.25",
6287
6287
  "relatedSpdxElement": "SPDXRef-Package-picocolors-1.1.1",
6288
6288
  "relationshipType": "DEPENDS_ON"
6289
6289
  },
6290
6290
  {
6291
- "spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.24",
6291
+ "spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.25",
6292
6292
  "relatedSpdxElement": "SPDXRef-Package-winston-daily-rotate-file-5.0.0",
6293
6293
  "relationshipType": "DEPENDS_ON"
6294
6294
  },
6295
6295
  {
6296
- "spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.24",
6296
+ "spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.25",
6297
6297
  "relatedSpdxElement": "SPDXRef-Package-winston-3.19.0",
6298
6298
  "relationshipType": "DEPENDS_ON"
6299
6299
  },
6300
6300
  {
6301
- "spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.24",
6301
+ "spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.25",
6302
6302
  "relatedSpdxElement": "SPDXRef-Package-zod-4.4.3",
6303
6303
  "relationshipType": "DEPENDS_ON"
6304
6304
  },
6305
6305
  {
6306
- "spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.24",
6306
+ "spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.25",
6307
6307
  "relatedSpdxElement": "SPDXRef-Package-zx-8.8.5",
6308
6308
  "relationshipType": "DEPENDS_ON"
6309
6309
  },
6310
6310
  {
6311
- "spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.24",
6311
+ "spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.25",
6312
6312
  "relatedSpdxElement": "SPDXRef-Package-jose-6.2.3",
6313
6313
  "relationshipType": "DEPENDS_ON"
6314
6314
  },
6315
6315
  {
6316
- "spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.24",
6316
+ "spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.25",
6317
6317
  "relatedSpdxElement": "SPDXRef-Package-jsonpath-plus-10.4.0",
6318
6318
  "relationshipType": "DEPENDS_ON"
6319
6319
  },
6320
6320
  {
6321
- "spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.24",
6321
+ "spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.25",
6322
6322
  "relatedSpdxElement": "SPDXRef-Package-picomatch-4.0.4",
6323
6323
  "relationshipType": "DEPENDS_ON"
6324
6324
  },
6325
6325
  {
6326
- "spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.24",
6326
+ "spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.25",
6327
6327
  "relatedSpdxElement": "SPDXRef-Package-safe-regex-2.1.1",
6328
6328
  "relationshipType": "DEPENDS_ON"
6329
6329
  },
6330
6330
  {
6331
- "spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.24",
6331
+ "spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.25",
6332
6332
  "relatedSpdxElement": "SPDXRef-Package-zod-4.4.3",
6333
6333
  "relationshipType": "DEPENDS_ON"
6334
6334
  },
6335
6335
  {
6336
- "spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.24",
6337
- "relatedSpdxElement": "SPDXRef-Package--kici-dev-core-0.1.24",
6336
+ "spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.25",
6337
+ "relatedSpdxElement": "SPDXRef-Package--kici-dev-core-0.1.25",
6338
6338
  "relationshipType": "DEPENDS_ON"
6339
6339
  },
6340
6340
  {
6341
- "spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.24",
6342
- "relatedSpdxElement": "SPDXRef-Package--kici-dev-engine-0.1.24",
6341
+ "spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.25",
6342
+ "relatedSpdxElement": "SPDXRef-Package--kici-dev-engine-0.1.25",
6343
6343
  "relationshipType": "DEPENDS_ON"
6344
6344
  },
6345
6345
  {
6346
- "spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.24",
6346
+ "spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.25",
6347
6347
  "relatedSpdxElement": "SPDXRef-Package-micromatch-4.0.8",
6348
6348
  "relationshipType": "DEPENDS_ON"
6349
6349
  },
6350
6350
  {
6351
- "spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.24",
6351
+ "spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.25",
6352
6352
  "relatedSpdxElement": "SPDXRef-Package-zod-4.4.3",
6353
6353
  "relationshipType": "DEPENDS_ON"
6354
6354
  },
6355
6355
  {
6356
- "spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.24",
6356
+ "spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.25",
6357
6357
  "relatedSpdxElement": "SPDXRef-Package-zx-8.8.5",
6358
6358
  "relationshipType": "DEPENDS_ON"
6359
6359
  },
6360
6360
  {
6361
- "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.24",
6361
+ "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.25",
6362
6362
  "relatedSpdxElement": "SPDXRef-Package--aws-sdk-client-s3-3.1064.0",
6363
6363
  "relationshipType": "DEPENDS_ON"
6364
6364
  },
6365
6365
  {
6366
- "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.24",
6367
- "relatedSpdxElement": "SPDXRef-Package--kici-dev-core-0.1.24",
6366
+ "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.25",
6367
+ "relatedSpdxElement": "SPDXRef-Package--kici-dev-core-0.1.25",
6368
6368
  "relationshipType": "DEPENDS_ON"
6369
6369
  },
6370
6370
  {
6371
- "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.24",
6371
+ "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.25",
6372
6372
  "relatedSpdxElement": "SPDXRef-Package--opentelemetry-api-1.9.1",
6373
6373
  "relationshipType": "DEPENDS_ON"
6374
6374
  },
6375
6375
  {
6376
- "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.24",
6376
+ "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.25",
6377
6377
  "relatedSpdxElement": "SPDXRef-Package--opentelemetry-exporter-metrics-otlp-http-0.218.0",
6378
6378
  "relationshipType": "DEPENDS_ON"
6379
6379
  },
6380
6380
  {
6381
- "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.24",
6381
+ "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.25",
6382
6382
  "relatedSpdxElement": "SPDXRef-Package--opentelemetry-exporter-prometheus-0.218.0",
6383
6383
  "relationshipType": "DEPENDS_ON"
6384
6384
  },
6385
6385
  {
6386
- "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.24",
6386
+ "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.25",
6387
6387
  "relatedSpdxElement": "SPDXRef-Package--opentelemetry-exporter-trace-otlp-http-0.218.0",
6388
6388
  "relationshipType": "DEPENDS_ON"
6389
6389
  },
6390
6390
  {
6391
- "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.24",
6391
+ "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.25",
6392
6392
  "relatedSpdxElement": "SPDXRef-Package--opentelemetry-instrumentation-runtime-node-0.31.0",
6393
6393
  "relationshipType": "DEPENDS_ON"
6394
6394
  },
6395
6395
  {
6396
- "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.24",
6396
+ "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.25",
6397
6397
  "relatedSpdxElement": "SPDXRef-Package--opentelemetry-resources-2.7.1",
6398
6398
  "relationshipType": "DEPENDS_ON"
6399
6399
  },
6400
6400
  {
6401
- "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.24",
6401
+ "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.25",
6402
6402
  "relatedSpdxElement": "SPDXRef-Package--opentelemetry-sdk-node-0.218.0",
6403
6403
  "relationshipType": "DEPENDS_ON"
6404
6404
  },
6405
6405
  {
6406
- "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.24",
6406
+ "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.25",
6407
6407
  "relatedSpdxElement": "SPDXRef-Package--opentelemetry-semantic-conventions-1.41.1",
6408
6408
  "relationshipType": "DEPENDS_ON"
6409
6409
  },
6410
6410
  {
6411
- "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.24",
6411
+ "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.25",
6412
6412
  "relatedSpdxElement": "SPDXRef-Package-archiver-8.0.0",
6413
6413
  "relationshipType": "DEPENDS_ON"
6414
6414
  },
6415
6415
  {
6416
- "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.24",
6416
+ "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.25",
6417
6417
  "relatedSpdxElement": "SPDXRef-Package-diff-9.0.0",
6418
6418
  "relationshipType": "DEPENDS_ON"
6419
6419
  },
6420
6420
  {
6421
- "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.24",
6421
+ "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.25",
6422
6422
  "relatedSpdxElement": "SPDXRef-Package-hono-4.12.25",
6423
6423
  "relationshipType": "DEPENDS_ON"
6424
6424
  },
6425
6425
  {
6426
- "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.24",
6426
+ "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.25",
6427
6427
  "relatedSpdxElement": "SPDXRef-Package-kysely-0.29.2",
6428
6428
  "relationshipType": "DEPENDS_ON"
6429
6429
  },
6430
6430
  {
6431
- "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.24",
6431
+ "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.25",
6432
6432
  "relatedSpdxElement": "SPDXRef-Package-oxc-transform-0.135.0",
6433
6433
  "relationshipType": "DEPENDS_ON"
6434
6434
  },
6435
6435
  {
6436
- "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.24",
6436
+ "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.25",
6437
6437
  "relatedSpdxElement": "SPDXRef-Package-pg-8.21.0",
6438
6438
  "relationshipType": "DEPENDS_ON"
6439
6439
  },
6440
6440
  {
6441
- "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.24",
6441
+ "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.25",
6442
6442
  "relatedSpdxElement": "SPDXRef-Package-picocolors-1.1.1",
6443
6443
  "relationshipType": "DEPENDS_ON"
6444
6444
  },
6445
6445
  {
6446
- "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.24",
6446
+ "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.25",
6447
6447
  "relatedSpdxElement": "SPDXRef-Package-winston-daily-rotate-file-5.0.0",
6448
6448
  "relationshipType": "DEPENDS_ON"
6449
6449
  },
6450
6450
  {
6451
- "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.24",
6451
+ "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.25",
6452
6452
  "relatedSpdxElement": "SPDXRef-Package-winston-3.19.0",
6453
6453
  "relationshipType": "DEPENDS_ON"
6454
6454
  },
6455
6455
  {
6456
- "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.24",
6456
+ "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.25",
6457
6457
  "relatedSpdxElement": "SPDXRef-Package-yaml-2.9.0",
6458
6458
  "relationshipType": "DEPENDS_ON"
6459
6459
  },
6460
6460
  {
6461
- "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.24",
6461
+ "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.25",
6462
6462
  "relatedSpdxElement": "SPDXRef-Package-zod-4.4.3",
6463
6463
  "relationshipType": "DEPENDS_ON"
6464
6464
  },
6465
6465
  {
6466
- "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.24",
6466
+ "spdxElementId": "SPDXRef-Package--kici-dev-shared-0.1.25",
6467
6467
  "relatedSpdxElement": "SPDXRef-Package-zx-8.8.5",
6468
6468
  "relationshipType": "DEPENDS_ON"
6469
6469
  },