@aspectlylabs/mikka 0.0.1 → 0.0.4

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/README.md CHANGED
@@ -49,10 +49,26 @@ index for inspection. The test daemon still performs its normal lifecycle
49
49
  cleanup inside the new dedicated fixture; previously retained resources are
50
50
  not part of that fixture.
51
51
 
52
- No global `tsx` or separate `@mikka/protocol` package is needed. The proposed
53
- release workflow uses npm trusted publishing from GitHub Actions, with npm
54
- provenance. It is manual and must be configured for the approved package before
55
- it can publish. Running tests or building a package does not publish it.
52
+ No global `tsx` or separate `@mikka/protocol` package is needed. Every push to
53
+ `main` publishes through `.github/workflows/publish-daemon.yml` after daemon
54
+ checks and the installed-package runtime test pass. The workflow uses npm
55
+ trusted publishing and provenance; its npm trust configuration must authorize
56
+ this exact repository and workflow filename.
57
+
58
+ CI stamps `0.0.<workflow run number + 1>` into the source manifest before the
59
+ build, so the npm package and runtime report the same version. Source commits
60
+ keep their development version. Re-running a workflow keeps its version and
61
+ skips publication if npm already records that version from the same commit.
62
+ CI writes the commit SHA into the published manifest as `gitHead`; an existing
63
+ version with absent or different commit metadata stops the release. After
64
+ publication, CI reads npm metadata back and checks the commit SHA. If npm
65
+ serves 404 while the published version becomes available, CI checks up to 31
66
+ times with 10-second waits. Exhausting that wait reports registry verification
67
+ as incomplete; it does not claim that npm rejected publication.
68
+ Releases queue without cancellation (up to GitHub's 100 pending-run limit).
69
+ An older run can publish under its own `build-<version>` tag, preserving the
70
+ newer `latest` version. Manual dispatch remains available on `main` and gets
71
+ a new run version. Running local tests or builds does not publish anything.
56
72
 
57
73
  `test:package` installs the actual tarball into a new directory with public
58
74
  registry dependencies, without workspace symlinks or install scripts. Plain
package/cli.js CHANGED
@@ -2216,7 +2216,7 @@ function closeLogger(logger) {
2216
2216
  // src/service.ts
2217
2217
  import { execFile } from "node:child_process";
2218
2218
  import { promisify } from "node:util";
2219
- import { mkdir, writeFile } from "node:fs/promises";
2219
+ import { mkdir, writeFile, rm } from "node:fs/promises";
2220
2220
  import { homedir } from "node:os";
2221
2221
  import { dirname as dirname2, join as join2, resolve } from "node:path";
2222
2222
  var exec = promisify(execFile);
@@ -2284,6 +2284,34 @@ async function restartService(update, beforeRestart, dependencies = {
2284
2284
  beforeRestart();
2285
2285
  dependencies.restart();
2286
2286
  }
2287
+ async function uninstallService(clearCredential, dependencies = {
2288
+ manager: process.env.MIKKA_SERVICE,
2289
+ home: homedir(),
2290
+ uid: process.getuid?.() ?? 0,
2291
+ run: async (program, args) => {
2292
+ await exec(program, args, { timeout: 1e4 });
2293
+ },
2294
+ exit: () => {
2295
+ process.exit(0);
2296
+ }
2297
+ }) {
2298
+ try {
2299
+ if (dependencies.manager === "systemd") {
2300
+ await dependencies.run("systemctl", ["--user", "disable", "mikka-daemon.service"]);
2301
+ await rm(join2(dependencies.home, ".config/systemd/user/mikka-daemon.service"), { force: true });
2302
+ await dependencies.run("systemctl", ["--user", "daemon-reload"]);
2303
+ } else if (dependencies.manager === "launchd") {
2304
+ await rm(join2(dependencies.home, "Library/LaunchAgents", `${label}.plist`), { force: true });
2305
+ }
2306
+ } finally {
2307
+ clearCredential();
2308
+ try {
2309
+ if (dependencies.manager === "launchd") await dependencies.run("launchctl", ["bootout", `gui/${dependencies.uid}/${label}`]);
2310
+ } finally {
2311
+ dependencies.exit();
2312
+ }
2313
+ }
2314
+ }
2287
2315
 
2288
2316
  // ../protocol/src/accounts.ts
2289
2317
  import { z } from "zod";
@@ -2971,13 +2999,18 @@ var runReport = z9.object({
2971
2999
  sha: z9.string().regex(/^[a-f0-9]{40}$/),
2972
3000
  readOnly: z9.literal(true)
2973
3001
  }).optional(),
3002
+ /** Preview branch created or used by the producing run; descriptive metadata only. */
3003
+ previewDatabaseBranch: z9.string().trim().min(1).max(500).optional(),
2974
3004
  /** Acceptance criteria the agent checked, with evidence. */
2975
3005
  criteria: z9.array(
2976
3006
  z9.object({
2977
3007
  criterionId: z9.uuid().optional(),
2978
3008
  text: z9.string().max(2e3).optional(),
2979
3009
  passed: z9.boolean(),
2980
- evidence: z9.string().max(1e4).optional()
3010
+ evidence: z9.string().max(1e4).optional(),
3011
+ /** Agent-reported criterion check; the daemon-owned verify remains authoritative. */
3012
+ command: z9.string().trim().min(1).max(2e3).optional(),
3013
+ exitCode: z9.number().int().nullable().optional()
2981
3014
  })
2982
3015
  ).max(200).optional(),
2983
3016
  /**
@@ -3321,7 +3354,7 @@ var heartbeatRunSchema = z13.object({
3321
3354
  /** Last tool call, turn boundary or file change (not raw output). */
3322
3355
  lastProgressAt: isoTime
3323
3356
  });
3324
- var deviceManagementCommand = z13.object({ id: z13.uuid(), kind: z13.enum(["redetect", "restart", "update"]) });
3357
+ var deviceManagementCommand = z13.object({ id: z13.uuid(), kind: z13.enum(["redetect", "restart", "update", "uninstall"]) });
3325
3358
  var deviceHeartbeatSchema = z13.object({
3326
3359
  completedDeviceCommands: z13.array(z13.uuid()).max(100).optional(),
3327
3360
  failedDeviceCommands: z13.array(z13.object({ id: z13.uuid(), error: z13.string().max(500) })).max(100).optional(),
@@ -4248,7 +4281,7 @@ import { join as join3 } from "node:path";
4248
4281
  // package.json
4249
4282
  var package_default = {
4250
4283
  name: "@mikka/daemon",
4251
- version: "0.0.1",
4284
+ version: "0.0.4",
4252
4285
  description: "Local Mikka daemon: runs agents through the user's own CLIs",
4253
4286
  type: "module",
4254
4287
  private: true,
@@ -4305,7 +4338,8 @@ var package_default = {
4305
4338
  },
4306
4339
  engines: {
4307
4340
  node: ">=24"
4308
- }
4341
+ },
4342
+ gitHead: "4c5f80747928d60ea3e03ed6a3de5ca2dc2216df"
4309
4343
  };
4310
4344
 
4311
4345
  // src/paths.ts
@@ -4944,12 +4978,28 @@ var HttpServerLink = class {
4944
4978
  const liveness = new AbortController();
4945
4979
  const gate = new ToolApprovalGate(assignment.runId, {
4946
4980
  permission: (request) => toolApprovalDecision(request, assignment.policy, worktree),
4981
+ // Keep the callback pending at the tool boundary. The adapter's current
4982
+ // session and checkpoint remain intact; handback resolves this same call.
4947
4983
  isLive: async () => {
4948
4984
  try {
4949
- check();
4950
- const current = await this.call("POST", path, { body: { operation: "check", deviceId: assignment.deviceId, policyRevision }, schema: z25.object({ live: z25.boolean() }), signal: liveness.signal, timeoutMs: 5e3 });
4951
- check();
4952
- return current.live;
4985
+ for (; ; ) {
4986
+ check();
4987
+ const current = await this.call("POST", path, { body: { operation: "check", deviceId: assignment.deviceId, policyRevision }, schema: z25.object({ live: z25.boolean(), paused: z25.boolean().optional() }), signal: liveness.signal, timeoutMs: 5e3 });
4988
+ check();
4989
+ if (!current.live || !current.paused) return current.live;
4990
+ await new Promise((resolve10, reject) => {
4991
+ const stop = () => {
4992
+ clearTimeout(timer);
4993
+ reject(new Error("cancelled"));
4994
+ };
4995
+ const timer = setTimeout(() => {
4996
+ liveness.signal.removeEventListener("abort", stop);
4997
+ resolve10();
4998
+ }, 1e3);
4999
+ liveness.signal.addEventListener("abort", stop, { once: true });
5000
+ if (liveness.signal.aborted) stop();
5001
+ });
5002
+ }
4953
5003
  } catch {
4954
5004
  return false;
4955
5005
  }
@@ -4978,9 +5028,11 @@ var HttpServerLink = class {
4978
5028
  }
4979
5029
  }
4980
5030
  };
5031
+ if (!await gate.checkLive()) return "deny";
4981
5032
  const created = await retry(() => this.call("POST", path, { body: { operation: "propose", deviceId: assignment.deviceId, policyRevision, requestId: request.requestId, harness: request.harness, method: request.method, input: request.input }, schema: proposal, signal: signal2 }));
4982
5033
  try {
4983
5034
  for (; ; ) {
5035
+ if (!await gate.checkLive()) return "deny";
4984
5036
  const result = await retry(() => this.call("POST", path, { body: { operation: "take", deviceId: assignment.deviceId, id: created.id, digest: created.digest }, schema: response, signal: signal2 }));
4985
5037
  if (result.decision !== "pending") return result.decision;
4986
5038
  await delay3();
@@ -6193,6 +6245,7 @@ var RunContext = class {
6193
6245
  nextStep: file.nextStep ?? null,
6194
6246
  ...file.blockedOn ? { blockedOn: file.blockedOn } : {},
6195
6247
  ...plan ? { plan } : {},
6248
+ ...typeof file.previewDatabaseBranch === "string" ? { previewDatabaseBranch: file.previewDatabaseBranch } : {},
6196
6249
  ...Array.isArray(file.criteria) ? { criteria: file.criteria } : {},
6197
6250
  ...runQuestion.safeParse(file.question).success ? { question: runQuestion.parse(file.question) } : {}
6198
6251
  };
@@ -6635,6 +6688,7 @@ import {
6635
6688
  existsSync as existsSync6,
6636
6689
  mkdirSync as mkdirSync15,
6637
6690
  readFileSync as readFileSync11,
6691
+ rmSync as rmSync16,
6638
6692
  realpathSync as realpathSync11
6639
6693
  } from "node:fs";
6640
6694
  import { arch, hostname, platform as platform2 } from "node:os";
@@ -12302,7 +12356,7 @@ var RunSupervisor = class {
12302
12356
  const current = this.deps.journal.get(runId);
12303
12357
  assertMailboxRunAuthority({ held: this.active.get(runId) === run2, state: current?.state, stopped: this.stopping || run2.cancelRequested || run2.abort.signal.aborted, lease });
12304
12358
  };
12305
- const mailbox = await this.deps.prepareRunMailbox?.(assignment, run2.abort.signal, checkMailbox);
12359
+ let mailbox = await this.deps.prepareRunMailbox?.(assignment, run2.abort.signal, checkMailbox);
12306
12360
  run2.mailbox = mailbox;
12307
12361
  if (this.stopping) return;
12308
12362
  if (run2.cancelRequested) {
@@ -12312,6 +12366,14 @@ var RunSupervisor = class {
12312
12366
  if (mailbox) checkMailbox();
12313
12367
  const toolApproval = await this.deps.prepareRunToolApproval?.(assignment, run2.abort.signal, checkMailbox, worktree);
12314
12368
  run2.toolApproval = toolApproval;
12369
+ if (mailbox && toolApproval) {
12370
+ const original = mailbox;
12371
+ mailbox = { tools: original.tools, close: () => original.close(), request: async (body, signal) => {
12372
+ if (!await toolApproval.checkLive()) throw new Error("Run tool approval unavailable");
12373
+ return original.request(body, signal);
12374
+ } };
12375
+ run2.mailbox = mailbox;
12376
+ }
12315
12377
  if (this.stopping) {
12316
12378
  toolApproval?.revoke();
12317
12379
  return;
@@ -12605,18 +12667,21 @@ function pickAgentReport(agent) {
12605
12667
  const question = runQuestion.safeParse(agent.question);
12606
12668
  if (question.success) picked.question = question.data;
12607
12669
  if (agent.plan) picked.plan = agent.plan;
12670
+ if (typeof agent.previewDatabaseBranch === "string" && agent.previewDatabaseBranch.trim())
12671
+ picked.previewDatabaseBranch = agent.previewDatabaseBranch.trim().slice(0, 500);
12608
12672
  if (Array.isArray(agent.criteria)) {
12609
12673
  const criteria = [];
12610
12674
  for (const entry of agent.criteria) {
12611
12675
  if (criteria.length >= MAX_REPORTED_CRITERIA) break;
12612
12676
  if (!entry || typeof entry !== "object") continue;
12613
- const { criterionId, passed, evidence } = entry;
12677
+ const { criterionId, passed, evidence, command, exitCode } = entry;
12614
12678
  if (typeof criterionId !== "string" || !UUID_RE.test(criterionId))
12615
12679
  continue;
12616
12680
  if (typeof passed !== "boolean") continue;
12617
12681
  criteria.push({
12618
12682
  criterionId,
12619
12683
  passed,
12684
+ ...typeof command === "string" && command.trim() ? { command: command.trim().slice(0, 2e3), ...exitCode === null || typeof exitCode === "number" && Number.isSafeInteger(exitCode) ? { exitCode } : {} } : {},
12620
12685
  ...typeof evidence === "string" ? { evidence: evidence.slice(0, 1e4) } : {}
12621
12686
  });
12622
12687
  }
@@ -12789,7 +12854,7 @@ import { promisify as promisify4 } from "node:util";
12789
12854
 
12790
12855
  // src/agents/public-egress.ts
12791
12856
  import { lookup } from "node:dns/promises";
12792
- import { chmod, mkdtemp, rm } from "node:fs/promises";
12857
+ import { chmod, mkdtemp, rm as rm2 } from "node:fs/promises";
12793
12858
  import { createServer as createServer3, request as httpRequest } from "node:http";
12794
12859
  import { connect, isIP as isIP3 } from "node:net";
12795
12860
  import { tmpdir as tmpdir3 } from "node:os";
@@ -12971,7 +13036,7 @@ async function startPublicEgress(options) {
12971
13036
  for (const socket of sockets) socket.destroy();
12972
13037
  closing = new Promise((resolve10, reject) => server.close((error) => {
12973
13038
  options.signal.removeEventListener("abort", onAbort);
12974
- void rm(directory, { recursive: true, force: true }).then(() => error && error.code !== "ERR_SERVER_NOT_RUNNING" ? reject(error) : resolve10(), reject);
13039
+ void rm2(directory, { recursive: true, force: true }).then(() => error && error.code !== "ERR_SERVER_NOT_RUNNING" ? reject(error) : resolve10(), reject);
12975
13040
  }));
12976
13041
  return closing;
12977
13042
  };
@@ -14777,7 +14842,7 @@ var Daemon = class {
14777
14842
  }
14778
14843
  const deviceId = this.device.deviceId;
14779
14844
  const quotas = this.quota?.snapshot().quotas;
14780
- const { leases, revoked, cloudCredential, deviceCommands } = await link.heartbeat(deviceId, {
14845
+ const heartbeat = {
14781
14846
  completedDeviceCommands: this.device.completedDeviceCommands,
14782
14847
  failedDeviceCommands: this.device.failedDeviceCommands,
14783
14848
  harnesses: this.harnesses,
@@ -14785,7 +14850,8 @@ var Daemon = class {
14785
14850
  runs: this.supervisor.heartbeatRuns(),
14786
14851
  worktreeDiskBytes: await this.worktrees.diskUsage(),
14787
14852
  ...quotas ? { quotas } : {}
14788
- });
14853
+ };
14854
+ const { leases, revoked, cloudCredential, deviceCommands } = await link.heartbeat(deviceId, heartbeat);
14789
14855
  this.supervisor.renewLeases(leases);
14790
14856
  if (revoked) this.supervisor.revokeLeases(revoked);
14791
14857
  if (this.options.cloudComputer && cloudCredential && this.device.credential === credential) {
@@ -14794,6 +14860,26 @@ var Daemon = class {
14794
14860
  this.registered = false;
14795
14861
  }
14796
14862
  const command = deviceCommands?.[0];
14863
+ if (command?.kind === "uninstall") {
14864
+ this.draining = true;
14865
+ if (this.supervisor.heartbeatRuns().length === 0) {
14866
+ this.saveDevice({ completedDeviceCommands: [command.id], failedDeviceCommands: [] });
14867
+ try {
14868
+ await link.heartbeat(deviceId, { ...heartbeat, runs: [], completedDeviceCommands: [command.id], failedDeviceCommands: [] });
14869
+ } catch {
14870
+ this.log.warn("Could not confirm uninstall receipt");
14871
+ }
14872
+ setImmediate(() => {
14873
+ void this.stop().then(() => uninstallService(() => {
14874
+ this.saveDevice({ credential: void 0 });
14875
+ rmSync16(this.paths.secret, { force: true });
14876
+ })).catch(() => {
14877
+ this.options.onDrained?.();
14878
+ });
14879
+ });
14880
+ }
14881
+ return;
14882
+ }
14797
14883
  if (command && !this.device.completedDeviceCommands?.includes(command.id) && !this.device.failedDeviceCommands?.some((failure) => failure.id === command.id)) {
14798
14884
  if (command.kind === "redetect" || this.supervisor.heartbeatRuns().length === 0) {
14799
14885
  try {
package/client.js CHANGED
@@ -2547,13 +2547,18 @@ var runReport = z9.object({
2547
2547
  sha: z9.string().regex(/^[a-f0-9]{40}$/),
2548
2548
  readOnly: z9.literal(true)
2549
2549
  }).optional(),
2550
+ /** Preview branch created or used by the producing run; descriptive metadata only. */
2551
+ previewDatabaseBranch: z9.string().trim().min(1).max(500).optional(),
2550
2552
  /** Acceptance criteria the agent checked, with evidence. */
2551
2553
  criteria: z9.array(
2552
2554
  z9.object({
2553
2555
  criterionId: z9.uuid().optional(),
2554
2556
  text: z9.string().max(2e3).optional(),
2555
2557
  passed: z9.boolean(),
2556
- evidence: z9.string().max(1e4).optional()
2558
+ evidence: z9.string().max(1e4).optional(),
2559
+ /** Agent-reported criterion check; the daemon-owned verify remains authoritative. */
2560
+ command: z9.string().trim().min(1).max(2e3).optional(),
2561
+ exitCode: z9.number().int().nullable().optional()
2557
2562
  })
2558
2563
  ).max(200).optional(),
2559
2564
  /**
@@ -2809,7 +2814,7 @@ var heartbeatRunSchema = z13.object({
2809
2814
  /** Last tool call, turn boundary or file change (not raw output). */
2810
2815
  lastProgressAt: isoTime
2811
2816
  });
2812
- var deviceManagementCommand = z13.object({ id: z13.uuid(), kind: z13.enum(["redetect", "restart", "update"]) });
2817
+ var deviceManagementCommand = z13.object({ id: z13.uuid(), kind: z13.enum(["redetect", "restart", "update", "uninstall"]) });
2813
2818
  var deviceHeartbeatSchema = z13.object({
2814
2819
  completedDeviceCommands: z13.array(z13.uuid()).max(100).optional(),
2815
2820
  failedDeviceCommands: z13.array(z13.object({ id: z13.uuid(), error: z13.string().max(500) })).max(100).optional(),
@@ -3616,7 +3621,7 @@ import { join } from "node:path";
3616
3621
  // package.json
3617
3622
  var package_default = {
3618
3623
  name: "@mikka/daemon",
3619
- version: "0.0.1",
3624
+ version: "0.0.4",
3620
3625
  description: "Local Mikka daemon: runs agents through the user's own CLIs",
3621
3626
  type: "module",
3622
3627
  private: true,
@@ -3673,7 +3678,8 @@ var package_default = {
3673
3678
  },
3674
3679
  engines: {
3675
3680
  node: ">=24"
3676
- }
3681
+ },
3682
+ gitHead: "4c5f80747928d60ea3e03ed6a3de5ca2dc2216df"
3677
3683
  };
3678
3684
 
3679
3685
  // src/paths.ts
package/http-link.js CHANGED
@@ -2993,13 +2993,18 @@ var runReport = z15.object({
2993
2993
  sha: z15.string().regex(/^[a-f0-9]{40}$/),
2994
2994
  readOnly: z15.literal(true)
2995
2995
  }).optional(),
2996
+ /** Preview branch created or used by the producing run; descriptive metadata only. */
2997
+ previewDatabaseBranch: z15.string().trim().min(1).max(500).optional(),
2996
2998
  /** Acceptance criteria the agent checked, with evidence. */
2997
2999
  criteria: z15.array(
2998
3000
  z15.object({
2999
3001
  criterionId: z15.uuid().optional(),
3000
3002
  text: z15.string().max(2e3).optional(),
3001
3003
  passed: z15.boolean(),
3002
- evidence: z15.string().max(1e4).optional()
3004
+ evidence: z15.string().max(1e4).optional(),
3005
+ /** Agent-reported criterion check; the daemon-owned verify remains authoritative. */
3006
+ command: z15.string().trim().min(1).max(2e3).optional(),
3007
+ exitCode: z15.number().int().nullable().optional()
3003
3008
  })
3004
3009
  ).max(200).optional(),
3005
3010
  /**
@@ -3217,7 +3222,7 @@ var heartbeatRunSchema = z17.object({
3217
3222
  /** Last tool call, turn boundary or file change (not raw output). */
3218
3223
  lastProgressAt: isoTime
3219
3224
  });
3220
- var deviceManagementCommand = z17.object({ id: z17.uuid(), kind: z17.enum(["redetect", "restart", "update"]) });
3225
+ var deviceManagementCommand = z17.object({ id: z17.uuid(), kind: z17.enum(["redetect", "restart", "update", "uninstall"]) });
3221
3226
  var deviceHeartbeatSchema = z17.object({
3222
3227
  completedDeviceCommands: z17.array(z17.uuid()).max(100).optional(),
3223
3228
  failedDeviceCommands: z17.array(z17.object({ id: z17.uuid(), error: z17.string().max(500) })).max(100).optional(),
@@ -3916,7 +3921,7 @@ var InvalidIdError = class extends DaemonError {
3916
3921
  // package.json
3917
3922
  var package_default = {
3918
3923
  name: "@mikka/daemon",
3919
- version: "0.0.1",
3924
+ version: "0.0.4",
3920
3925
  description: "Local Mikka daemon: runs agents through the user's own CLIs",
3921
3926
  type: "module",
3922
3927
  private: true,
@@ -3973,7 +3978,8 @@ var package_default = {
3973
3978
  },
3974
3979
  engines: {
3975
3980
  node: ">=24"
3976
- }
3981
+ },
3982
+ gitHead: "4c5f80747928d60ea3e03ed6a3de5ca2dc2216df"
3977
3983
  };
3978
3984
 
3979
3985
  // src/paths.ts
@@ -4170,12 +4176,28 @@ var HttpServerLink = class {
4170
4176
  const liveness = new AbortController();
4171
4177
  const gate = new ToolApprovalGate(assignment.runId, {
4172
4178
  permission: (request) => toolApprovalDecision(request, assignment.policy, worktree),
4179
+ // Keep the callback pending at the tool boundary. The adapter's current
4180
+ // session and checkpoint remain intact; handback resolves this same call.
4173
4181
  isLive: async () => {
4174
4182
  try {
4175
- check();
4176
- const current = await this.call("POST", path, { body: { operation: "check", deviceId: assignment.deviceId, policyRevision }, schema: z25.object({ live: z25.boolean() }), signal: liveness.signal, timeoutMs: 5e3 });
4177
- check();
4178
- return current.live;
4183
+ for (; ; ) {
4184
+ check();
4185
+ const current = await this.call("POST", path, { body: { operation: "check", deviceId: assignment.deviceId, policyRevision }, schema: z25.object({ live: z25.boolean(), paused: z25.boolean().optional() }), signal: liveness.signal, timeoutMs: 5e3 });
4186
+ check();
4187
+ if (!current.live || !current.paused) return current.live;
4188
+ await new Promise((resolve2, reject) => {
4189
+ const stop = () => {
4190
+ clearTimeout(timer);
4191
+ reject(new Error("cancelled"));
4192
+ };
4193
+ const timer = setTimeout(() => {
4194
+ liveness.signal.removeEventListener("abort", stop);
4195
+ resolve2();
4196
+ }, 1e3);
4197
+ liveness.signal.addEventListener("abort", stop, { once: true });
4198
+ if (liveness.signal.aborted) stop();
4199
+ });
4200
+ }
4179
4201
  } catch {
4180
4202
  return false;
4181
4203
  }
@@ -4204,9 +4226,11 @@ var HttpServerLink = class {
4204
4226
  }
4205
4227
  }
4206
4228
  };
4229
+ if (!await gate.checkLive()) return "deny";
4207
4230
  const created = await retry(() => this.call("POST", path, { body: { operation: "propose", deviceId: assignment.deviceId, policyRevision, requestId: request.requestId, harness: request.harness, method: request.method, input: request.input }, schema: proposal, signal: signal2 }));
4208
4231
  try {
4209
4232
  for (; ; ) {
4233
+ if (!await gate.checkLive()) return "deny";
4210
4234
  const result = await retry(() => this.call("POST", path, { body: { operation: "take", deviceId: assignment.deviceId, id: created.id, digest: created.digest }, schema: response, signal: signal2 }));
4211
4235
  if (result.decision !== "pending") return result.decision;
4212
4236
  await delay();
package/login.js CHANGED
@@ -2544,13 +2544,18 @@ var runReport = z9.object({
2544
2544
  sha: z9.string().regex(/^[a-f0-9]{40}$/),
2545
2545
  readOnly: z9.literal(true)
2546
2546
  }).optional(),
2547
+ /** Preview branch created or used by the producing run; descriptive metadata only. */
2548
+ previewDatabaseBranch: z9.string().trim().min(1).max(500).optional(),
2547
2549
  /** Acceptance criteria the agent checked, with evidence. */
2548
2550
  criteria: z9.array(
2549
2551
  z9.object({
2550
2552
  criterionId: z9.uuid().optional(),
2551
2553
  text: z9.string().max(2e3).optional(),
2552
2554
  passed: z9.boolean(),
2553
- evidence: z9.string().max(1e4).optional()
2555
+ evidence: z9.string().max(1e4).optional(),
2556
+ /** Agent-reported criterion check; the daemon-owned verify remains authoritative. */
2557
+ command: z9.string().trim().min(1).max(2e3).optional(),
2558
+ exitCode: z9.number().int().nullable().optional()
2554
2559
  })
2555
2560
  ).max(200).optional(),
2556
2561
  /**
@@ -2805,7 +2810,7 @@ var heartbeatRunSchema = z13.object({
2805
2810
  /** Last tool call, turn boundary or file change (not raw output). */
2806
2811
  lastProgressAt: isoTime
2807
2812
  });
2808
- var deviceManagementCommand = z13.object({ id: z13.uuid(), kind: z13.enum(["redetect", "restart", "update"]) });
2813
+ var deviceManagementCommand = z13.object({ id: z13.uuid(), kind: z13.enum(["redetect", "restart", "update", "uninstall"]) });
2809
2814
  var deviceHeartbeatSchema = z13.object({
2810
2815
  completedDeviceCommands: z13.array(z13.uuid()).max(100).optional(),
2811
2816
  failedDeviceCommands: z13.array(z13.object({ id: z13.uuid(), error: z13.string().max(500) })).max(100).optional(),
@@ -3623,7 +3628,7 @@ var ServerUnreachableError = class extends DaemonError {
3623
3628
  // package.json
3624
3629
  var package_default = {
3625
3630
  name: "@mikka/daemon",
3626
- version: "0.0.1",
3631
+ version: "0.0.4",
3627
3632
  description: "Local Mikka daemon: runs agents through the user's own CLIs",
3628
3633
  type: "module",
3629
3634
  private: true,
@@ -3680,7 +3685,8 @@ var package_default = {
3680
3685
  },
3681
3686
  engines: {
3682
3687
  node: ">=24"
3683
- }
3688
+ },
3689
+ gitHead: "4c5f80747928d60ea3e03ed6a3de5ca2dc2216df"
3684
3690
  };
3685
3691
 
3686
3692
  // src/paths.ts