@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.
package/dist/config.d.ts CHANGED
@@ -10,6 +10,7 @@ declare const configSchema: z.ZodObject<{
10
10
  orchestratorUrl: z.ZodString;
11
11
  agentId: z.ZodOptional<z.ZodString>;
12
12
  labels: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<string[], string>>;
13
+ properties: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<Record<string, string | number | boolean>, string>>;
13
14
  roles: z.ZodPipe<z.ZodPipe<z.ZodOptional<z.ZodString>, z.ZodTransform<string[] | undefined, string | undefined>>, z.ZodTransform<string[] | undefined, string[] | undefined>>;
14
15
  port: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
15
16
  logLevel: z.ZodDefault<z.ZodEnum<{
@@ -43,6 +44,7 @@ declare const configSchema: z.ZodObject<{
43
44
  }>>;
44
45
  otelExporterOtlpEndpoint: z.ZodOptional<z.ZodString>;
45
46
  concurrencyWaitTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
47
+ isOrchestratorHost: z.ZodPipe<z.ZodOptional<z.ZodString>, z.ZodTransform<boolean, string | undefined>>;
46
48
  }, z.core.$strip>;
47
49
  /**
48
50
  * App configuration type. Includes computed agentId when not provided.
@@ -59,6 +61,7 @@ export type AppConfig = z.infer<typeof configSchema> & {
59
61
  export declare const envDef: import("@kici-dev/shared/env").DefineEnvResult<{
60
62
  orchestratorUrl: string;
61
63
  labels: string[];
64
+ properties: Record<string, string | number | boolean>;
62
65
  roles: string[] | undefined;
63
66
  port: number;
64
67
  logLevel: "error" | "debug" | "info" | "warn";
@@ -73,6 +76,7 @@ export declare const envDef: import("@kici-dev/shared/env").DefineEnvResult<{
73
76
  scalerIdleTimeoutMs: number;
74
77
  scalerPendingDispatchTimeoutMs: number;
75
78
  concurrencyWaitTimeoutMs: number;
79
+ isOrchestratorHost: boolean;
76
80
  agentId?: string | undefined;
77
81
  agentToken?: string | undefined;
78
82
  githubToken?: string | undefined;
@@ -86,6 +90,7 @@ export declare const envDef: import("@kici-dev/shared/env").DefineEnvResult<{
86
90
  * - KICI_ORCHESTRATOR_URL (required)
87
91
  * - KICI_AGENT_ID (optional, auto-generated from hostname-uuid8)
88
92
  * - KICI_LABELS (comma-separated, e.g. "linux,docker"). Labels with 'kici-' prefix are reserved.
93
+ * - KICI_PROPERTIES (comma-separated key=value host-vars, e.g. "region=eu,cores=8,gpu=true"). Typed (bool/number/string), reported into the host roster.
89
94
  * - KICI_ROLES (comma-separated agent roles, e.g. "builder,init-runner". undefined=all, empty=execution-only)
90
95
  * - KICI_PORT (default: 8080)
91
96
  * - KICI_LOG_LEVEL (default: info)
@@ -120,6 +125,7 @@ export declare function agentClientConnectionOptions(config: AppConfig): {
120
125
  url: string;
121
126
  agentId: string;
122
127
  labels: string[];
128
+ properties: Record<string, string | number | boolean>;
123
129
  scalerManaged: boolean;
124
130
  token: string | undefined;
125
131
  };
@@ -1,5 +1,6 @@
1
1
  import type { AgentToOrchestratorMessage, JobDispatch } from '@kici-dev/engine';
2
2
  import type { AppConfig } from '../config.js';
3
+ import { buildNeedsContext } from '@kici-dev/sdk';
3
4
  import type { CacheRequestIpc, CacheResponseIpc, ProvenanceRequestIpc, ProvenanceResponseIpc, StepApprovalRequestIpc, StepApprovalResolvedIpc } from './sandbox/index.js';
4
5
  /**
5
6
  * Dependencies injected into JobRunner.
@@ -112,6 +113,15 @@ interface ActiveJob {
112
113
  completionPromise: Promise<void>;
113
114
  runId: string;
114
115
  }
116
+ /**
117
+ * Build the result-aware `ctx.needs` for a dynamic eval from its frozen upstream
118
+ * snapshot. Returns undefined for an event-only generator (no snapshot).
119
+ */
120
+ export declare function buildEvalNeedsContext(config: {
121
+ resultAware?: boolean;
122
+ declaredNeeds?: readonly unknown[];
123
+ upstreamSnapshot?: import('@kici-dev/engine').UpstreamSnapshot;
124
+ }): ReturnType<typeof buildNeedsContext> | undefined;
115
125
  /**
116
126
  * Top-level job execution orchestrator for the agent.
117
127
  *
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Cross-OS host reboot for the workflow-level `restartHost()` step.
3
+ *
4
+ * The agent runs ON the host it executes jobs for, so a `restartHost()` step
5
+ * reboots that host. `rebootCommandFor` is the pure OS→command mapping (kept
6
+ * pure for unit-testing); `issueReboot` spawns it detached with a tiny grace so
7
+ * the job's final flush completes before the box goes down.
8
+ *
9
+ * Rebooting needs host privilege (root/admin). If the primitive is denied, the
10
+ * spawn fails and the caller clears the orchestrator's reboot-pending flag and
11
+ * surfaces the error — the deadline sweep is the backstop.
12
+ */
13
+ /** The OS reboot primitive for a Node platform string. */
14
+ export declare function rebootCommandFor(platform: NodeJS.Platform): {
15
+ cmd: string;
16
+ args: string[];
17
+ };
18
+ /**
19
+ * Issue the OS reboot detached. Resolves once the child has been spawned (the
20
+ * box is going down; there is nothing to await). Rejects synchronously if the
21
+ * spawn itself fails (e.g. the binary is missing). A privilege denial usually
22
+ * surfaces as a non-zero exit AFTER spawn — logged, not thrown, because by then
23
+ * the box may already be on its way down.
24
+ */
25
+ export declare function issueReboot(platform?: NodeJS.Platform): Promise<void>;
26
+ //# sourceMappingURL=reboot.d.ts.map
@@ -1,3 +1,4 @@
1
+ import type { CheckMode, CheckStepOutcome } from '@kici-dev/engine';
1
2
  import type { SandboxStepResult } from './types.js';
2
3
  /**
3
4
  * Structured clone auth. Wire-compatible with `gitAuthSchema` on the
@@ -24,11 +25,11 @@ interface StepStartMessage {
24
25
  /** Distinguishes regular steps from hook executions (e.g., 'hook:onCancel', 'hook:cleanup'). Defaults to 'step'. */
25
26
  step_type?: string;
26
27
  }
