@kici-dev/agent 0.1.20 → 0.1.22

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.
@@ -7,8 +7,8 @@ import os, { homedir, tmpdir } from "node:os";
7
7
  import path, { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
8
8
  import { $ } from "zx";
9
9
  import { createLogger, deriveSharedSecret, initZx, normalizeLineEndings, sha256, sha256File, toErrorMessage } from "@kici-dev/shared";
10
- import { CacheOutcome, CacheStepType, ExecutionJobStatus, ExecutionStepStatus, TimeoutReason } from "@kici-dev/engine";
11
- import { buildKiciApi, createStepSecrets, evaluateRules, isDynamicJobFn, normalizeCacheSpecs, normalizeRequireApproval, provenanceSubjectIsPath, resolveJobOutputs, resolveStepOutputs, setJobOutputsMap, setStepOutputsMap, setStepRefMap } from "@kici-dev/sdk";
10
+ import { CacheOutcome, CacheStepType, CheckMode, CheckStepOutcome, ExecutionJobStatus, ExecutionStepStatus, TimeoutReason } from "@kici-dev/engine";
11
+ import { buildKiciApi, buildNeedsContext, createStepSecrets, evaluateRules, isDynamicJobFn, normalizeApproval, normalizeCacheSpecs, provenanceSubjectIsPath, resolveJobOutputs, resolveStepOutputs, setJobOutputsMap, setStepOutputsMap, setStepRefMap } from "@kici-dev/sdk";
12
12
  import { OIDC_TOKEN_REQUEST_METHOD } from "@kici-dev/engine/protocol/messages/oidc-token-relay";
13
13
  import { sha256File as sha256File$1 } from "@kici-dev/core";
14
14
  import { calculateJwkThumbprint, decodeJwt, exportJWK, generateKeyPair } from "jose";
@@ -22,6 +22,7 @@ import { pipeline } from "node:stream/promises";
22
22
  import { createGunzip } from "node:zlib";
23
23
  import { fileURLToPath, pathToFileURL } from "node:url";
24
24
  import { c, x } from "tar";
25
+ import { runIdempotentStep } from "@kici-dev/core/idempotency";
25
26
  import { execFile } from "node:child_process";
26
27
  import { promisify } from "node:util";
27
28
  import { PNPM_IGNORE_BUILD_GATE_ARG, PackageManager, YarnFlavor, detectPackageManagerFromManifests, detectYarnFlavor } from "@kici-dev/shared/package-manager";
@@ -1116,12 +1117,92 @@ function createRuleContext(event, changedFiles = [], env = {}) {
1116
1117
  }
1117
1118
  //#endregion
1118
1119
  //#region src/execution/sandbox/step-loop.ts
1120
+ /** Result of a rejected drift gate: the run was declined by a reviewer. */
1121
+ var DriftGateRejectedError = class extends Error {
1122
+ constructor(reason) {
1123
+ super(reason ? `approval rejected: ${reason}` : "approval rejected");
1124
+ this.name = "DriftGateRejectedError";
1125
+ }
1126
+ };
1127
+ /**
1128
+ * Run one step honoring the run-level {@link CheckMode}, reusing the
1129
+ * `runIdempotentStep` primitive for checked steps (never hand-rolled branching).
1130
+ *
1131
+ * - Plain step (no `check`): in apply mode, runs as today; in any check mode it
1132
+ * is skipped with `no_check` (a side-effecting step can't be safely previewed).
1133
+ * - Checked step: adapted into an `IdempotentStep` and driven by the primitive
1134
+ * with `dryRun` set in check mode (so `apply`/`run` never fires). On drift the
1135
+ * summary is emitted as a log line. A `approval: { when: 'drift' }` step in
1136
+ * apply mode passes a `confirm` callback that round-trips a payload-bearing
1137
+ * step-approval; on reject the gate throws (fail-stop). Any other apply-mode
1138
+ * step uses `yes: true`.
1139
+ */
1140
+ async function runStepWithCheckMode(step, stepIndex, ctx, checkMode, sendFn, opts) {
1141
+ if (!step.check) {
1142
+ if (checkMode !== CheckMode.enum.apply) return {
1143
+ checkOutcome: CheckStepOutcome.enum.no_check,
1144
+ status: "skipped",
1145
+ outputs: void 0
1146
+ };
1147
+ return {
1148
+ status: "success",
1149
+ outputs: await step.run(ctx)
1150
+ };
1151
+ }
1152
+ let lastDrift = null;
1153
+ const adapted = {
1154
+ name: step.name,
1155
+ check: async () => {
1156
+ lastDrift = await step.check(ctx);
1157
+ return lastDrift;
1158
+ },
1159
+ summarize: step.summarize,
1160
+ apply: (drift) => step.run(ctx, drift),
1161
+ whenInSync: step.whenInSync ? () => step.whenInSync(ctx) : void 0
1162
+ };
1163
+ const driftGate = step.approval !== void 0 && normalizeApproval(step.approval).when === "drift" && checkMode === CheckMode.enum.apply && opts.awaitStepApprovalWithPayload !== void 0;
1164
+ const res = await runIdempotentStep(adapted, {
1165
+ dryRun: checkMode !== CheckMode.enum.apply,
1166
+ ...driftGate ? { confirm: async () => {
1167
+ const norm = normalizeApproval(step.approval);
1168
+ const summaryMarkdown = step.summarize(lastDrift);
1169
+ const resolution = await opts.awaitStepApprovalWithPayload({
1170
+ stepIndex,
1171
+ stepName: step.name,
1172
+ clauses: norm.clauses,
1173
+ reason: norm.reason ?? `Approval required for drift in '${step.name}'`,
1174
+ ...norm.timeoutSeconds !== void 0 && { timeoutSeconds: norm.timeoutSeconds },
1175
+ payload: {
1176
+ summaryMarkdown,
1177
+ drift: lastDrift
1178
+ }
1179
+ });
1180
+ if (resolution.outcome === "approved") return true;
1181
+ throw new DriftGateRejectedError(resolution.outcome === "expired" ? "approval expired" : resolution.reason);
1182
+ } } : { yes: true },
1183
+ log: (line) => sendFn({
1184
+ type: "log.line",
1185
+ stepIndex,
1186
+ line
1187
+ })
1188
+ });
1189
+ const driftSummary = res.drift != null ? step.summarize(res.drift) : void 0;
1190
+ const status = res.outcome === CheckStepOutcome.enum.applied ? "success" : "skipped";
1191
+ const mappedStatus = res.outcome === CheckStepOutcome.enum["dry-run"] ? "success" : status;
1192
+ return {
1193
+ checkOutcome: res.outcome,
1194
+ status: mappedStatus,
1195
+ outputs: res.result,
1196
+ ...driftSummary !== void 0 && { driftSummary },
1197
+ ...res.drift != null && { drift: res.drift }
1198
+ };
1199
+ }
1119
1200
  /**
1120
1201
  * Execute a single step with timeout enforcement.
1121
1202
  *
1122
1203
  * Timeout pattern using Promise.race + AbortController, with IPC status reporting.
1123
1204
  */
1124
- async function executeStepInLoop(step, stepIndex, ctx, timeoutMs, sendFn, outputsMap, getSecretsAccessLog, getSecretMountRecords, jobDeadlineSignal) {
1205
+ async function executeStepInLoop(step, stepIndex, ctx, timeoutMs, sendFn, outputsMap, getSecretsAccessLog, getSecretMountRecords, jobDeadlineSignal, checkMode, opts) {
1125
1206
  sendFn({
1126
1207
  type: "step.start",
1127
1208
  stepIndex,
@@ -1131,8 +1212,8 @@ async function executeStepInLoop(step, stepIndex, ctx, timeoutMs, sendFn, output
1131
1212
  const abortController = new AbortController();
1132
1213
  const timeoutId = setTimeout(() => abortController.abort(), timeoutMs);
1133
1214
  try {
1134
- const result = await Promise.race([
1135
- step.run(ctx),
1215
+ const phase = await Promise.race([
1216
+ runStepWithCheckMode(step, stepIndex, ctx, checkMode, sendFn, opts),
1136
1217
  new Promise((_, reject) => {
1137
1218
  abortController.signal.addEventListener("abort", () => {
1138
1219
  reject(/* @__PURE__ */ new Error(`Step '${step.name}' timed out after ${timeoutMs}ms`));
@@ -1151,22 +1232,26 @@ async function executeStepInLoop(step, stepIndex, ctx, timeoutMs, sendFn, output
1151
1232
  ]);
1152
1233
  clearTimeout(timeoutId);
1153
1234
  const durationMs = Date.now() - startTime;
1154
- const outputsPayload = result != null ? result : void 0;
1235
+ const outputsPayload = phase.outputs != null ? phase.outputs : void 0;
1155
1236
  if (outputsPayload) outputsMap.set(step.name, outputsPayload);
1156
1237
  const secretsAccessed = getSecretsAccessLog?.();
1157
1238
  emitSecretMountEvents(getSecretMountRecords?.(), stepIndex, sendFn);
1239
+ const stepStatus = phase.status === "skipped" ? ExecutionStepStatus.enum.skipped : ExecutionStepStatus.enum.success;
1158
1240
  sendFn({
1159
1241
  type: "step.complete",
1160
1242
  stepIndex,
1161
- status: ExecutionStepStatus.enum.success,
1243
+ status: stepStatus,
1162
1244
  durationMs,
1163
1245
  ...outputsPayload && { outputs: outputsPayload },
1164
- ...secretsAccessed !== void 0 && { secretsAccessed }
1246
+ ...secretsAccessed !== void 0 && { secretsAccessed },
1247
+ ...phase.checkOutcome !== void 0 && { checkOutcome: phase.checkOutcome },
1248
+ ...phase.driftSummary !== void 0 && { driftSummary: phase.driftSummary },
1249
+ ...phase.drift !== void 0 && { drift: phase.drift }
1165
1250
  });
1166
1251
  return {
1167
1252
  name: step.name,
1168
1253
  stepIndex,
1169
- status: ExecutionStepStatus.enum.success,
1254
+ status: stepStatus,
1170
1255
  durationMs,
1171
1256
  ...outputsPayload && { outputs: outputsPayload }
1172
1257
  };
@@ -1258,14 +1343,17 @@ async function evaluateStepRulesAndMaybeSkip(step, stepIndex, opts) {
1258
1343
  };
1259
1344
  }
1260
1345
  /**
1261
- * Manual approval gate for a step. When the step declares `requireApproval`
1262
- * and the harness wired `awaitStepApproval`, block until the orchestrator
1263
- * resolves the hold. Returns a failed `StepIterationOutcome` (breaking the
1264
- * loop) on reject/expired; returns null when approved or when no gate applies.
1346
+ * Pre-step manual approval gate. When the step declares `approval` with
1347
+ * `when: 'always'` and the harness wired `awaitStepApproval`, block until the
1348
+ * orchestrator resolves the hold. A `when: 'drift'` gate is NOT handled here —
1349
+ * it fires mid-execution inside `runStepWithCheckMode` once `check()` returns
1350
+ * drift. Returns a failed `StepIterationOutcome` (breaking the loop) on
1351
+ * reject/expired; returns null when approved or when no gate applies.
1265
1352
  */
1266
1353
  async function maybeGateStepApproval(step, stepIndex, opts) {
1267
- if (step.requireApproval === void 0 || !opts.awaitStepApproval) return null;
1268
- const normalized = normalizeRequireApproval(step.requireApproval);
1354
+ if (step.approval === void 0 || !opts.awaitStepApproval) return null;
1355
+ const normalized = normalizeApproval(step.approval);
1356
+ if (normalized.when === "drift") return null;
1269
1357
  opts.sendIpc({
1270
1358
  type: "log.line",
1271
1359
  stepIndex,
@@ -1381,7 +1469,7 @@ async function runStepIteration(step, stepIndex, opts) {
1381
1469
  const timeoutMs = step.timeout ?? opts.defaultTimeoutMs;
1382
1470
  let result;
1383
1471
  try {
1384
- result = await executeStepInLoop(step, stepIndex, ctx, timeoutMs, opts.sendIpc, opts.outputsMap, opts.getSecretsAccessLog, opts.getSecretMountRecords, opts.jobDeadlineSignal);
1472
+ result = await executeStepInLoop(step, stepIndex, ctx, timeoutMs, opts.sendIpc, opts.outputsMap, opts.getSecretsAccessLog, opts.getSecretMountRecords, opts.jobDeadlineSignal, opts.checkMode ?? CheckMode.enum.apply, opts);
1385
1473
  } finally {
1386
1474
  await opts.afterStepApplyEnvFiles?.();
1387
1475
  }
@@ -3023,8 +3111,8 @@ function logSubprocessStreams(e, tokens) {
3023
3111
  * no Rolldown step at runtime. `@kici-dev/sdk` and host-repo deps resolve via
3024
3112
  * Node's normal ESM lookup against `.kici/node_modules/`.
3025
3113
  */
3026
- const AGENT_SDK_VERSION = "0.1.20";
3027
- const AGENT_SDK_BUNDLE_HASH = "8308089347c304e41b457d3867b17bbff11d6b5cd9706b6823e7abdbd849f33f";
3114
+ const AGENT_SDK_VERSION = "0.1.22";
3115
+ const AGENT_SDK_BUNDLE_HASH = "4b26a42978f77e29db110d47523ec947d3d673c3109fc48141c1a62814bde7a1";
3028
3116
  /**
3029
3117
  * Register the `@kici-dev/core/ts-loader-hook` oxc-transform ESM loader hook so
3030
3118
  * subsequent dynamic `import()` calls for `.ts` / `.tsx` files transform on the
@@ -3386,7 +3474,7 @@ async function applyOverlay(config) {
3386
3474
  */
3387
3475
  init_download();
3388
3476
  init_dep_restore();
3389
- const AGENT_VERSION = "0.1.20";
3477
+ const AGENT_VERSION = "0.1.22";
3390
3478
  process.on("uncaughtException", (err) => {
3391
3479
  process.stderr.write(`[workflow-runner] UNCAUGHT EXCEPTION: ${err.message}\n`);
3392
3480
  if (err.stack) process.stderr.write(`[workflow-runner] Stack: ${err.stack}\n`);
@@ -3779,35 +3867,49 @@ function waitForApprovalResolution(requestId) {
3779
3867
  });
3780
3868
  }
3781
3869
  /**
3782
- * Build the `awaitStepApproval` callback the step loop uses to block on a
3783
- * `requireApproval` step. Sends an `approval.request` IPC (relayed by the agent
3784
- * over the WS as a `step.approval-request`) and awaits the matching
3785
- * `approval.resolved`. A relay error is treated as a fail-closed reject.
3870
+ * Send a step-approval `approval.request` IPC (relayed by the agent over the WS
3871
+ * as a `step.approval-request`), await the matching `approval.resolved`, and map
3872
+ * it onto a `StepApprovalResolution`. A relay error is treated as a fail-closed
3873
+ * reject. Shared by the `when: 'always'` and `when: 'drift'` callbacks; the
3874
+ * latter carries a drift `payload`.
3786
3875
  */
3787
- function buildAwaitStepApproval() {
3788
- return async (req) => {
3789
- const requestId = randomUUID();
3790
- sendMessage({
3791
- type: "approval.request",
3792
- requestId,
3793
- stepIndex: req.stepIndex,
3794
- stepName: req.stepName,
3795
- clauses: req.clauses,
3796
- reason: req.reason,
3797
- ...req.timeoutSeconds !== void 0 && { timeoutSeconds: req.timeoutSeconds }
3798
- });
3799
- const resolution = await waitForApprovalResolution(requestId);
3800
- if (resolution.error) return {
3801
- outcome: "rejected",
3802
- reason: resolution.error
3803
- };
3804
- return {
3805
- outcome: resolution.outcome ?? "rejected",
3806
- ...resolution.reason !== void 0 && { reason: resolution.reason }
3807
- };
3876
+ async function requestStepApproval(req) {
3877
+ const requestId = randomUUID();
3878
+ sendMessage({
3879
+ type: "approval.request",
3880
+ requestId,
3881
+ stepIndex: req.stepIndex,
3882
+ stepName: req.stepName,
3883
+ clauses: req.clauses,
3884
+ reason: req.reason,
3885
+ ...req.timeoutSeconds !== void 0 && { timeoutSeconds: req.timeoutSeconds },
3886
+ ...req.payload !== void 0 && { payload: req.payload }
3887
+ });
3888
+ const resolution = await waitForApprovalResolution(requestId);
3889
+ if (resolution.error) return {
3890
+ outcome: "rejected",
3891
+ reason: resolution.error
3892
+ };
3893
+ return {
3894
+ outcome: resolution.outcome ?? "rejected",
3895
+ ...resolution.reason !== void 0 && { reason: resolution.reason }
3808
3896
  };
3809
3897
  }
3810
3898
  /**
3899
+ * Build the `awaitStepApproval` callback the step loop uses to block on an
3900
+ * `approval` step (`when: 'always'`).
3901
+ */
3902
+ function buildAwaitStepApproval() {
3903
+ return (req) => requestStepApproval(req);
3904
+ }
3905
+ /**
3906
+ * Build the `awaitStepApprovalWithPayload` callback the step loop uses to block
3907
+ * on a `when: 'drift'` step mid-execution, carrying the computed drift payload.
3908
+ */
3909
+ function buildAwaitStepApprovalWithPayload() {
3910
+ return (req) => requestStepApproval(req);
3911
+ }
3912
+ /**
3811
3913
  * Build the {@link CacheTransport} the sandbox-side cache engine uses to reach
3812
3914
  * the orchestrator. Each method sends a `cache.request` IPC (relayed by the
3813
3915
  * agent over the WS as a `cache.user.*` message) and awaits the matching
@@ -4040,6 +4142,75 @@ function createIpcLogger(stepIndex, stepName, sendFn) {
4040
4142
  }
4041
4143
  };
4042
4144
  }
4145
+ /** A lock needs entry maps to a base name (single/matrix/host) or a group name. */
4146
+ function needBaseName(need) {
4147
+ if (typeof need === "string") return {
4148
+ kind: "job",
4149
+ key: need
4150
+ };
4151
+ if (need && typeof need === "object") {
4152
+ if ("group" in need) return {
4153
+ kind: "group",
4154
+ key: need.group
4155
+ };
4156
+ if ("name" in need) return {
4157
+ kind: "job",
4158
+ key: need.name
4159
+ };
4160
+ }
4161
+ return null;
4162
+ }
4163
+ /**
4164
+ * Build `ctx.needs` for a job's steps from the dispatch envelope. Reconstructs
4165
+ * an {@link UpstreamSnapshot} from `upstreamJobOutputs` (flat per single job;
4166
+ * `byMatrix` / `byHost` envelopes per fan-out) + `upstreamJobStatuses` (keyed by
4167
+ * each upstream job/child name), then resolves the job's declared needs into the
4168
+ * `{ result, status }` / ordered-array shape via the shared SDK builder. Returns
4169
+ * undefined when the job declares no needs.
4170
+ */
4171
+ function buildStepNeedsContext(declaredNeeds, upstreamJobOutputs, upstreamJobStatuses) {
4172
+ if (!declaredNeeds || declaredNeeds.length === 0) return void 0;
4173
+ const statuses = upstreamJobStatuses ?? {};
4174
+ const jobs = {};
4175
+ const groups = {};
4176
+ const snapStatuses = {};
4177
+ const resolvedNeeds = [];
4178
+ for (const need of declaredNeeds) {
4179
+ const base = needBaseName(need);
4180
+ if (!base) continue;
4181
+ const childNames = Object.keys(statuses).filter((n) => n.startsWith(`${base.key} (`));
4182
+ if (base.kind === "group" || childNames.length > 0) {
4183
+ groups[base.key] = [...childNames].sort();
4184
+ const envelope = upstreamJobOutputs?.[base.key];
4185
+ const bySuffix = envelopeChildOutputs(envelope);
4186
+ for (const child of childNames) {
4187
+ jobs[child] = bySuffix[child.slice(base.key.length + 2, -1)] ?? {};
4188
+ snapStatuses[child] = statuses[child];
4189
+ }
4190
+ resolvedNeeds.push({ group: base.key });
4191
+ } else {
4192
+ jobs[base.key] = upstreamJobOutputs?.[base.key] ?? {};
4193
+ if (statuses[base.key]) snapStatuses[base.key] = statuses[base.key];
4194
+ resolvedNeeds.push(base.key);
4195
+ }
4196
+ }
4197
+ return buildNeedsContext({
4198
+ jobs,
4199
+ groups,
4200
+ statuses: snapStatuses
4201
+ }, resolvedNeeds);
4202
+ }
4203
+ /**
4204
+ * Extract per-child output records from a fan-out outputs envelope, keyed by the
4205
+ * combination suffix (matrix `byMatrix`) or hostname (`runsOnAll` `byHost`).
4206
+ * Returns an empty map for a non-envelope value.
4207
+ */
4208
+ function envelopeChildOutputs(envelope) {
4209
+ if (!envelope) return {};
4210
+ const byMatrix = envelope.byMatrix;
4211
+ const byHost = envelope.byHost;
4212
+ return byMatrix ?? byHost ?? {};
4213
+ }
4043
4214
  /**
4044
4215
  * Build StepSecrets from the job execution request, wired with the per-step
4045
4216
  * file-mount host (used by `ctx.secrets.mountFile` / `exposeFile`).
@@ -4235,7 +4406,11 @@ function createSandboxStepContext(workDir, stepIndex, stepName, request, maskedS
4235
4406
  ...request.provider && { provider: request.provider },
4236
4407
  ...request.matrixValues && { matrix: request.matrixValues },
4237
4408
  ...request.host && { host: request.host },
4238
- ...request.agent && { agent: request.agent }
4409
+ ...request.agent && { agent: request.agent },
4410
+ ...(() => {
4411
+ const needs = buildStepNeedsContext(request.jobNeeds, request.upstreamJobOutputs, request.upstreamJobStatuses);
4412
+ return needs ? { needs } : {};
4413
+ })()
4239
4414
  };
4240
4415
  }
4241
4416
  /** Raw provider webhook body for ctx.rawPayload — nested in the envelope. */
@@ -5099,6 +5274,7 @@ async function main() {
5099
5274
  const jobStartTime = Date.now();
5100
5275
  const loopResult = await executeStepLoop({
5101
5276
  steps: normalizedSteps,
5277
+ checkMode: request.checkMode,
5102
5278
  createStepContext: createStepCtxWithCapture,
5103
5279
  sendIpc: maskedSend,
5104
5280
  defaultTimeoutMs,
@@ -5126,7 +5302,8 @@ async function main() {
5126
5302
  },
5127
5303
  beforeStepEnvFiles: stepEnvHooks.beforeStepEnvFiles,
5128
5304
  afterStepApplyEnvFiles: stepEnvHooks.afterStepApplyEnvFiles,
5129
- awaitStepApproval: buildAwaitStepApproval()
5305
+ awaitStepApproval: buildAwaitStepApproval(),
5306
+ awaitStepApprovalWithPayload: buildAwaitStepApprovalWithPayload()
5130
5307
  });
5131
5308
  jobDeadline.clear();
5132
5309
  await maybeSaveJobCache(jobCacheSpecs, jobCacheRestore, cachePhaseDeps, !aborted && loopResult.status === ExecutionStepStatus.enum.success);
@@ -5212,6 +5389,6 @@ main().catch((error) => {
5212
5389
  setTimeout(() => process.exit(1), 100);
5213
5390
  });
5214
5391
  //#endregion
5215
- export { createSandboxStepContext, rawPayloadFromEvent };
5392
+ export { buildStepNeedsContext, createSandboxStepContext, rawPayloadFromEvent };
5216
5393
 
5217
5394
  //# sourceMappingURL=workflow-runner.js.map
@@ -8,6 +8,12 @@ export interface OrchestratorClientOptions {
8
8
  agentId: string;
9
9
  /** Agent's label set for job routing. */
10
10
  labels: string[];
11
+ /**
12
+ * Agent-reported typed host-vars (the `KICI_PROPERTIES` bag). Reported at
13
+ * registration and shallow-merged into the orchestrator's host roster.
14
+ * Omitted / empty ⇒ no properties reported.
15
+ */
16
+ properties?: Record<string, string | number | boolean>;
11
17
  /** Callback invoked when a job.dispatch message is received. */
12
18
  onJobDispatch: (dispatch: JobDispatch) => void;
13
19
  /** Callback invoked when a job.cancel message is received. */
@@ -94,6 +100,7 @@ export declare class OrchestratorClient {
94
100
  private readonly url;
95
101
  private readonly agentId;
96
102
  private readonly labels;
103
+ private readonly properties;
97
104
  private readonly onJobDispatch;
98
105
  private readonly onJobCancel;
99
106
  private readonly token?;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kici-dev/agent",
3
- "version": "0.1.20",
3
+ "version": "0.1.22",
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/core": "0.1.20",
68
- "@kici-dev/engine": "0.1.20",
69
- "@kici-dev/sdk": "0.1.20",
70
- "@kici-dev/shared": "0.1.20"
67
+ "@kici-dev/core": "0.1.22",
68
+ "@kici-dev/engine": "0.1.22",
69
+ "@kici-dev/sdk": "0.1.22",
70
+ "@kici-dev/shared": "0.1.22"
71
71
  },
72
72
  "kici": {
73
73
  "metrics": {