@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/mikka-mcp.mjs CHANGED
@@ -137,6 +137,7 @@ const TOOLS = [
137
137
  },
138
138
  spec: SPEC_SCHEMA,
139
139
  split: SPLIT_SCHEMA,
140
+ previewDatabaseBranch: { type: "string", description: "Name of the preview database branch you actually created or used; omit when none." },
140
141
  criteria: {
141
142
  type: "array",
142
143
  description:
@@ -149,6 +150,8 @@ const TOOLS = [
149
150
  description: "The criterion's id, as given in your brief.",
150
151
  },
151
152
  passed: { type: "boolean" },
153
+ command: { type: "string", description: "Exact command used to check this criterion, when one was run." },
154
+ exitCode: { type: ["integer", "null"], description: "Observed exit code of that command." },
152
155
  evidence: {
153
156
  type: "string",
154
157
  description:
@@ -332,6 +335,7 @@ function record(kind, args, toolName) {
332
335
  : {}),
333
336
  ...(plan ? { plan } : {}),
334
337
  // Kept as sent; the daemon validates and trims each entry.
338
+ ...(typeof args.previewDatabaseBranch === "string" ? { previewDatabaseBranch: args.previewDatabaseBranch } : {}),
335
339
  ...(Array.isArray(args.criteria) ? { criteria: args.criteria } : {}),
336
340
  at: new Date().toISOString(),
337
341
  }
package/mikka.js CHANGED
@@ -2217,7 +2217,7 @@ function closeLogger(logger) {
2217
2217
  // src/service.ts
2218
2218
  import { execFile } from "node:child_process";
2219
2219
  import { promisify } from "node:util";
2220
- import { mkdir, writeFile } from "node:fs/promises";
2220
+ import { mkdir, writeFile, rm } from "node:fs/promises";
2221
2221
  import { homedir } from "node:os";
2222
2222
  import { dirname as dirname2, join as join2, resolve } from "node:path";
2223
2223
  var exec = promisify(execFile);
@@ -2285,6 +2285,34 @@ async function restartService(update, beforeRestart, dependencies = {
2285
2285
  beforeRestart();
2286
2286
  dependencies.restart();
2287
2287
  }
2288
+ async function uninstallService(clearCredential, dependencies = {
2289
+ manager: process.env.MIKKA_SERVICE,
2290
+ home: homedir(),
2291
+ uid: process.getuid?.() ?? 0,
2292
+ run: async (program, args) => {
2293
+ await exec(program, args, { timeout: 1e4 });
2294
+ },
2295
+ exit: () => {
2296
+ process.exit(0);
2297
+ }
2298
+ }) {
2299
+ try {
2300
+ if (dependencies.manager === "systemd") {
2301
+ await dependencies.run("systemctl", ["--user", "disable", "mikka-daemon.service"]);
2302
+ await rm(join2(dependencies.home, ".config/systemd/user/mikka-daemon.service"), { force: true });
2303
+ await dependencies.run("systemctl", ["--user", "daemon-reload"]);
2304
+ } else if (dependencies.manager === "launchd") {
2305
+ await rm(join2(dependencies.home, "Library/LaunchAgents", `${label}.plist`), { force: true });
2306
+ }
2307
+ } finally {
2308
+ clearCredential();
2309
+ try {
2310
+ if (dependencies.manager === "launchd") await dependencies.run("launchctl", ["bootout", `gui/${dependencies.uid}/${label}`]);
2311
+ } finally {
2312
+ dependencies.exit();
2313
+ }
2314
+ }
2315
+ }
2288
2316
 
2289
2317
  // ../protocol/src/accounts.ts
2290
2318
  import { z } from "zod";
@@ -2972,13 +3000,18 @@ var runReport = z9.object({
2972
3000
  sha: z9.string().regex(/^[a-f0-9]{40}$/),
2973
3001
  readOnly: z9.literal(true)
2974
3002
  }).optional(),
3003
+ /** Preview branch created or used by the producing run; descriptive metadata only. */
3004
+ previewDatabaseBranch: z9.string().trim().min(1).max(500).optional(),
2975
3005
  /** Acceptance criteria the agent checked, with evidence. */
2976
3006
  criteria: z9.array(
2977
3007
  z9.object({
2978
3008
  criterionId: z9.uuid().optional(),
2979
3009
  text: z9.string().max(2e3).optional(),
2980
3010
  passed: z9.boolean(),
2981
- evidence: z9.string().max(1e4).optional()
3011
+ evidence: z9.string().max(1e4).optional(),
3012
+ /** Agent-reported criterion check; the daemon-owned verify remains authoritative. */
3013
+ command: z9.string().trim().min(1).max(2e3).optional(),
3014
+ exitCode: z9.number().int().nullable().optional()
2982
3015
  })
2983
3016
  ).max(200).optional(),
2984
3017
  /**
@@ -3322,7 +3355,7 @@ var heartbeatRunSchema = z13.object({
3322
3355
  /** Last tool call, turn boundary or file change (not raw output). */
3323
3356
  lastProgressAt: isoTime
3324
3357
  });
3325
- var deviceManagementCommand = z13.object({ id: z13.uuid(), kind: z13.enum(["redetect", "restart", "update"]) });
3358
+ var deviceManagementCommand = z13.object({ id: z13.uuid(), kind: z13.enum(["redetect", "restart", "update", "uninstall"]) });
3326
3359
  var deviceHeartbeatSchema = z13.object({
3327
3360
  completedDeviceCommands: z13.array(z13.uuid()).max(100).optional(),
3328
3361
  failedDeviceCommands: z13.array(z13.object({ id: z13.uuid(), error: z13.string().max(500) })).max(100).optional(),
@@ -4249,7 +4282,7 @@ import { join as join3 } from "node:path";
4249
4282
  // package.json
4250
4283
  var package_default = {
4251
4284
  name: "@mikka/daemon",
4252
- version: "0.0.1",
4285
+ version: "0.0.4",
4253
4286
  description: "Local Mikka daemon: runs agents through the user's own CLIs",
4254
4287
  type: "module",
4255
4288
  private: true,
@@ -4306,7 +4339,8 @@ var package_default = {
4306
4339
  },
4307
4340
  engines: {
4308
4341
  node: ">=24"
4309
- }
4342
+ },
4343
+ gitHead: "4c5f80747928d60ea3e03ed6a3de5ca2dc2216df"
4310
4344
  };
4311
4345
 
4312
4346
  // src/paths.ts
@@ -4945,12 +4979,28 @@ var HttpServerLink = class {
4945
4979
  const liveness = new AbortController();
4946
4980
  const gate = new ToolApprovalGate(assignment.runId, {
4947
4981
  permission: (request) => toolApprovalDecision(request, assignment.policy, worktree),
4982
+ // Keep the callback pending at the tool boundary. The adapter's current
4983
+ // session and checkpoint remain intact; handback resolves this same call.
4948
4984
  isLive: async () => {
4949
4985
  try {
4950
- check();
4951
- 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 });
4952
- check();
4953
- return current.live;
4986
+ for (; ; ) {
4987
+ check();
4988
+ 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 });
4989
+ check();
4990
+ if (!current.live || !current.paused) return current.live;
4991
+ await new Promise((resolve10, reject) => {
4992
+ const stop = () => {
4993
+ clearTimeout(timer);
4994
+ reject(new Error("cancelled"));
4995
+ };
4996
+ const timer = setTimeout(() => {
4997
+ liveness.signal.removeEventListener("abort", stop);
4998
+ resolve10();
4999
+ }, 1e3);
5000
+ liveness.signal.addEventListener("abort", stop, { once: true });
5001
+ if (liveness.signal.aborted) stop();
5002
+ });
5003
+ }
4954
5004
  } catch {
4955
5005
  return false;
4956
5006
  }
@@ -4979,9 +5029,11 @@ var HttpServerLink = class {
4979
5029
  }
4980
5030
  }
4981
5031
  };