27
- /** A step has completed (success or failure). */
28
+ /** A step has completed (success, failure, or a check-mode skip). */
28
29
  interface StepCompleteMessage {
29
30
  type: 'step.complete';
30
31
  stepIndex: number;
31
- status: 'success' | 'failed';
32
+ status: 'success' | 'failed' | 'skipped';
32
33
  durationMs: number;
33
34
  error?: {
34
35
  message: string;
@@ -47,6 +48,16 @@ interface StepCompleteMessage {
47
48
  * pseudo-steps carry `{ cacheOutcome, key, matchedKey?, bytes? }` here.
48
49
  */
49
50
  data?: Record<string, unknown>;
51
+ /**
52
+ * Idempotent per-step outcome (`CheckStepOutcome`). Present only when the run
53
+ * carried a check mode and the step has a `check` facet (or was a plain step
54
+ * skipped under check mode). Orthogonal to `status`.
55
+ */
56
+ checkOutcome?: CheckStepOutcome;
57
+ /** Human-readable drift summary (`summarize(drift)`). Present when drift was detected. */
58
+ driftSummary?: string;
59
+ /** Structured drift value returned by `check()`. Present when drift was detected. */
60
+ drift?: unknown;
50
61
  }
51
62
  /** A single log line from step execution. */
52
63
  interface LogLineMessage {
@@ -182,8 +193,13 @@ export interface StepApprovalRequestIpc {
182
193
  }>;
183
194
  /** Human label for the gate. */
184
195
  reason: string;
185
- /** Per-gate timeout override (seconds) from the SDK `requireApproval.timeout`. */
196
+ /** Per-gate timeout override (seconds) from the SDK `approval.timeout`. */
186
197
  timeoutSeconds?: number;
198
+ /** Computed drift payload, present only for `when: 'drift'` gates. */
199
+ payload?: {
200
+ summaryMarkdown: string;
201
+ drift: unknown;
202
+ };
187
203
  }
188
204
  /** Which provenance upload operation to relay. */
189
205
  export type ProvenanceRequestOp = 'requestUploadUrl' | 'complete';
@@ -426,6 +442,13 @@ export interface JobExecutionRequest {
426
442
  checkout?: boolean;
427
443
  /** Whether this job is part of a test run triggered by `kici test`. */
428
444
  isTestRun?: boolean;
445
+ /**
446
+ * Run mode for idempotent steps (`apply` | `check` | `check-fail-on-drift`).
447
+ * Threaded from the dispatch event. In check / check-fail-on-drift mode the
448
+ * runner previews drift and never invokes a checked step's apply (`run`).
449
+ * Defaults to `apply` when unset.
450
+ */
451
+ checkMode?: CheckMode;
429
452
  /** When true, skip git clone -- use overlay tarball as complete workspace. */
430
453
  fullRepo?: boolean;
431
454
  /** URL to download the encrypted overlay tarball (test runs with uncommitted changes). */
@@ -460,6 +483,10 @@ export interface JobExecutionRequest {
460
483
  branch?: string;
461
484
  /** Plain outputs from upstream jobs (keyed by job name, then by step name). For ctx.jobOutputs(). */
462
485
  upstreamJobOutputs?: Record<string, Record<string, unknown>>;
486
+ /** Terminal status of each upstream job (keyed by job name; per-child for fan-out). For ctx.needs.<job>.status. */
487
+ upstreamJobStatuses?: Record<string, import('@kici-dev/engine').ExecutionJobStatus>;
488
+ /** This job's declared upstream needs (normalized lock edges) used to shape ctx.needs for steps. */
489
+ jobNeeds?: readonly unknown[];
463
490
  /** Resolved private npm registries for `npm install` auth (token bytes already filled). */
464
491
  npmRegistries?: ReadonlyArray<{
465
492
  url: string;
@@ -6,6 +6,7 @@
6
6
  * module loading, and calls this loop for step execution with hooks.
7
7
  */
8
8
  import type { Step, StepContext, HookInput, OutputsMap, StepSecretMountRecord } from '@kici-dev/sdk';
9
+ import { CheckMode } from '@kici-dev/engine';
9
10
  import type { RunnerToAgentMessage } from './ipc-protocol.js';
10
11
  import type { SandboxStepResult } from './types.js';
11
12
  import { type CachePhaseDeps } from '../cache/index.js';
@@ -21,6 +22,11 @@ export interface JobHooks {
21
22
  /** Options for the step execution loop. */
22
23
  export interface StepLoopOptions {
23
24
  steps: Step[];
25
+ /**
26
+ * Run mode for idempotent steps. `apply` (default) converges; `check` /
27
+ * `check-fail-on-drift` preview drift and never invoke a checked step's apply.
28
+ */
29
+ checkMode?: CheckMode;
24
30
  /** Factory that creates a StepContext for a given step index and name. */
25
31
  createStepContext: (stepIndex: number, stepName: string) => StepContext;
26
32
  sendIpc: (msg: RunnerToAgentMessage) => void;
@@ -84,11 +90,11 @@ export interface StepLoopOptions {
84
90
  */
85
91
  afterStepApplyEnvFiles?: () => Promise<void>;
86
92
  /**
87
- * Block a `requireApproval` step pending an orchestrator-side approval hold.
88
- * The runner sends the normalized requirement and awaits the resolution; the
89
- * agent keeps job heartbeats flowing during the wait so the agent isn't
90
- * reaped. Absent ⇒ approvals are not gated (CT / unit harnesses) and steps
91
- * run unconditionally.
93
+ * Block an `approval` step (`when: 'always'`) pending an orchestrator-side
94
+ * approval hold. The runner sends the normalized requirement and awaits the
95
+ * resolution; the agent keeps job heartbeats flowing during the wait so the
96
+ * agent isn't reaped. Absent ⇒ approvals are not gated (CT / unit harnesses)
97
+ * and steps run unconditionally.
92
98
  */
93
99
  awaitStepApproval?: (req: {
94
100
  stepIndex: number;
@@ -101,6 +107,28 @@ export interface StepLoopOptions {
101
107
  reason: string;
102
108
  timeoutSeconds?: number;
103
109
  }) => Promise<StepApprovalResolution>;
110
+ /**
111
+ * Block an `approval: { when: 'drift' }` step mid-execution: after `check()`
112
+ * returns drift in apply mode, send a payload-bearing step-approval and await
113
+ * the resolution. The payload carries the computed drift (`summaryMarkdown` +
114
+ * structured `drift`) so the operator approves the actual diff. Absent ⇒ the
115
+ * drift gate is not enforced (CT / unit harnesses) and the step applies.
116
+ */
117
+ awaitStepApprovalWithPayload?: (req: {
118
+ stepIndex: number;
119
+ stepName: string;
120
+ clauses: Array<{
121
+ team: string;
122
+ } | {
123
+ user: string;
124
+ }>;
125
+ reason: string;
126
+ timeoutSeconds?: number;
127
+ payload: {
128
+ summaryMarkdown: string;
129
+ drift: unknown;
130
+ };
131
+ }) => Promise<StepApprovalResolution>;
104
132
  }
105
133
  /** Outcome of an awaited step-level approval hold. */
106
134
  export interface StepApprovalResolution {
@@ -13,10 +13,21 @@
13
13
  * This file is compiled alongside the agent by rolldown (existing build), but
14
14
  * runs as a SEPARATE process spawned by the sandbox backend.
15
15
  */
16
+ import { ExecutionJobStatus } from '@kici-dev/engine';
16
17
  import type { StepContext } from '@kici-dev/sdk';
18
+ import type { NeedsContext } from '@kici-dev/sdk';
17
19
  import type { OutputsMap, StepRefMap, TrackedStepSecrets } from '@kici-dev/sdk';
18
20
  import type { RunnerToAgentMessage, JobExecutionRequest } from './ipc-protocol.js';
19
21
  import { LogMasker } from './log-masker.js';
22
+ /**
23
+ * Build `ctx.needs` for a job's steps from the dispatch envelope. Reconstructs
24
+ * an {@link UpstreamSnapshot} from `upstreamJobOutputs` (flat per single job;
25
+ * `byMatrix` / `byHost` envelopes per fan-out) + `upstreamJobStatuses` (keyed by
26
+ * each upstream job/child name), then resolves the job's declared needs into the
27
+ * `{ result, status }` / ordered-array shape via the shared SDK builder. Returns
28
+ * undefined when the job declares no needs.
29
+ */
30
+ export declare function buildStepNeedsContext(declaredNeeds: readonly unknown[] | undefined, upstreamJobOutputs: Record<string, Record<string, unknown>> | undefined, upstreamJobStatuses: Record<string, ExecutionJobStatus> | undefined): NeedsContext | undefined;
20
31
  /**
21
32
  * Create a StepContext natively inside the workflow runner.
22
33
  *
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import { homedir, hostname, tmpdir } from "node:os";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  import { z } from "zod";
5
5
  import { LOGGER_ENV_VARS, defineEnv, validateUnknownKiciVars } from "@kici-dev/shared/env";
6
- import { KNOWN_ROLES, validateNoReservedLabels } from "@kici-dev/engine";
6
+ import { KNOWN_ROLES, parseHostPropertyAssignments, validateNoReservedLabels } from "@kici-dev/engine";
7
7
  import { execFile } from "node:child_process";
8
8
  import { access, cp, lstat, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, unlink, writeFile } from "node:fs/promises";
9
9
  import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
@@ -50,6 +50,7 @@ const envDef = defineEnv({
50
50
  orchestratorUrl: z.string().url().min(1, "KICI_ORCHESTRATOR_URL is required"),
51
51
  agentId: z.string().optional(),
52
52
  labels: z.string().default("").transform((s) => s.split(",").filter(Boolean)),
53
+ properties: z.string().default("").transform((s) => parseHostPropertyAssignments(s.split(",").filter(Boolean))),
53
54
  roles: z.string().optional().transform((s) => {
54
55
  if (s === void 0) return void 0;
55
56
  if (s === "") return [];
@@ -85,16 +86,19 @@ const envDef = defineEnv({
85
86
  scalerPendingDispatchTimeoutMs: z.coerce.number().default(6e4),
86
87
  executionMode: ExecutionMode.optional(),
87
88
  otelExporterOtlpEndpoint: z.string().optional(),
88
- concurrencyWaitTimeoutMs: z.coerce.number().int().min(1e3).default(36e5)
89
+ concurrencyWaitTimeoutMs: z.coerce.number().int().min(1e3).default(36e5),
90
+ isOrchestratorHost: z.string().optional().transform((v) => v === "true")
89
91
  }),
90
92
  envMap: {
91
93
  orchestratorUrl: "KICI_ORCHESTRATOR_URL",
92
94
  agentId: "KICI_AGENT_ID",
93
95
  labels: "KICI_LABELS",
96
+ properties: "KICI_PROPERTIES",
94
97
  roles: "KICI_ROLES",
95
98
  port: "KICI_PORT",
96
99
  logLevel: "KICI_LOG_LEVEL",
97
100
  agentToken: "KICI_AGENT_TOKEN",
101
+ isOrchestratorHost: "KICI_AGENT_IS_ORCHESTRATOR_HOST",
98
102
  githubToken: "KICI_GITHUB_TOKEN",
99
103
  maxLogSizeBytes: "KICI_MAX_LOG_SIZE_BYTES",
100
104
  defaultStepTimeoutMs: "KICI_DEFAULT_STEP_TIMEOUT_MS",
@@ -118,6 +122,7 @@ const envDef = defineEnv({
118
122
  * - KICI_ORCHESTRATOR_URL (required)
119
123
  * - KICI_AGENT_ID (optional, auto-generated from hostname-uuid8)
120
124
  * - KICI_LABELS (comma-separated, e.g. "linux,docker"). Labels with 'kici-' prefix are reserved.
125
+ * - KICI_PROPERTIES (comma-separated key=value host-vars, e.g. "region=eu,cores=8,gpu=true"). Typed (bool/number/string), reported into the host roster.
121
126
  * - KICI_ROLES (comma-separated agent roles, e.g. "builder,init-runner". undefined=all, empty=execution-only)
122
127
  * - KICI_PORT (default: 8080)
123
128
  * - KICI_LOG_LEVEL (default: info)