5032
+ if (!await gate.checkLive()) return "deny";
4982
5033
  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 }));
4983
5034
  try {
4984
5035
  for (; ; ) {
5036
+ if (!await gate.checkLive()) return "deny";
4985
5037
  const result = await retry(() => this.call("POST", path, { body: { operation: "take", deviceId: assignment.deviceId, id: created.id, digest: created.digest }, schema: response, signal: signal2 }));
4986
5038
  if (result.decision !== "pending") return result.decision;
4987
5039
  await delay3();
@@ -6194,6 +6246,7 @@ var RunContext = class {
6194
6246
  nextStep: file.nextStep ?? null,
6195
6247
  ...file.blockedOn ? { blockedOn: file.blockedOn } : {},
6196
6248
  ...plan ? { plan } : {},
6249
+ ...typeof file.previewDatabaseBranch === "string" ? { previewDatabaseBranch: file.previewDatabaseBranch } : {},
6197
6250
  ...Array.isArray(file.criteria) ? { criteria: file.criteria } : {},
6198
6251
  ...runQuestion.safeParse(file.question).success ? { question: runQuestion.parse(file.question) } : {}
6199
6252
  };
@@ -6636,6 +6689,7 @@ import {
6636
6689
  existsSync as existsSync6,
6637
6690
  mkdirSync as mkdirSync15,
6638
6691
  readFileSync as readFileSync11,
6692
+ rmSync as rmSync16,
6639
6693
  realpathSync as realpathSync11
6640
6694
  } from "node:fs";
6641
6695
  import { arch, hostname, platform as platform2 } from "node:os";
@@ -12303,7 +12357,7 @@ var RunSupervisor = class {
12303
12357
  const current = this.deps.journal.get(runId);
12304
12358
  assertMailboxRunAuthority({ held: this.active.get(runId) === run2, state: current?.state, stopped: this.stopping || run2.cancelRequested || run2.abort.signal.aborted, lease });
12305
12359
  };
12306
- const mailbox = await this.deps.prepareRunMailbox?.(assignment, run2.abort.signal, checkMailbox);
12360
+ let mailbox = await this.deps.prepareRunMailbox?.(assignment, run2.abort.signal, checkMailbox);
12307
12361
  run2.mailbox = mailbox;
12308
12362
  if (this.stopping) return;
12309
12363
  if (run2.cancelRequested) {
@@ -12313,6 +12367,14 @@ var RunSupervisor = class {
12313
12367
  if (mailbox) checkMailbox();
12314
12368
  const toolApproval = await this.deps.prepareRunToolApproval?.(assignment, run2.abort.signal, checkMailbox, worktree);
12315
12369
  run2.toolApproval = toolApproval;
12370
+ if (mailbox && toolApproval) {
12371
+ const original = mailbox;
12372
+ mailbox = { tools: original.tools, close: () => original.close(), request: async (body, signal) => {
12373
+ if (!await toolApproval.checkLive()) throw new Error("Run tool approval unavailable");
12374
+ return original.request(body, signal);
12375
+ } };
12376
+ run2.mailbox = mailbox;
12377
+ }
12316
12378
  if (this.stopping) {
12317
12379
  toolApproval?.revoke();
12318
12380
  return;
@@ -12606,18 +12668,21 @@ function pickAgentReport(agent) {
12606
12668
  const question = runQuestion.safeParse(agent.question);
12607
12669
  if (question.success) picked.question = question.data;
12608
12670
  if (agent.plan) picked.plan = agent.plan;
12671
+ if (typeof agent.previewDatabaseBranch === "string" && agent.previewDatabaseBranch.trim())
12672
+ picked.previewDatabaseBranch = agent.previewDatabaseBranch.trim().slice(0, 500);
12609
12673
  if (Array.isArray(agent.criteria)) {
12610
12674
  const criteria = [];
12611
12675
  for (const entry of agent.criteria) {
12612
12676
  if (criteria.length >= MAX_REPORTED_CRITERIA) break;
12613
12677
  if (!entry || typeof entry !== "object") continue;
12614
- const { criterionId, passed, evidence } = entry;
12678
+ const { criterionId, passed, evidence, command, exitCode } = entry;
12615
12679
  if (typeof criterionId !== "string" || !UUID_RE.test(criterionId))
12616
12680
  continue;
12617
12681
  if (typeof passed !== "boolean") continue;
12618
12682
  criteria.push({
12619
12683
  criterionId,
12620
12684
  passed,
12685
+ ...typeof command === "string" && command.trim() ? { command: command.trim().slice(0, 2e3), ...exitCode === null || typeof exitCode === "number" && Number.isSafeInteger(exitCode) ? { exitCode } : {} } : {},
12621
12686
  ...typeof evidence === "string" ? { evidence: evidence.slice(0, 1e4) } : {}
12622
12687
  });
12623
12688
  }
@@ -12790,7 +12855,7 @@ import { promisify as promisify4 } from "node:util";
12790
12855
 
12791
12856
  // src/agents/public-egress.ts
12792
12857
  import { lookup } from "node:dns/promises";
12793
- import { chmod, mkdtemp, rm } from "node:fs/promises";
12858
+ import { chmod, mkdtemp, rm as rm2 } from "node:fs/promises";
12794
12859
  import { createServer as createServer3, request as httpRequest } from "node:http";
12795
12860
  import { connect, isIP as isIP3 } from "node:net";
12796
12861
  import { tmpdir as tmpdir3 } from "node:os";
@@ -12972,7 +13037,7 @@ async function startPublicEgress(options) {
12972
13037
  for (const socket of sockets) socket.destroy();
12973
13038
  closing = new Promise((resolve10, reject) => server.close((error) => {
12974
13039
  options.signal.removeEventListener("abort", onAbort);
12975
- void rm(directory, { recursive: true, force: true }).then(() => error && error.code !== "ERR_SERVER_NOT_RUNNING" ? reject(error) : resolve10(), reject);
13040
+ void rm2(directory, { recursive: true, force: true }).then(() => error && error.code !== "ERR_SERVER_NOT_RUNNING" ? reject(error) : resolve10(), reject);
12976
13041
  }));
12977
13042
  return closing;
12978
13043
  };
@@ -14778,7 +14843,7 @@ var Daemon = class {
14778
14843
  }
14779
14844
  const deviceId = this.device.deviceId;
14780
14845
  const quotas = this.quota?.snapshot().quotas;
14781
- const { leases, revoked, cloudCredential, deviceCommands } = await link.heartbeat(deviceId, {
14846
+ const heartbeat = {
14782
14847
  completedDeviceCommands: this.device.completedDeviceCommands,
14783
14848
  failedDeviceCommands: this.device.failedDeviceCommands,
14784
14849
  harnesses: this.harnesses,
@@ -14786,7 +14851,8 @@ var Daemon = class {
14786
14851
  runs: this.supervisor.heartbeatRuns(),
14787
14852
  worktreeDiskBytes: await this.worktrees.diskUsage(),
14788
14853
  ...quotas ? { quotas } : {}
14789
- });
14854
+ };
14855
+ const { leases, revoked, cloudCredential, deviceCommands } = await link.heartbeat(deviceId, heartbeat);
14790
14856
  this.supervisor.renewLeases(leases);
14791
14857
  if (revoked) this.supervisor.revokeLeases(revoked);
14792
14858
  if (this.options.cloudComputer && cloudCredential && this.device.credential === credential) {
@@ -14795,6 +14861,26 @@ var Daemon = class {
14795
14861
  this.registered = false;
14796
14862
  }
14797
14863
  const command = deviceCommands?.[0];
14864
+ if (command?.kind === "uninstall") {
14865
+ this.draining = true;
14866
+ if (this.supervisor.heartbeatRuns().length === 0) {
14867
+ this.saveDevice({ completedDeviceCommands: [command.id], failedDeviceCommands: [] });
14868
+ try {
14869
+ await link.heartbeat(deviceId, { ...heartbeat, runs: [], completedDeviceCommands: [command.id], failedDeviceCommands: [] });
14870
+ } catch {
14871
+ this.log.warn("Could not confirm uninstall receipt");
14872
+ }
14873
+ setImmediate(() => {
14874
+ void this.stop().then(() => uninstallService(() => {
14875
+ this.saveDevice({ credential: void 0 });
14876
+ rmSync16(this.paths.secret, { force: true });
14877
+ })).catch(() => {
14878
+ this.options.onDrained?.();
14879
+ });
14880
+ });
14881
+ }
14882
+ return;
14883
+ }
14798
14884
  if (command && !this.device.completedDeviceCommands?.includes(command.id) && !this.device.failedDeviceCommands?.some((failure) => failure.id === command.id)) {
14799
14885
  if (command.kind === "redetect" || this.supervisor.heartbeatRuns().length === 0) {
14800
14886
  try {
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@aspectlylabs/mikka",
3
- "version": "0.0.1",
3
+ "version": "0.0.4",
4
4
  "description": "Local Mikka daemon: runs agents through the user's own CLIs",
5
+ "gitHead": "4c5f80747928d60ea3e03ed6a3de5ca2dc2216df",
5
6
  "type": "module",
6
7
  "private": false,
7
8
  "license": "AGPL-3.0-only",
package/paths.js CHANGED
@@ -6,7 +6,7 @@ import { join } from "node:path";
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "@mikka/daemon",
9
- version: "0.0.1",
9
+ version: "0.0.4",
10
10
  description: "Local Mikka daemon: runs agents through the user's own CLIs",
11
11
  type: "module",
12
12
  private: true,
@@ -63,7 +63,8 @@ var package_default = {
63
63
  },
64
64
  engines: {
65
65
  node: ">=24"
66
- }
66
+ },
67
+ gitHead: "4c5f80747928d60ea3e03ed6a3de5ca2dc2216df"
67
68
  };
68
69
 
69
70
  // src/paths.ts