@hasna/todos 0.11.66 → 0.11.68

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.
Files changed (47) hide show
  1. package/README.md +2 -2
  2. package/dashboard/dist/assets/{index-aJefI7kh.js → index-DVotjwab.js} +1 -1
  3. package/dashboard/dist/index.html +1 -1
  4. package/dist/cli/commands/project-commands.d.ts.map +1 -1
  5. package/dist/cli/commands/storage-commands.d.ts +5 -0
  6. package/dist/cli/commands/storage-commands.d.ts.map +1 -1
  7. package/dist/cli/index.js +8811 -2900
  8. package/dist/contracts.js +301 -71
  9. package/dist/db/agents.d.ts.map +1 -1
  10. package/dist/db/audit.d.ts.map +1 -1
  11. package/dist/db/migrations.d.ts.map +1 -1
  12. package/dist/db/plans.d.ts.map +1 -1
  13. package/dist/db/projects.d.ts.map +1 -1
  14. package/dist/db/schema.d.ts.map +1 -1
  15. package/dist/db/storage-tombstones.d.ts +26 -0
  16. package/dist/db/storage-tombstones.d.ts.map +1 -0
  17. package/dist/db/task-crud.d.ts.map +1 -1
  18. package/dist/db/task-lifecycle.d.ts.map +1 -1
  19. package/dist/db/task-lists.d.ts.map +1 -1
  20. package/dist/db/task-runs.d.ts.map +1 -1
  21. package/dist/db/templates.d.ts.map +1 -1
  22. package/dist/index.js +628 -160
  23. package/dist/lib/approval-gates.d.ts.map +1 -1
  24. package/dist/lib/event-emission-safety.d.ts +9 -0
  25. package/dist/lib/event-emission-safety.d.ts.map +1 -0
  26. package/dist/lib/event-hooks.d.ts +1 -0
  27. package/dist/lib/event-hooks.d.ts.map +1 -1
  28. package/dist/lib/feature-manifest.d.ts.map +1 -1
  29. package/dist/lib/project-panel.d.ts +8 -0
  30. package/dist/lib/project-panel.d.ts.map +1 -0
  31. package/dist/lib/review-queues.d.ts.map +1 -1
  32. package/dist/lib/shared-events.d.ts +1 -0
  33. package/dist/lib/shared-events.d.ts.map +1 -1
  34. package/dist/mcp/index.js +343 -104
  35. package/dist/registry.js +301 -71
  36. package/dist/release-provenance.json +3 -3
  37. package/dist/server/index.js +366 -127
  38. package/dist/storage/interfaces.d.ts +20 -0
  39. package/dist/storage/interfaces.d.ts.map +1 -1
  40. package/dist/storage/postgres-adapter.d.ts.map +1 -1
  41. package/dist/storage/postgres-sync.d.ts +6 -1
  42. package/dist/storage/postgres-sync.d.ts.map +1 -1
  43. package/dist/storage/sqlite-snapshot.d.ts.map +1 -1
  44. package/dist/storage.js +569 -94
  45. package/dist/types/index.d.ts +22 -0
  46. package/dist/types/index.d.ts.map +1 -1
  47. package/package.json +2 -1
@@ -1232,6 +1232,22 @@ var init_migrations = __esm(() => {
1232
1232
  CREATE INDEX IF NOT EXISTS idx_task_findings_source ON task_findings(source);
1233
1233
  CREATE INDEX IF NOT EXISTS idx_task_findings_fingerprint ON task_findings(fingerprint);
1234
1234
  INSERT OR IGNORE INTO _migrations (id) VALUES (62);
1235
+ `,
1236
+ `
1237
+ CREATE TABLE IF NOT EXISTS storage_tombstones (
1238
+ id TEXT PRIMARY KEY,
1239
+ object_type TEXT NOT NULL,
1240
+ object_id TEXT NOT NULL,
1241
+ deleted_at TEXT NOT NULL,
1242
+ updated_at TEXT NOT NULL,
1243
+ source_machine_id TEXT,
1244
+ payload TEXT,
1245
+ version INTEGER,
1246
+ UNIQUE(object_type, object_id)
1247
+ );
1248
+ CREATE INDEX IF NOT EXISTS idx_storage_tombstones_object ON storage_tombstones(object_type, object_id);
1249
+ CREATE INDEX IF NOT EXISTS idx_storage_tombstones_updated ON storage_tombstones(updated_at);
1250
+ INSERT OR IGNORE INTO _migrations (id) VALUES (63);
1235
1251
  `
1236
1252
  ];
1237
1253
  });
@@ -1753,6 +1769,20 @@ function ensureSchema(db) {
1753
1769
  )`);
1754
1770
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_machine_paths_project ON project_machine_paths(project_id)");
1755
1771
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_machine_paths_machine ON project_machine_paths(machine_id)");
1772
+ ensureTable("storage_tombstones", `
1773
+ CREATE TABLE storage_tombstones (
1774
+ id TEXT PRIMARY KEY,
1775
+ object_type TEXT NOT NULL,
1776
+ object_id TEXT NOT NULL,
1777
+ deleted_at TEXT NOT NULL,
1778
+ updated_at TEXT NOT NULL,
1779
+ source_machine_id TEXT,
1780
+ payload TEXT,
1781
+ version INTEGER,
1782
+ UNIQUE(object_type, object_id)
1783
+ )`);
1784
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_storage_tombstones_object ON storage_tombstones(object_type, object_id)");
1785
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_storage_tombstones_updated ON storage_tombstones(updated_at)");
1756
1786
  ensureTable("machines", `
1757
1787
  CREATE TABLE machines (
1758
1788
  id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, hostname TEXT, platform TEXT,
@@ -3077,6 +3107,65 @@ var init_config = __esm(() => {
3077
3107
  };
3078
3108
  });
3079
3109
 
3110
+ // src/db/storage-tombstones.ts
3111
+ function recordStorageTombstone(input, db) {
3112
+ const d = db ?? getDatabase();
3113
+ const deletedAt = input.deleted_at ?? now();
3114
+ const machineId = input.source_machine_id ?? currentStorageMachineId(d);
3115
+ d.run(`INSERT INTO storage_tombstones (
3116
+ id, object_type, object_id, deleted_at, updated_at, source_machine_id, payload, version
3117
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
3118
+ ON CONFLICT(object_type, object_id) DO UPDATE SET
3119
+ deleted_at = excluded.deleted_at,
3120
+ updated_at = excluded.updated_at,
3121
+ source_machine_id = excluded.source_machine_id,
3122
+ payload = excluded.payload,
3123
+ version = excluded.version
3124
+ WHERE storage_tombstones.updated_at IS NULL OR storage_tombstones.updated_at <= excluded.updated_at`, [
3125
+ uuid(),
3126
+ input.object_type,
3127
+ input.object_id,
3128
+ deletedAt,
3129
+ deletedAt,
3130
+ machineId,
3131
+ input.payload ? JSON.stringify(input.payload) : null,
3132
+ input.version ?? null
3133
+ ]);
3134
+ return getStorageTombstone(input.object_type, input.object_id, d);
3135
+ }
3136
+ function getStorageTombstone(objectType, objectId, db) {
3137
+ const d = db ?? getDatabase();
3138
+ const row = d.query("SELECT * FROM storage_tombstones WHERE object_type = ? AND object_id = ?").get(objectType, objectId);
3139
+ return row ? rowToStorageTombstone(row) : null;
3140
+ }
3141
+ function rowToStorageTombstone(row) {
3142
+ return {
3143
+ ...row,
3144
+ payload: parsePayload(row.payload)
3145
+ };
3146
+ }
3147
+ function parsePayload(value) {
3148
+ if (!value)
3149
+ return null;
3150
+ try {
3151
+ const parsed = JSON.parse(value);
3152
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
3153
+ } catch {
3154
+ return null;
3155
+ }
3156
+ }
3157
+ function currentStorageMachineId(db) {
3158
+ try {
3159
+ return getMachineId(db);
3160
+ } catch {
3161
+ return null;
3162
+ }
3163
+ }
3164
+ var init_storage_tombstones = __esm(() => {
3165
+ init_database();
3166
+ init_machines();
3167
+ });
3168
+
3080
3169
  // src/db/projects.ts
3081
3170
  var exports_projects = {};
3082
3171
  __export(exports_projects, {
@@ -3128,8 +3217,9 @@ function createProject(input, db) {
3128
3217
  const timestamp = now();
3129
3218
  const taskListId = input.task_list_id ?? `todos-${slugify(input.name)}`;
3130
3219
  const taskPrefix = input.task_prefix || generatePrefix(input.name, d);
3131
- d.run(`INSERT INTO projects (id, name, path, description, task_list_id, task_prefix, task_counter, created_at, updated_at)
3132
- VALUES (?, ?, ?, ?, ?, ?, 0, ?, ?)`, [id, input.name, input.path, input.description || null, taskListId, taskPrefix, timestamp, timestamp]);
3220
+ const machineId = currentStorageMachineId(d);
3221
+ d.run(`INSERT INTO projects (id, name, path, description, task_list_id, task_prefix, task_counter, created_at, updated_at, machine_id)
3222
+ VALUES (?, ?, ?, ?, ?, ?, 0, ?, ?, ?)`, [id, input.name, input.path, input.description || null, taskListId, taskPrefix, timestamp, timestamp, machineId]);
3133
3223
  return getProject(id, d);
3134
3224
  }
3135
3225
  function getProject(id, db) {
@@ -3208,6 +3298,14 @@ function renameProject(id, input, db) {
3208
3298
  }
3209
3299
  function deleteProject(id, db) {
3210
3300
  const d = db || getDatabase();
3301
+ const project = getProject(id, d);
3302
+ if (!project)
3303
+ return false;
3304
+ recordStorageTombstone({
3305
+ object_type: "projects",
3306
+ object_id: id,
3307
+ payload: project
3308
+ }, d);
3211
3309
  const result = d.run("DELETE FROM projects WHERE id = ?", [id]);
3212
3310
  return result.changes > 0;
3213
3311
  }
@@ -3312,6 +3410,14 @@ function listMachineLocalPaths(projectId, db) {
3312
3410
  function removeMachineLocalPath(projectId, machineId, db) {
3313
3411
  const d = db || getDatabase();
3314
3412
  const mid = machineId ?? getMachineId(d);
3413
+ const existing = d.query("SELECT * FROM project_machine_paths WHERE project_id = ? AND machine_id = ?").get(projectId, mid);
3414
+ if (!existing)
3415
+ return false;
3416
+ recordStorageTombstone({
3417
+ object_type: "project_machine_paths",
3418
+ object_id: existing.id,
3419
+ payload: existing
3420
+ }, d);
3315
3421
  const result = d.run("DELETE FROM project_machine_paths WHERE project_id = ? AND machine_id = ?", [projectId, mid]);
3316
3422
  return result.changes > 0;
3317
3423
  }
@@ -3319,6 +3425,7 @@ var init_projects = __esm(() => {
3319
3425
  init_types();
3320
3426
  init_database();
3321
3427
  init_machines();
3428
+ init_storage_tombstones();
3322
3429
  });
3323
3430
 
3324
3431
  // src/lib/completion-guard.ts
@@ -3370,6 +3477,57 @@ var init_completion_guard = __esm(() => {
3370
3477
  init_projects();
3371
3478
  });
3372
3479
 
3480
+ // src/lib/event-emission-safety.ts
3481
+ import { tmpdir } from "os";
3482
+ import { resolve as resolve3, sep } from "path";
3483
+ function envFlag(name) {
3484
+ const value = process.env[name];
3485
+ return value === "1" || value === "true" || value === "yes";
3486
+ }
3487
+ function isUnder(parent, child) {
3488
+ const normalizedParent = resolve3(parent);
3489
+ const normalizedChild = resolve3(child);
3490
+ return normalizedChild === normalizedParent || normalizedChild.startsWith(`${normalizedParent}${sep}`);
3491
+ }
3492
+ function databasePathFromDatabase(db) {
3493
+ const filename = db?.filename;
3494
+ return typeof filename === "string" && filename.trim() ? filename : undefined;
3495
+ }
3496
+ function isEphemeralTodosDatabase(dbPath) {
3497
+ const resolvedPath = dbPath ?? getDatabasePath();
3498
+ if (resolvedPath === ":memory:" || resolvedPath.startsWith("file::memory:"))
3499
+ return true;
3500
+ return isUnder(tmpdir(), resolvedPath);
3501
+ }
3502
+ function hasExplicitSharedEventsStore() {
3503
+ return Boolean(process.env["HASNA_EVENTS_DIR"] || process.env["HASNA_EVENTS_HOME"]);
3504
+ }
3505
+ function usesIsolatedTodosHome() {
3506
+ return isUnder(tmpdir(), getTodosGlobalDir());
3507
+ }
3508
+ function shouldEmitSharedTaskEvents(dbPath) {
3509
+ if (envFlag("TODOS_DISABLE_SHARED_EVENTS"))
3510
+ return false;
3511
+ if (envFlag("TODOS_ALLOW_EPHEMERAL_SHARED_EVENTS"))
3512
+ return true;
3513
+ if (!isEphemeralTodosDatabase(dbPath))
3514
+ return true;
3515
+ return hasExplicitSharedEventsStore();
3516
+ }
3517
+ function shouldDeliverLocalLifecycleHooks(dbPath) {
3518
+ if (envFlag("TODOS_DISABLE_LOCAL_EVENT_HOOKS"))
3519
+ return false;
3520
+ if (envFlag("TODOS_ALLOW_EPHEMERAL_LOCAL_EVENT_HOOKS"))
3521
+ return true;
3522
+ if (!isEphemeralTodosDatabase(dbPath))
3523
+ return true;
3524
+ return usesIsolatedTodosHome();
3525
+ }
3526
+ var init_event_emission_safety = __esm(() => {
3527
+ init_database();
3528
+ init_sync_utils();
3529
+ });
3530
+
3373
3531
  // src/lib/redaction.ts
3374
3532
  function unique(values) {
3375
3533
  return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
@@ -3470,9 +3628,9 @@ var init_redaction = __esm(() => {
3470
3628
  });
3471
3629
 
3472
3630
  // src/lib/workspace-trust.ts
3473
- import { relative, resolve as resolve3 } from "path";
3631
+ import { relative, resolve as resolve4 } from "path";
3474
3632
  function normalizePath(path) {
3475
- return resolve3(path);
3633
+ return resolve4(path);
3476
3634
  }
3477
3635
  function unique2(values) {
3478
3636
  return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
@@ -3654,9 +3812,9 @@ var init_workspace_trust = __esm(() => {
3654
3812
  });
3655
3813
 
3656
3814
  // src/lib/runner-sandbox.ts
3657
- import { relative as relative2, resolve as resolve4 } from "path";
3815
+ import { relative as relative2, resolve as resolve5 } from "path";
3658
3816
  function normalizePath2(path) {
3659
- return resolve4(path);
3817
+ return resolve5(path);
3660
3818
  }
3661
3819
  function unique3(values) {
3662
3820
  return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
@@ -3846,7 +4004,7 @@ var init_runner_sandbox = __esm(() => {
3846
4004
  // src/lib/event-hooks.ts
3847
4005
  import { createHash as createHash2, randomUUID } from "crypto";
3848
4006
  import { appendFileSync, mkdirSync as mkdirSync3 } from "fs";
3849
- import { dirname as dirname4, resolve as resolve5 } from "path";
4007
+ import { dirname as dirname4, resolve as resolve6 } from "path";
3850
4008
  import { createConnection } from "net";
3851
4009
  function safeName(name) {
3852
4010
  const trimmed = name.trim();
@@ -3984,7 +4142,7 @@ async function deliverHook(hook, envelope) {
3984
4142
  if (hook.target === "stdout") {
3985
4143
  output = line.trim();
3986
4144
  } else if (hook.target === "file") {
3987
- const filePath = resolve5(hook.file_path);
4145
+ const filePath = resolve6(hook.file_path);
3988
4146
  mkdirSync3(dirname4(filePath), { recursive: true });
3989
4147
  appendFileSync(filePath, line);
3990
4148
  } else if (hook.target === "socket") {
@@ -4059,6 +4217,8 @@ async function emitLocalEventHooks(input) {
4059
4217
  return Promise.all(hooks.map((hook) => deliverHook(hook, envelope)));
4060
4218
  }
4061
4219
  function emitLocalEventHooksQuiet(input) {
4220
+ if (!shouldDeliverLocalLifecycleHooks(input.databasePath))
4221
+ return;
4062
4222
  emitLocalEventHooks(input).catch(() => {});
4063
4223
  }
4064
4224
  async function testLocalEventHook(name, input) {
@@ -4072,6 +4232,7 @@ var init_event_hooks = __esm(() => {
4072
4232
  init_redaction();
4073
4233
  init_runner_sandbox();
4074
4234
  init_config();
4235
+ init_event_emission_safety();
4075
4236
  LOCAL_EVENT_TYPES = [
4076
4237
  "task.created",
4077
4238
  "task.assigned",
@@ -4404,7 +4565,7 @@ async function dispatchCommand(event, channel) {
4404
4565
  HASNA_EVENT_SCHEMA_VERSION: event.schemaVersion,
4405
4566
  HASNA_EVENT_JSON: eventJson
4406
4567
  };
4407
- return new Promise((resolve6) => {
4568
+ return new Promise((resolve7) => {
4408
4569
  const child = spawn(channel.command.command, channel.command.args ?? [], {
4409
4570
  cwd: channel.command.cwd,
4410
4571
  env,
@@ -4422,7 +4583,7 @@ async function dispatchCommand(event, channel) {
4422
4583
  });
4423
4584
  child.on("error", (error) => {
4424
4585
  clearTimeout(timeout);
4425
- resolve6({
4586
+ resolve7({
4426
4587
  attempt: 1,
4427
4588
  status: "failed",
4428
4589
  startedAt,
@@ -4435,7 +4596,7 @@ async function dispatchCommand(event, channel) {
4435
4596
  child.on("close", (code, signal) => {
4436
4597
  clearTimeout(timeout);
4437
4598
  const success = code === 0;
4438
- resolve6({
4599
+ resolve7({
4439
4600
  attempt: 1,
4440
4601
  status: success ? "success" : "failed",
4441
4602
  startedAt,
@@ -4702,14 +4863,15 @@ function createTaskList(input, db) {
4702
4863
  const id = uuid();
4703
4864
  const timestamp = now();
4704
4865
  const slug = input.slug || slugify(input.name);
4866
+ const machineId = currentStorageMachineId(d);
4705
4867
  if (!input.project_id) {
4706
4868
  const existing = d.query("SELECT id FROM task_lists WHERE project_id IS NULL AND slug = ?").get(slug);
4707
4869
  if (existing) {
4708
4870
  throw new Error(`Standalone task list with slug "${slug}" already exists`);
4709
4871
  }
4710
4872
  }
4711
- d.run(`INSERT INTO task_lists (id, project_id, slug, name, description, metadata, created_at, updated_at)
4712
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [id, input.project_id || null, slug, input.name, input.description || null, JSON.stringify(input.metadata || {}), timestamp, timestamp]);
4873
+ d.run(`INSERT INTO task_lists (id, project_id, slug, name, description, metadata, created_at, updated_at, machine_id)
4874
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [id, input.project_id || null, slug, input.name, input.description || null, JSON.stringify(input.metadata || {}), timestamp, timestamp, machineId]);
4713
4875
  return getTaskList(id, d);
4714
4876
  }
4715
4877
  function getTaskList(id, db) {
@@ -4759,6 +4921,14 @@ function updateTaskList(id, input, db) {
4759
4921
  }
4760
4922
  function deleteTaskList(id, db) {
4761
4923
  const d = db || getDatabase();
4924
+ const list = getTaskList(id, d);
4925
+ if (!list)
4926
+ return false;
4927
+ recordStorageTombstone({
4928
+ object_type: "task_lists",
4929
+ object_id: id,
4930
+ payload: list
4931
+ }, d);
4762
4932
  return d.run("DELETE FROM task_lists WHERE id = ?", [id]).changes > 0;
4763
4933
  }
4764
4934
  function ensureTaskList(name, slug, projectId, db) {
@@ -4772,6 +4942,7 @@ var init_task_lists = __esm(() => {
4772
4942
  init_types();
4773
4943
  init_database();
4774
4944
  init_projects();
4945
+ init_storage_tombstones();
4775
4946
  });
4776
4947
 
4777
4948
  // src/lib/task-route-contract.ts
@@ -4975,6 +5146,8 @@ function readMachineLocalPath(project) {
4975
5146
  }
4976
5147
  }
4977
5148
  async function emitSharedTaskEvent(input) {
5149
+ if (!shouldEmitSharedTaskEvents(input.databasePath))
5150
+ return;
4978
5151
  const data = taskEventData(input.task, input.data);
4979
5152
  await new EventsClient().emit({
4980
5153
  source: SOURCE,
@@ -4998,6 +5171,7 @@ var init_shared_events = __esm(() => {
4998
5171
  init_database();
4999
5172
  init_projects();
5000
5173
  init_task_lists();
5174
+ init_event_emission_safety();
5001
5175
  });
5002
5176
 
5003
5177
  // src/lib/secret-redaction.ts
@@ -5246,8 +5420,9 @@ function logTaskChange(taskId, action, field, oldValue, newValue, agentId, db) {
5246
5420
  const d = db || getDatabase();
5247
5421
  const id = uuid();
5248
5422
  const timestamp = now();
5249
- d.run(`INSERT INTO task_history (id, task_id, action, field, old_value, new_value, agent_id, created_at)
5250
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [id, taskId, action, field || null, oldValue ?? null, newValue ?? null, agentId || null, timestamp]);
5423
+ const machineId = currentStorageMachineId(d);
5424
+ d.run(`INSERT INTO task_history (id, task_id, action, field, old_value, new_value, agent_id, created_at, machine_id)
5425
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [id, taskId, action, field || null, oldValue ?? null, newValue ?? null, agentId || null, timestamp, machineId]);
5251
5426
  try {
5252
5427
  const { logActivity: logActivity2 } = (init_activity_audit(), __toCommonJS(exports_activity_audit));
5253
5428
  logActivity2({
@@ -5260,7 +5435,7 @@ function logTaskChange(taskId, action, field, oldValue, newValue, agentId, db) {
5260
5435
  actor_id: agentId ?? undefined
5261
5436
  }, d);
5262
5437
  } catch {}
5263
- return { id, task_id: taskId, action, field: field || null, old_value: oldValue ?? null, new_value: newValue ?? null, agent_id: agentId || null, created_at: timestamp };
5438
+ return { id, task_id: taskId, action, field: field || null, old_value: oldValue ?? null, new_value: newValue ?? null, agent_id: agentId || null, created_at: timestamp, machine_id: machineId };
5264
5439
  }
5265
5440
  function getTaskHistory(taskId, db) {
5266
5441
  const d = db || getDatabase();
@@ -5272,6 +5447,7 @@ function getRecentActivity(limit = 50, db) {
5272
5447
  }
5273
5448
  var init_audit = __esm(() => {
5274
5449
  init_database();
5450
+ init_storage_tombstones();
5275
5451
  });
5276
5452
 
5277
5453
  // src/db/webhooks.ts
@@ -5537,13 +5713,14 @@ function createTask(input, db) {
5537
5713
  const d = db || getDatabase();
5538
5714
  const timestamp = now();
5539
5715
  const tags = input.tags || [];
5716
+ const machineId = currentStorageMachineId(d);
5540
5717
  const assignedBy = input.assigned_by || input.agent_id;
5541
5718
  const assignedFromProject = input.assigned_from_project || null;
5542
5719
  let id = uuid();
5543
5720
  for (let attempt = 0;attempt < 3; attempt++) {
5544
5721
  try {
5545
- d.run(`INSERT INTO tasks (id, short_id, project_id, parent_id, plan_id, task_list_id, cycle_id, title, description, status, priority, agent_id, assigned_to, session_id, working_dir, tags, metadata, version, created_at, updated_at, due_at, estimated_minutes, sla_minutes, confidence, retry_count, max_retries, retry_after, requires_approval, approved_by, approved_at, recurrence_rule, recurrence_parent_id, spawns_template_id, reason, spawned_from_session, assigned_by, assigned_from_project, task_type)
5546
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
5722
+ d.run(`INSERT INTO tasks (id, short_id, project_id, parent_id, plan_id, task_list_id, cycle_id, title, description, status, priority, agent_id, assigned_to, session_id, working_dir, tags, metadata, version, created_at, updated_at, due_at, estimated_minutes, sla_minutes, confidence, retry_count, max_retries, retry_after, requires_approval, approved_by, approved_at, recurrence_rule, recurrence_parent_id, spawns_template_id, reason, spawned_from_session, assigned_by, assigned_from_project, task_type, machine_id)
5723
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
5547
5724
  id,
5548
5725
  null,
5549
5726
  input.project_id || null,
@@ -5580,7 +5757,8 @@ function createTask(input, db) {
5580
5757
  input.spawned_from_session || null,
5581
5758
  assignedBy || null,
5582
5759
  assignedFromProject || null,
5583
- input.task_type || null
5760
+ input.task_type || null,
5761
+ machineId
5584
5762
  ]);
5585
5763
  break;
5586
5764
  } catch (e) {
@@ -5596,9 +5774,10 @@ function createTask(input, db) {
5596
5774
  }
5597
5775
  const task = getTask(id, d);
5598
5776
  const payload = taskEventData(task);
5777
+ const databasePath = databasePathFromDatabase(d);
5599
5778
  dispatchWebhook2("task.created", payload, d).catch(() => {});
5600
- emitLocalEventHooksQuiet({ type: "task.created", payload });
5601
- emitSharedTaskEventQuiet({ type: "task.created", task });
5779
+ emitLocalEventHooksQuiet({ type: "task.created", payload, databasePath });
5780
+ emitSharedTaskEventQuiet({ type: "task.created", task, databasePath });
5602
5781
  return task;
5603
5782
  }
5604
5783
  function getTask(id, db) {
@@ -6016,29 +6195,39 @@ function updateTask(id, input, db) {
6016
6195
  approved_by: input.approved_by ?? task.approved_by,
6017
6196
  approved_at: input.approved_by ? timestamp : task.approved_at
6018
6197
  };
6198
+ const databasePath = databasePathFromDatabase(d);
6019
6199
  if (input.assigned_to !== undefined && input.assigned_to !== task.assigned_to) {
6020
6200
  const payload = taskEventData(updatedTask, { assigned_to: input.assigned_to, old_assigned_to: task.assigned_to });
6021
6201
  dispatchWebhook2("task.assigned", payload, d).catch(() => {});
6022
- emitLocalEventHooksQuiet({ type: "task.assigned", payload });
6023
- emitSharedTaskEventQuiet({ type: "task.assigned", task: updatedTask, data: { old_assigned_to: task.assigned_to } });
6202
+ emitLocalEventHooksQuiet({ type: "task.assigned", payload, databasePath });
6203
+ emitSharedTaskEventQuiet({ type: "task.assigned", task: updatedTask, data: { old_assigned_to: task.assigned_to }, databasePath });
6024
6204
  }
6025
6205
  if (input.status !== undefined && input.status !== task.status) {
6026
6206
  const payload = taskEventData(updatedTask, { old_status: task.status, new_status: input.status });
6027
6207
  dispatchWebhook2("task.status_changed", payload, d).catch(() => {});
6028
- emitLocalEventHooksQuiet({ type: "task.status_changed", payload });
6029
- emitSharedTaskEventQuiet({ type: "task.status_changed", task: updatedTask, data: { old_status: task.status, new_status: input.status } });
6208
+ emitLocalEventHooksQuiet({ type: "task.status_changed", payload, databasePath });
6209
+ emitSharedTaskEventQuiet({ type: "task.status_changed", task: updatedTask, data: { old_status: task.status, new_status: input.status }, databasePath });
6030
6210
  }
6031
6211
  if (input.approved_by !== undefined) {
6032
- emitLocalEventHooksQuiet({ type: "approval.decided", payload: { id, approved_by: input.approved_by, title: task.title } });
6212
+ emitLocalEventHooksQuiet({ type: "approval.decided", payload: { id, approved_by: input.approved_by, title: task.title }, databasePath });
6033
6213
  }
6034
6214
  const updatePayload = taskEventData(updatedTask);
6035
6215
  dispatchWebhook2("task.updated", updatePayload, d).catch(() => {});
6036
- emitLocalEventHooksQuiet({ type: "task.updated", payload: updatePayload });
6037
- emitSharedTaskEventQuiet({ type: "task.updated", task: updatedTask });
6216
+ emitLocalEventHooksQuiet({ type: "task.updated", payload: updatePayload, databasePath });
6217
+ emitSharedTaskEventQuiet({ type: "task.updated", task: updatedTask, databasePath });
6038
6218
  return updatedTask;
6039
6219
  }
6040
6220
  function deleteTask(id, db) {
6041
6221
  const d = db || getDatabase();
6222
+ const row = d.query("SELECT * FROM tasks WHERE id = ?").get(id);
6223
+ if (!row)
6224
+ return false;
6225
+ recordStorageTombstone({
6226
+ object_type: "tasks",
6227
+ object_id: id,
6228
+ payload: rowToTask(row),
6229
+ version: row.version
6230
+ }, d);
6042
6231
  const result = d.run("DELETE FROM tasks WHERE id = ?", [id]);
6043
6232
  return result.changes > 0;
6044
6233
  }
@@ -6046,11 +6235,13 @@ var init_task_crud = __esm(() => {
6046
6235
  init_types();
6047
6236
  init_database();
6048
6237
  init_completion_guard();
6238
+ init_event_emission_safety();
6049
6239
  init_event_hooks();
6050
6240
  init_shared_events();
6051
6241
  init_audit();
6052
6242
  init_webhooks();
6053
6243
  init_checklists();
6244
+ init_storage_tombstones();
6054
6245
  });
6055
6246
 
6056
6247
  // src/lib/recurrence.ts
@@ -6199,8 +6390,9 @@ function resolveTemplateId(id, d) {
6199
6390
  function createTemplate(input, db) {
6200
6391
  const d = db || getDatabase();
6201
6392
  const id = uuid();
6202
- d.run(`INSERT INTO task_templates (id, name, title_pattern, description, priority, tags, variables, project_id, plan_id, metadata, created_at)
6203
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
6393
+ const machineId = currentStorageMachineId(d);
6394
+ d.run(`INSERT INTO task_templates (id, name, title_pattern, description, priority, tags, variables, project_id, plan_id, metadata, created_at, machine_id)
6395
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
6204
6396
  id,
6205
6397
  input.name,
6206
6398
  input.title_pattern,
@@ -6211,7 +6403,8 @@ function createTemplate(input, db) {
6211
6403
  input.project_id || null,
6212
6404
  input.plan_id || null,
6213
6405
  JSON.stringify(input.metadata || {}),
6214
- now()
6406
+ now(),
6407
+ machineId
6215
6408
  ]);
6216
6409
  if (input.tasks && input.tasks.length > 0) {
6217
6410
  addTemplateTasks(id, input.tasks, d);
@@ -6235,6 +6428,15 @@ function deleteTemplate(id, db) {
6235
6428
  const resolved = resolveTemplateId(id, d);
6236
6429
  if (!resolved)
6237
6430
  return false;
6431
+ const template = getTemplate(resolved, d);
6432
+ if (!template)
6433
+ return false;
6434
+ recordStorageTombstone({
6435
+ object_type: "templates",
6436
+ object_id: resolved,
6437
+ payload: template,
6438
+ version: template.version
6439
+ }, d);
6238
6440
  return d.run("DELETE FROM task_templates WHERE id = ?", [resolved]).changes > 0;
6239
6441
  }
6240
6442
  function updateTemplate(id, updates, db) {
@@ -6604,6 +6806,7 @@ function previewTemplate(templateId, variables, db) {
6604
6806
  var init_templates = __esm(() => {
6605
6807
  init_database();
6606
6808
  init_tasks();
6809
+ init_storage_tombstones();
6607
6810
  });
6608
6811
 
6609
6812
  // src/db/task-graph.ts
@@ -6770,6 +6973,7 @@ function getBlockingDeps(id, db) {
6770
6973
  }
6771
6974
  function startTask(id, agentId, db) {
6772
6975
  const d = db || getDatabase();
6976
+ const databasePath = databasePathFromDatabase(d);
6773
6977
  const task = getTask(id, d);
6774
6978
  if (!task)
6775
6979
  throw new TaskNotFoundError(id);
@@ -6784,7 +6988,8 @@ function startTask(id, agentId, db) {
6784
6988
  agent_id: agentId,
6785
6989
  title: task.title,
6786
6990
  blockers: blocking.map((b) => ({ id: b.id, short_id: b.short_id, title: b.title, status: b.status }))
6787
- }
6991
+ },
6992
+ databasePath
6788
6993
  });
6789
6994
  throw new Error(`Task is blocked by ${blocking.length} unfinished dependency(ies): ${blockerIds}`);
6790
6995
  }
@@ -6806,12 +7011,13 @@ function startTask(id, agentId, db) {
6806
7011
  const startedTask = { ...task, status: "in_progress", assigned_to: agentId, locked_by: agentId, locked_at: timestamp, started_at: task.started_at || timestamp, version: task.version + 1, updated_at: timestamp };
6807
7012
  const payload = taskEventData(startedTask, { agent_id: agentId });
6808
7013
  dispatchWebhook2("task.started", payload, d).catch(() => {});
6809
- emitLocalEventHooksQuiet({ type: "task.started", payload });
6810
- emitSharedTaskEventQuiet({ type: "task.started", task: startedTask, data: { agent_id: agentId } });
7014
+ emitLocalEventHooksQuiet({ type: "task.started", payload, databasePath });
7015
+ emitSharedTaskEventQuiet({ type: "task.started", task: startedTask, data: { agent_id: agentId }, databasePath });
6811
7016
  return startedTask;
6812
7017
  }
6813
7018
  function completeTask(id, agentId, db, options) {
6814
7019
  const d = db || getDatabase();
7020
+ const databasePath = databasePathFromDatabase(d);
6815
7021
  const task = getTask(id, d);
6816
7022
  if (!task)
6817
7023
  throw new TaskNotFoundError(id);
@@ -6857,8 +7063,8 @@ function completeTask(id, agentId, db, options) {
6857
7063
  };
6858
7064
  const completionPayload = taskEventData(completedTaskForEvent, { agent_id: agentId, completed_at: timestamp });
6859
7065
  dispatchWebhook2("task.completed", completionPayload, d).catch(() => {});
6860
- emitLocalEventHooksQuiet({ type: "task.completed", payload: completionPayload });
6861
- emitSharedTaskEventQuiet({ type: "task.completed", task: completedTaskForEvent, data: { agent_id: agentId, completed_at: timestamp } });
7066
+ emitLocalEventHooksQuiet({ type: "task.completed", payload: completionPayload, databasePath });
7067
+ emitSharedTaskEventQuiet({ type: "task.completed", task: completedTaskForEvent, data: { agent_id: agentId, completed_at: timestamp }, databasePath });
6862
7068
  let spawnedTask = null;
6863
7069
  if (task.recurrence_rule && !options?.skip_recurrence) {
6864
7070
  spawnedTask = spawnNextRecurrence(task, d, timestamp);
@@ -6902,9 +7108,9 @@ function completeTask(id, agentId, db, options) {
6902
7108
  const depTask = getTask(dep.id, d);
6903
7109
  const payload = depTask ? taskEventData(depTask, { unblocked_by: id }) : { id: dep.id, unblocked_by: id, title: dep.title };
6904
7110
  dispatchWebhook2("task.unblocked", payload, d).catch(() => {});
6905
- emitLocalEventHooksQuiet({ type: "task.unblocked", payload });
7111
+ emitLocalEventHooksQuiet({ type: "task.unblocked", payload, databasePath });
6906
7112
  if (depTask)
6907
- emitSharedTaskEventQuiet({ type: "task.unblocked", task: depTask, data: { unblocked_by: id } });
7113
+ emitSharedTaskEventQuiet({ type: "task.unblocked", task: depTask, data: { unblocked_by: id }, databasePath });
6908
7114
  }
6909
7115
  }
6910
7116
  return { ...task, status: "completed", locked_by: null, locked_at: null, completed_at: timestamp, confidence, version: task.version + 1, updated_at: timestamp, metadata: meta };
@@ -7074,6 +7280,7 @@ function getTasksChangedSince(since, filters, db) {
7074
7280
  }
7075
7281
  function failTask(id, agentId, reason, options, db) {
7076
7282
  const d = db || getDatabase();
7283
+ const databasePath = databasePathFromDatabase(d);
7077
7284
  const task = getTask(id, d);
7078
7285
  if (!task)
7079
7286
  throw new TaskNotFoundError(id);
@@ -7102,8 +7309,8 @@ function failTask(id, agentId, reason, options, db) {
7102
7309
  logTaskChange(id, "fail", "status", task.status, "failed", agentId || null, d);
7103
7310
  const failurePayload = taskEventData(failedTask, { reason, error_code: options?.error_code, agent_id: agentId });
7104
7311
  dispatchWebhook2("task.failed", failurePayload, d).catch(() => {});
7105
- emitLocalEventHooksQuiet({ type: "task.failed", payload: failurePayload });
7106
- emitSharedTaskEventQuiet({ type: "task.failed", task: failedTask, data: { reason, error_code: options?.error_code, agent_id: agentId }, severity: "warning" });
7312
+ emitLocalEventHooksQuiet({ type: "task.failed", payload: failurePayload, databasePath });
7313
+ emitSharedTaskEventQuiet({ type: "task.failed", task: failedTask, data: { reason, error_code: options?.error_code, agent_id: agentId }, severity: "warning", databasePath });
7107
7314
  let retryTask;
7108
7315
  if (options?.retry) {
7109
7316
  const retryCount = (task.retry_count || 0) + 1;
@@ -7163,6 +7370,7 @@ function getStaleTasks(staleQuery = 30, filters, db) {
7163
7370
  }
7164
7371
  function stealTask(agentId, opts, db) {
7165
7372
  const d = db || getDatabase();
7373
+ const databasePath = databasePathFromDatabase(d);
7166
7374
  const staleMinutes = opts?.stale_minutes ?? 30;
7167
7375
  const staleTasks = getStaleTasks(staleMinutes, { project_id: opts?.project_id, task_list_id: opts?.task_list_id }, d);
7168
7376
  if (staleTasks.length === 0)
@@ -7181,8 +7389,8 @@ function stealTask(agentId, opts, db) {
7181
7389
  const stolenTask = { ...target, assigned_to: agentId, locked_by: agentId, locked_at: timestamp, updated_at: timestamp, version: target.version + 1 };
7182
7390
  const payload = taskEventData(stolenTask, { agent_id: agentId, stolen_from: target.assigned_to });
7183
7391
  dispatchWebhook2("task.assigned", payload, d).catch(() => {});
7184
- emitLocalEventHooksQuiet({ type: "task.assigned", payload });
7185
- emitSharedTaskEventQuiet({ type: "task.assigned", task: stolenTask, data: { agent_id: agentId, stolen_from: target.assigned_to } });
7392
+ emitLocalEventHooksQuiet({ type: "task.assigned", payload, databasePath });
7393
+ emitSharedTaskEventQuiet({ type: "task.assigned", task: stolenTask, data: { agent_id: agentId, stolen_from: target.assigned_to }, databasePath });
7186
7394
  return stolenTask;
7187
7395
  }
7188
7396
  function claimOrSteal(agentId, filters, db) {
@@ -7230,6 +7438,7 @@ var init_task_lifecycle = __esm(() => {
7230
7438
  init_types();
7231
7439
  init_database();
7232
7440
  init_completion_guard();
7441
+ init_event_emission_safety();
7233
7442
  init_event_hooks();
7234
7443
  init_shared_events();
7235
7444
  init_audit();
@@ -7937,8 +8146,9 @@ function createPlan(input, db) {
7937
8146
  const d = db || getDatabase();
7938
8147
  const id = uuid();
7939
8148
  const timestamp = now();
7940
- d.run(`INSERT INTO plans (id, project_id, task_list_id, agent_id, name, description, status, created_at, updated_at)
7941
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
8149
+ const machineId = currentStorageMachineId(d);
8150
+ d.run(`INSERT INTO plans (id, project_id, task_list_id, agent_id, name, description, status, created_at, updated_at, machine_id)
8151
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
7942
8152
  id,
7943
8153
  input.project_id || null,
7944
8154
  input.task_list_id || null,
@@ -7947,7 +8157,8 @@ function createPlan(input, db) {
7947
8157
  input.description || null,
7948
8158
  input.status || "active",
7949
8159
  timestamp,
7950
- timestamp
8160
+ timestamp,
8161
+ machineId
7951
8162
  ]);
7952
8163
  return getPlan(id, d);
7953
8164
  }
@@ -7995,19 +8206,30 @@ function updatePlan(id, input, db) {
7995
8206
  const updated = getPlan(id, d);
7996
8207
  emitLocalEventHooksQuiet({
7997
8208
  type: "plan.updated",
7998
- payload: { id, old_status: plan.status, new_status: updated.status, name: updated.name, project_id: updated.project_id }
8209
+ payload: { id, old_status: plan.status, new_status: updated.status, name: updated.name, project_id: updated.project_id },
8210
+ databasePath: databasePathFromDatabase(d)
7999
8211
  });
8000
8212
  return updated;
8001
8213
  }
8002
8214
  function deletePlan(id, db) {
8003
8215
  const d = db || getDatabase();
8216
+ const plan = getPlan(id, d);
8217
+ if (!plan)
8218
+ return false;
8219
+ recordStorageTombstone({
8220
+ object_type: "plans",
8221
+ object_id: id,
8222
+ payload: plan
8223
+ }, d);
8004
8224
  const result = d.run("DELETE FROM plans WHERE id = ?", [id]);
8005
8225
  return result.changes > 0;
8006
8226
  }
8007
8227
  var init_plans = __esm(() => {
8008
8228
  init_types();
8229
+ init_event_emission_safety();
8009
8230
  init_event_hooks();
8010
8231
  init_database();
8232
+ init_storage_tombstones();
8011
8233
  });
8012
8234
 
8013
8235
  // src/db/boards.ts
@@ -8414,20 +8636,20 @@ var init_boards = __esm(() => {
8414
8636
  // src/lib/artifact-store.ts
8415
8637
  import { createHash as createHash3 } from "crypto";
8416
8638
  import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync3, rmSync, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
8417
- import { basename, dirname as dirname5, join as join6, resolve as resolve6 } from "path";
8418
- import { tmpdir } from "os";
8639
+ import { basename, dirname as dirname5, join as join6, resolve as resolve7 } from "path";
8640
+ import { tmpdir as tmpdir2 } from "os";
8419
8641
  function isInMemoryDb2(path) {
8420
8642
  return path === ":memory:" || path.startsWith("file::memory:");
8421
8643
  }
8422
8644
  function artifactStoreRoot() {
8423
8645
  if (process.env["HASNA_TODOS_ARTIFACTS_DIR"])
8424
- return resolve6(process.env["HASNA_TODOS_ARTIFACTS_DIR"]);
8646
+ return resolve7(process.env["HASNA_TODOS_ARTIFACTS_DIR"]);
8425
8647
  if (process.env["TODOS_ARTIFACTS_DIR"])
8426
- return resolve6(process.env["TODOS_ARTIFACTS_DIR"]);
8648
+ return resolve7(process.env["TODOS_ARTIFACTS_DIR"]);
8427
8649
  const dbPath = getDatabasePath();
8428
8650
  if (isInMemoryDb2(dbPath))
8429
- return join6(tmpdir(), "hasna-todos-artifacts");
8430
- return join6(dirname5(resolve6(dbPath)), "artifacts");
8651
+ return join6(tmpdir2(), "hasna-todos-artifacts");
8652
+ return join6(dirname5(resolve7(dbPath)), "artifacts");
8431
8653
  }
8432
8654
  function artifactStorePath(relativePath) {
8433
8655
  const normalized = relativePath.replace(/\\/g, "/");
@@ -8474,7 +8696,7 @@ function mediaTypeFor(path, textLike) {
8474
8696
  return "application/octet-stream";
8475
8697
  }
8476
8698
  function storeArtifactContent(input) {
8477
- const sourcePath = resolve6(input.path);
8699
+ const sourcePath = resolve7(input.path);
8478
8700
  if (!existsSync7(sourcePath))
8479
8701
  return null;
8480
8702
  const sourceStat = statSync2(sourcePath);
@@ -9171,7 +9393,11 @@ function startTaskRun(input, db) {
9171
9393
  }, d);
9172
9394
  }
9173
9395
  const run = getTaskRun(id, d);
9174
- emitLocalEventHooksQuiet({ type: "run.started", payload: { id: run.id, task_id: run.task_id, agent_id: run.agent_id, title: run.title } });
9396
+ emitLocalEventHooksQuiet({
9397
+ type: "run.started",
9398
+ payload: { id: run.id, task_id: run.task_id, agent_id: run.agent_id, title: run.title },
9399
+ databasePath: databasePathFromDatabase(d)
9400
+ });
9175
9401
  return run;
9176
9402
  }
9177
9403
  function beginTaskRunTransaction(input, db) {
@@ -9440,7 +9666,8 @@ function finishTaskRun(input, db) {
9440
9666
  const updated = getTaskRun(run.id, d);
9441
9667
  emitLocalEventHooksQuiet({
9442
9668
  type: `run.${input.status}`,
9443
- payload: { id: updated.id, task_id: updated.task_id, agent_id: updated.agent_id, status: updated.status, summary: updated.summary, completed_at: timestamp }
9669
+ payload: { id: updated.id, task_id: updated.task_id, agent_id: updated.agent_id, status: updated.status, summary: updated.summary, completed_at: timestamp },
9670
+ databasePath: databasePathFromDatabase(d)
9444
9671
  });
9445
9672
  return updated;
9446
9673
  }
@@ -9524,6 +9751,7 @@ function getTaskRunLedger(runId, db) {
9524
9751
  var LOOP_RUN_TRANSACTION_SCHEMA_VERSION = "todos.loop_run_transaction.v1";
9525
9752
  var init_task_runs = __esm(() => {
9526
9753
  init_artifact_store();
9754
+ init_event_emission_safety();
9527
9755
  init_event_hooks();
9528
9756
  init_redaction();
9529
9757
  init_types();
@@ -10410,6 +10638,7 @@ function rowToAgent(row) {
10410
10638
  }
10411
10639
  function registerAgent(input, db) {
10412
10640
  const d = db || getDatabase();
10641
+ const machineId = currentStorageMachineId(d);
10413
10642
  const existingNames = d.query("SELECT name FROM agents").all().map((row) => row.name);
10414
10643
  const normalizedName = validateAgentName(input.name, existingNames);
10415
10644
  const existing = getAgentByName(normalizedName, d);
@@ -10450,14 +10679,18 @@ function registerAgent(input, db) {
10450
10679
  updates.push("active_project_id = ?");
10451
10680
  params.push(input.project_id);
10452
10681
  }
10682
+ if (!existing.machine_id && machineId) {
10683
+ updates.push("machine_id = ?");
10684
+ params.push(machineId);
10685
+ }
10453
10686
  params.push(existing.id);
10454
10687
  d.run(`UPDATE agents SET ${updates.join(", ")} WHERE id = ?`, params);
10455
10688
  return getAgent(existing.id, d);
10456
10689
  }
10457
10690
  const id = shortUuid();
10458
10691
  const timestamp = now();
10459
- d.run(`INSERT INTO agents (id, name, description, role, title, level, permissions, capabilities, reports_to, org_id, metadata, created_at, last_seen_at, session_id, working_dir, active_project_id)
10460
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
10692
+ d.run(`INSERT INTO agents (id, name, description, role, title, level, permissions, capabilities, reports_to, org_id, metadata, created_at, last_seen_at, session_id, working_dir, active_project_id, machine_id)
10693
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
10461
10694
  id,
10462
10695
  normalizedName,
10463
10696
  input.description || null,
@@ -10473,7 +10706,8 @@ function registerAgent(input, db) {
10473
10706
  timestamp,
10474
10707
  input.session_id || null,
10475
10708
  input.working_dir || null,
10476
- input.project_id && input.session_id ? input.project_id : null
10709
+ input.project_id && input.session_id ? input.project_id : null,
10710
+ machineId
10477
10711
  ]);
10478
10712
  return getAgent(id, d);
10479
10713
  }
@@ -10656,6 +10890,7 @@ function getCapableAgents(capabilities, opts, db) {
10656
10890
  }
10657
10891
  var init_agents = __esm(() => {
10658
10892
  init_database();
10893
+ init_storage_tombstones();
10659
10894
  init_agent_names();
10660
10895
  });
10661
10896
 
@@ -11191,7 +11426,7 @@ var init_doctor = __esm(() => {
11191
11426
  });
11192
11427
 
11193
11428
  // src/server/routes.ts
11194
- import { join as join8, resolve as resolve7, sep } from "path";
11429
+ import { join as join8, resolve as resolve8, sep as sep2 } from "path";
11195
11430
  function parseFieldsParam(url) {
11196
11431
  const fieldsParam = url.searchParams.get("fields");
11197
11432
  return fieldsParam ? fieldsParam.split(",").map((f) => f.trim()).filter(Boolean) : undefined;
@@ -11929,9 +12164,9 @@ function handleStaticFiles(path, method, ctx, json2, serveStaticFile2) {
11929
12164
  return null;
11930
12165
  if (path !== "/") {
11931
12166
  const filePath = join8(ctx.dashboardDir, path);
11932
- const resolvedFile = resolve7(filePath);
11933
- const resolvedBase = resolve7(ctx.dashboardDir);
11934
- if (!resolvedFile.startsWith(resolvedBase + sep) && resolvedFile !== resolvedBase) {
12167
+ const resolvedFile = resolve8(filePath);
12168
+ const resolvedBase = resolve8(ctx.dashboardDir);
12169
+ if (!resolvedFile.startsWith(resolvedBase + sep2) && resolvedFile !== resolvedBase) {
11935
12170
  return json2({ error: "Forbidden" }, 403);
11936
12171
  }
11937
12172
  const res2 = serveStaticFile2(filePath);
@@ -15330,7 +15565,7 @@ class JSONSchemaGenerator {
15330
15565
  if (val === undefined) {
15331
15566
  if (this.unrepresentable === "throw") {
15332
15567
  throw new Error("Literal `undefined` cannot be represented in JSON Schema");
15333
- } else {}
15568
+ }
15334
15569
  } else if (typeof val === "bigint") {
15335
15570
  if (this.unrepresentable === "throw") {
15336
15571
  throw new Error("BigInt literals cannot be represented in JSON Schema");
@@ -17696,9 +17931,9 @@ data:
17696
17931
  const initRequest = messages.find((m) => isInitializeRequest(m));
17697
17932
  const clientProtocolVersion = initRequest ? initRequest.params.protocolVersion : req.headers.get("mcp-protocol-version") ?? DEFAULT_NEGOTIATED_PROTOCOL_VERSION;
17698
17933
  if (this._enableJsonResponse) {
17699
- return new Promise((resolve8) => {
17934
+ return new Promise((resolve9) => {
17700
17935
  this._streamMapping.set(streamId, {
17701
- resolveJson: resolve8,
17936
+ resolveJson: resolve9,
17702
17937
  cleanup: () => {
17703
17938
  this._streamMapping.delete(streamId);
17704
17939
  }
@@ -23998,7 +24233,7 @@ class Protocol {
23998
24233
  return;
23999
24234
  }
24000
24235
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1000;
24001
- await new Promise((resolve8) => setTimeout(resolve8, pollInterval));
24236
+ await new Promise((resolve9) => setTimeout(resolve9, pollInterval));
24002
24237
  options?.signal?.throwIfAborted();
24003
24238
  }
24004
24239
  } catch (error2) {
@@ -24010,7 +24245,7 @@ class Protocol {
24010
24245
  }
24011
24246
  request(request, resultSchema, options) {
24012
24247
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
24013
- return new Promise((resolve8, reject) => {
24248
+ return new Promise((resolve9, reject) => {
24014
24249
  const earlyReject = (error2) => {
24015
24250
  reject(error2);
24016
24251
  };
@@ -24088,7 +24323,7 @@ class Protocol {
24088
24323
  if (!parseResult.success) {
24089
24324
  reject(parseResult.error);
24090
24325
  } else {
24091
- resolve8(parseResult.data);
24326
+ resolve9(parseResult.data);
24092
24327
  }
24093
24328
  } catch (error2) {
24094
24329
  reject(error2);
@@ -24279,12 +24514,12 @@ class Protocol {
24279
24514
  interval = task.pollInterval;
24280
24515
  }
24281
24516
  } catch {}
24282
- return new Promise((resolve8, reject) => {
24517
+ return new Promise((resolve9, reject) => {
24283
24518
  if (signal.aborted) {
24284
24519
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
24285
24520
  return;
24286
24521
  }
24287
- const timeoutId = setTimeout(resolve8, interval);
24522
+ const timeoutId = setTimeout(resolve9, interval);
24288
24523
  signal.addEventListener("abort", () => {
24289
24524
  clearTimeout(timeoutId);
24290
24525
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -27269,7 +27504,7 @@ var require_compile = __commonJS((exports) => {
27269
27504
  const schOrFunc = root.refs[ref];
27270
27505
  if (schOrFunc)
27271
27506
  return schOrFunc;
27272
- let _sch = resolve8.call(this, root, ref);
27507
+ let _sch = resolve9.call(this, root, ref);
27273
27508
  if (_sch === undefined) {
27274
27509
  const schema = (_a = root.localRefs) === null || _a === undefined ? undefined : _a[ref];
27275
27510
  const { schemaId } = this.opts;
@@ -27296,7 +27531,7 @@ var require_compile = __commonJS((exports) => {
27296
27531
  function sameSchemaEnv(s1, s2) {
27297
27532
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
27298
27533
  }
27299
- function resolve8(root, ref) {
27534
+ function resolve9(root, ref) {
27300
27535
  let sch;
27301
27536
  while (typeof (sch = this.refs[ref]) == "string")
27302
27537
  ref = sch;
@@ -27826,7 +28061,7 @@ var require_fast_uri = __commonJS((exports, module) => {
27826
28061
  }
27827
28062
  return uri;
27828
28063
  }
27829
- function resolve8(baseURI, relativeURI, options) {
28064
+ function resolve9(baseURI, relativeURI, options) {
27830
28065
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
27831
28066
  const resolved = resolveComponent(parse6(baseURI, schemelessOptions), parse6(relativeURI, schemelessOptions), schemelessOptions, true);
27832
28067
  schemelessOptions.skipEscape = true;
@@ -28054,7 +28289,7 @@ var require_fast_uri = __commonJS((exports, module) => {
28054
28289
  var fastUri = {
28055
28290
  SCHEMES,
28056
28291
  normalize,
28057
- resolve: resolve8,
28292
+ resolve: resolve9,
28058
28293
  resolveComponent,
28059
28294
  equal,
28060
28295
  serialize,
@@ -31580,7 +31815,7 @@ class McpServer {
31580
31815
  let task = createTaskResult.task;
31581
31816
  const pollInterval = task.pollInterval ?? 5000;
31582
31817
  while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
31583
- await new Promise((resolve8) => setTimeout(resolve8, pollInterval));
31818
+ await new Promise((resolve9) => setTimeout(resolve9, pollInterval));
31584
31819
  const updatedTask = await extra.taskStore.getTask(taskId);
31585
31820
  if (!updatedTask) {
31586
31821
  throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
@@ -32204,12 +32439,12 @@ class StdioServerTransport {
32204
32439
  this.onclose?.();
32205
32440
  }
32206
32441
  send(message) {
32207
- return new Promise((resolve8) => {
32442
+ return new Promise((resolve9) => {
32208
32443
  const json2 = serializeMessage(message);
32209
32444
  if (this._stdout.write(json2)) {
32210
- resolve8();
32445
+ resolve9();
32211
32446
  } else {
32212
- this._stdout.once("drain", resolve8);
32447
+ this._stdout.once("drain", resolve9);
32213
32448
  }
32214
32449
  });
32215
32450
  }
@@ -33616,7 +33851,7 @@ var init_task_crud2 = __esm(() => {
33616
33851
 
33617
33852
  // src/lib/project-bootstrap.ts
33618
33853
  import { existsSync as existsSync9, readFileSync as readFileSync4, statSync as statSync4 } from "fs";
33619
- import { basename as basename3, dirname as dirname7, resolve as resolve8 } from "path";
33854
+ import { basename as basename3, dirname as dirname7, resolve as resolve9 } from "path";
33620
33855
  function safeStat(path) {
33621
33856
  try {
33622
33857
  return statSync4(path);
@@ -33625,7 +33860,7 @@ function safeStat(path) {
33625
33860
  }
33626
33861
  }
33627
33862
  function canonicalPath(input) {
33628
- const resolved = resolve8(input);
33863
+ const resolved = resolve9(input);
33629
33864
  const stats = safeStat(resolved);
33630
33865
  if (stats?.isFile())
33631
33866
  return dirname7(resolved);
@@ -33634,7 +33869,7 @@ function canonicalPath(input) {
33634
33869
  function findUp(start, marker) {
33635
33870
  let current = canonicalPath(start);
33636
33871
  while (true) {
33637
- if (existsSync9(resolve8(current, marker)))
33872
+ if (existsSync9(resolve9(current, marker)))
33638
33873
  return current;
33639
33874
  const parent = dirname7(current);
33640
33875
  if (parent === current)
@@ -33645,7 +33880,7 @@ function findUp(start, marker) {
33645
33880
  function readPackageJson(path) {
33646
33881
  if (!path)
33647
33882
  return null;
33648
- const file = resolve8(path, "package.json");
33883
+ const file = resolve9(path, "package.json");
33649
33884
  if (!existsSync9(file))
33650
33885
  return null;
33651
33886
  try {
@@ -33668,7 +33903,7 @@ function workspaceMarker(root, rootPackage) {
33668
33903
  if (rootPackage?.workspaces)
33669
33904
  markers.push("package.json#workspaces");
33670
33905
  for (const marker of ["pnpm-workspace.yaml", "turbo.json", "nx.json", "lerna.json", "rush.json", "bun.lock", "bun.lockb"]) {
33671
- if (existsSync9(resolve8(root, marker)))
33906
+ if (existsSync9(resolve9(root, marker)))
33672
33907
  markers.push(marker);
33673
33908
  }
33674
33909
  const kind = markers.find((marker) => marker !== "bun.lock" && marker !== "bun.lockb") ?? null;
@@ -34212,7 +34447,7 @@ var init_retention_cleanup = __esm(() => {
34212
34447
 
34213
34448
  // src/lib/mention-resolver.ts
34214
34449
  import { existsSync as existsSync11, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync5 } from "fs";
34215
- import { basename as basename4, isAbsolute, join as join9, relative as relative3, resolve as resolve9, sep as sep2 } from "path";
34450
+ import { basename as basename4, isAbsolute, join as join9, relative as relative3, resolve as resolve10, sep as sep3 } from "path";
34216
34451
  function blankResolution(parsed) {
34217
34452
  return {
34218
34453
  input: parsed.input,
@@ -34235,11 +34470,11 @@ function backlink(kind, key, label, target = key) {
34235
34470
  return { kind, key, label, target };
34236
34471
  }
34237
34472
  function normalizeWorkspace(workspace) {
34238
- return resolve9(workspace || process.cwd());
34473
+ return resolve10(workspace || process.cwd());
34239
34474
  }
34240
34475
  function isInside(root, absolutePath) {
34241
34476
  const rel = relative3(root, absolutePath);
34242
- return rel === "" || !rel.startsWith("..") && !rel.includes(`..${sep2}`) && !isAbsolute(rel);
34477
+ return rel === "" || !rel.startsWith("..") && !rel.includes(`..${sep3}`) && !isAbsolute(rel);
34243
34478
  }
34244
34479
  function normalizeRelativePath(value) {
34245
34480
  const normalized = value.trim().replace(/^\.?\//, "").replace(/\\/g, "/");
@@ -34303,7 +34538,7 @@ function resolveFile(parsed, workspace) {
34303
34538
  resolution.warnings.push("path is empty or escapes the workspace");
34304
34539
  return resolution;
34305
34540
  }
34306
- const absolutePath = resolve9(workspace, relPath);
34541
+ const absolutePath = resolve10(workspace, relPath);
34307
34542
  if (!isInside(workspace, absolutePath)) {
34308
34543
  resolution.path = relPath;
34309
34544
  resolution.warnings.push("path escapes the workspace");
@@ -34648,9 +34883,9 @@ var init_mention_resolver = __esm(() => {
34648
34883
  });
34649
34884
 
34650
34885
  // src/lib/policy-packs.ts
34651
- import { relative as relative4, resolve as resolve10 } from "path";
34886
+ import { relative as relative4, resolve as resolve11 } from "path";
34652
34887
  function normalizePath3(path) {
34653
- return resolve10(path);
34888
+ return resolve11(path);
34654
34889
  }
34655
34890
  function unique4(values) {
34656
34891
  return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
@@ -34705,7 +34940,7 @@ function commandMatches(commands, pattern) {
34705
34940
  }
34706
34941
  function pathMatches(paths, pattern, root) {
34707
34942
  return paths.filter((path) => {
34708
- const candidate = path.startsWith("/") ? path : resolve10(root, path);
34943
+ const candidate = path.startsWith("/") ? path : resolve11(root, path);
34709
34944
  if (!isPathInside3(root, candidate))
34710
34945
  return matchesPattern3(path, pattern);
34711
34946
  return matchesPattern3(path, pattern) || matchesPattern3(relative4(root, candidate), pattern);
@@ -35052,7 +35287,8 @@ function logApprovalEvent(taskId, action, gate, agentId, db) {
35052
35287
  if (action === "approved" || action === "rejected" || action === "expired") {
35053
35288
  emitLocalEventHooksQuiet({
35054
35289
  type: "approval.decided",
35055
- payload: { task_id: taskId, gate: gate.gate, status: gate.status, reviewer: gate.reviewer, run_id: gate.run_id }
35290
+ payload: { task_id: taskId, gate: gate.gate, status: gate.status, reviewer: gate.reviewer, run_id: gate.run_id },
35291
+ databasePath: databasePathFromDatabase(db)
35056
35292
  });
35057
35293
  }
35058
35294
  }
@@ -35192,6 +35428,7 @@ var init_approval_gates = __esm(() => {
35192
35428
  init_task_runs();
35193
35429
  init_tasks();
35194
35430
  init_types();
35431
+ init_event_emission_safety();
35195
35432
  init_event_hooks();
35196
35433
  });
35197
35434
 
@@ -37758,7 +37995,7 @@ function canonicalize(value) {
37758
37995
  function hash(value) {
37759
37996
  return createHash5("sha256").update(value).digest("hex");
37760
37997
  }
37761
- function parsePayload(value) {
37998
+ function parsePayload2(value) {
37762
37999
  if (!value)
37763
38000
  return {};
37764
38001
  try {
@@ -37857,7 +38094,7 @@ function taskScopedRows(db, scope) {
37857
38094
  FROM handoffs h
37858
38095
  WHERE h.project_id IS NULL OR h.project_id = COALESCE(?, h.project_id)
37859
38096
  `).all(scope.project_id ?? null).filter((row) => {
37860
- const payload = parsePayload(row.payload_json);
38097
+ const payload = parsePayload2(row.payload_json);
37861
38098
  const taskRefs = parseStringArray(payload["task_ids"]);
37862
38099
  const runRefs = parseStringArray(payload["run_ids"]);
37863
38100
  if (scope.project_id && row.project_id !== scope.project_id)
@@ -37888,7 +38125,7 @@ function toLedgerEntries(rows) {
37888
38125
  });
37889
38126
  let previous = LOCAL_AUDIT_LEDGER_INITIAL_HASH;
37890
38127
  return ordered.map((row, index) => {
37891
- const payload = parsePayload(row.payload_json);
38128
+ const payload = parsePayload2(row.payload_json);
37892
38129
  const payloadHash = hash(canonicalize({ source: row.source, source_id: row.source_id, created_at: row.created_at, payload }));
37893
38130
  const chainHash = hash(`${previous}
37894
38131
  ${payloadHash}`);
@@ -38030,7 +38267,7 @@ var init_audit_ledger = __esm(() => {
38030
38267
 
38031
38268
  // src/lib/release-compatibility.ts
38032
38269
  import { readFileSync as readFileSync6 } from "fs";
38033
- import { join as join10, resolve as resolve11 } from "path";
38270
+ import { join as join10, resolve as resolve12 } from "path";
38034
38271
  import { Database as Database2 } from "bun:sqlite";
38035
38272
  function pass(id, message, details) {
38036
38273
  return { id, status: "passed", message, details };
@@ -38138,7 +38375,7 @@ function checkChangelog() {
38138
38375
  ];
38139
38376
  }
38140
38377
  function createReleaseCompatibilityReport(options = {}) {
38141
- const root = resolve11(options.root ?? process.cwd());
38378
+ const root = resolve12(options.root ?? process.cwd());
38142
38379
  const packageJson = readPackageJson2(root);
38143
38380
  const simulatedLevels = options.simulated_levels ?? defaultSimulationLevels();
38144
38381
  const checks4 = [
@@ -58415,7 +58652,7 @@ var require_to_json_schema = __commonJS((exports) => {
58415
58652
  if (val === undefined) {
58416
58653
  if (this.unrepresentable === "throw") {
58417
58654
  throw new Error("Literal `undefined` cannot be represented in JSON Schema");
58418
- } else {}
58655
+ }
58419
58656
  } else if (typeof val === "bigint") {
58420
58657
  if (this.unrepresentable === "throw") {
58421
58658
  throw new Error("BigInt literals cannot be represented in JSON Schema");
@@ -65057,7 +65294,7 @@ var require_protocol = __commonJS((exports) => {
65057
65294
  return;
65058
65295
  }
65059
65296
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1000;
65060
- await new Promise((resolve12) => setTimeout(resolve12, pollInterval));
65297
+ await new Promise((resolve13) => setTimeout(resolve13, pollInterval));
65061
65298
  options?.signal?.throwIfAborted();
65062
65299
  }
65063
65300
  } catch (error2) {
@@ -65069,7 +65306,7 @@ var require_protocol = __commonJS((exports) => {
65069
65306
  }
65070
65307
  request(request, resultSchema, options) {
65071
65308
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
65072
- return new Promise((resolve12, reject) => {
65309
+ return new Promise((resolve13, reject) => {
65073
65310
  const earlyReject = (error2) => {
65074
65311
  reject(error2);
65075
65312
  };
@@ -65147,7 +65384,7 @@ var require_protocol = __commonJS((exports) => {
65147
65384
  if (!parseResult.success) {
65148
65385
  reject(parseResult.error);
65149
65386
  } else {
65150
- resolve12(parseResult.data);
65387
+ resolve13(parseResult.data);
65151
65388
  }
65152
65389
  } catch (error2) {
65153
65390
  reject(error2);
@@ -65338,12 +65575,12 @@ var require_protocol = __commonJS((exports) => {
65338
65575
  interval = task.pollInterval;
65339
65576
  }
65340
65577
  } catch {}
65341
- return new Promise((resolve12, reject) => {
65578
+ return new Promise((resolve13, reject) => {
65342
65579
  if (signal.aborted) {
65343
65580
  reject(new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, "Request cancelled"));
65344
65581
  return;
65345
65582
  }
65346
- const timeoutId = setTimeout(resolve12, interval);
65583
+ const timeoutId = setTimeout(resolve13, interval);
65347
65584
  signal.addEventListener("abort", () => {
65348
65585
  clearTimeout(timeoutId);
65349
65586
  reject(new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, "Request cancelled"));
@@ -66489,7 +66726,7 @@ var require_mcp = __commonJS((exports) => {
66489
66726
  let task = createTaskResult.task;
66490
66727
  const pollInterval = task.pollInterval ?? 5000;
66491
66728
  while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
66492
- await new Promise((resolve12) => setTimeout(resolve12, pollInterval));
66729
+ await new Promise((resolve13) => setTimeout(resolve13, pollInterval));
66493
66730
  const updatedTask = await extra.taskStore.getTask(taskId);
66494
66731
  if (!updatedTask) {
66495
66732
  throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Task ${taskId} not found during polling`);
@@ -67898,7 +68135,7 @@ function classifyLog(text) {
67898
68135
  async function sleep2(ms) {
67899
68136
  if (ms <= 0)
67900
68137
  return;
67901
- await new Promise((resolve12) => setTimeout(resolve12, ms));
68138
+ await new Promise((resolve13) => setTimeout(resolve13, ms));
67902
68139
  }
67903
68140
  async function runCommandProvider(provider, input) {
67904
68141
  const commandTemplate = input.command || provider.command;
@@ -70627,7 +70864,7 @@ var init_local_bridge = __esm(() => {
70627
70864
  // src/lib/local-backups.ts
70628
70865
  import { createHash as createHash6 } from "crypto";
70629
70866
  import { readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "fs";
70630
- import { dirname as dirname8, resolve as resolve12 } from "path";
70867
+ import { dirname as dirname8, resolve as resolve13 } from "path";
70631
70868
  import { mkdirSync as mkdirSync6 } from "fs";
70632
70869
  function stableJson(value) {
70633
70870
  if (value === null || typeof value !== "object")
@@ -70729,14 +70966,14 @@ function createLocalBackup(options = {}, db) {
70729
70966
  return backup;
70730
70967
  }
70731
70968
  function writeLocalBackupFile(backup, outputPath) {
70732
- const path = resolve12(outputPath);
70969
+ const path = resolve13(outputPath);
70733
70970
  mkdirSync6(dirname8(path), { recursive: true });
70734
70971
  writeFileSync3(path, `${JSON.stringify(backup, null, 2)}
70735
70972
  `);
70736
70973
  return path;
70737
70974
  }
70738
70975
  function readLocalBackupFile(path) {
70739
- return JSON.parse(readFileSync8(resolve12(path), "utf-8"));
70976
+ return JSON.parse(readFileSync8(resolve13(path), "utf-8"));
70740
70977
  }
70741
70978
  function verifyLocalBackup(value, options = {}, db) {
70742
70979
  const verifiedAt = options.verified_at ?? now();
@@ -72186,7 +72423,7 @@ __export(exports_local_extensions, {
72186
72423
  });
72187
72424
  import { createHash as createHash9, createVerify } from "crypto";
72188
72425
  import { existsSync as existsSync13, readdirSync as readdirSync3, readFileSync as readFileSync9, statSync as statSync6 } from "fs";
72189
- import { basename as basename5, join as join11, resolve as resolve13 } from "path";
72426
+ import { basename as basename5, join as join11, resolve as resolve14 } from "path";
72190
72427
  function isObject3(value) {
72191
72428
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
72192
72429
  }
@@ -72444,7 +72681,7 @@ function verifyExtensionSignature(input) {
72444
72681
  return verifier.verify(input.public_key, decodeSignature(input.signature));
72445
72682
  }
72446
72683
  function inspectExtensionSource(source3) {
72447
- const resolved = resolve13(source3);
72684
+ const resolved = resolve14(source3);
72448
72685
  if (!existsSync13(resolved))
72449
72686
  throw new Error(`extension source not found: ${source3}`);
72450
72687
  const stat = statSync6(resolved);
@@ -72542,7 +72779,7 @@ function testExtensionCompatibility(sourceOrManifest) {
72542
72779
  function projectExtensionSources(projectPath) {
72543
72780
  if (!projectPath)
72544
72781
  return [];
72545
- const root = resolve13(projectPath);
72782
+ const root = resolve14(projectPath);
72546
72783
  const candidates = [
72547
72784
  join11(root, "todos.extension.json"),
72548
72785
  join11(root, ".todos", "todos.extension.json")
@@ -72561,7 +72798,7 @@ function projectExtensionSources(projectPath) {
72561
72798
  }
72562
72799
  function discoverLocalExtensions(options = {}) {
72563
72800
  const config2 = loadConfig();
72564
- const projectPath = options.project_path ? resolve13(options.project_path) : null;
72801
+ const projectPath = options.project_path ? resolve14(options.project_path) : null;
72565
72802
  const configuredSources = [
72566
72803
  ...config2.extension_sources || [],
72567
72804
  ...projectPath ? config2.project_overrides?.[projectPath]?.extension_sources || [] : []
@@ -72569,7 +72806,7 @@ function discoverLocalExtensions(options = {}) {
72569
72806
  const sources = Array.from(new Set([
72570
72807
  ...configuredSources,
72571
72808
  ...projectExtensionSources(projectPath || undefined)
72572
- ])).map((source3) => projectPath && !source3.startsWith("/") ? resolve13(projectPath, source3) : resolve13(source3));
72809
+ ])).map((source3) => projectPath && !source3.startsWith("/") ? resolve14(projectPath, source3) : resolve14(source3));
72573
72810
  const warnings = [];
72574
72811
  const discovered = [];
72575
72812
  for (const source3 of sources) {
@@ -75519,7 +75756,8 @@ function writeQueue(task2, queue, actor, action, db) {
75519
75756
  logTaskChange(task2.id, `review_queue.${action}`, "review_queue", null, JSON.stringify(queue), actor, d);
75520
75757
  emitLocalEventHooksQuiet({
75521
75758
  type: `review.${action}`,
75522
- payload: { task_id: task2.id, queue: queue.queue, state: queue.state, reviewer: queue.reviewer, claimed_by: queue.claimed_by }
75759
+ payload: { task_id: task2.id, queue: queue.queue, state: queue.state, reviewer: queue.reviewer, claimed_by: queue.claimed_by },
75760
+ databasePath: databasePathFromDatabase(d)
75523
75761
  });
75524
75762
  return itemFromTask(taskOrThrow2(task2.id, d), queue, d);
75525
75763
  }
@@ -75823,6 +76061,7 @@ var init_review_queues = __esm(() => {
75823
76061
  init_database();
75824
76062
  init_tasks();
75825
76063
  init_config();
76064
+ init_event_emission_safety();
75826
76065
  init_event_hooks();
75827
76066
  init_task_contracts();
75828
76067
  STATES = new Set(["requested", "claimed", "approved", "changes_requested", "returned", "reopened"]);
@@ -76978,7 +77217,7 @@ __export(exports_extract, {
76978
77217
  });
76979
77218
  import { existsSync as existsSync14, readFileSync as readFileSync10, statSync as statSync7 } from "fs";
76980
77219
  import { createHash as createHash11 } from "crypto";
76981
- import { relative as relative5, resolve as resolve14, join as join12 } from "path";
77220
+ import { relative as relative5, resolve as resolve15, join as join12 } from "path";
76982
77221
  function stableHash(value) {
76983
77222
  return createHash11("sha256").update(value).digest("hex");
76984
77223
  }
@@ -76986,7 +77225,7 @@ function normalizePathForMatch(value) {
76986
77225
  return value.replace(/\\/g, "/").replace(/^\.\//, "");
76987
77226
  }
76988
77227
  function readGitignorePatterns(basePath) {
76989
- const root = statSync7(basePath).isFile() ? resolve14(basePath, "..") : basePath;
77228
+ const root = statSync7(basePath).isFile() ? resolve15(basePath, "..") : basePath;
76990
77229
  const gitignorePath = join12(root, ".gitignore");
76991
77230
  if (!existsSync14(gitignorePath))
76992
77231
  return [];
@@ -77122,7 +77361,7 @@ function collectFiles(basePath, extensions, excludes, respectGitignore) {
77122
77361
  return files.sort();
77123
77362
  }
77124
77363
  function buildCodebaseIndex(options) {
77125
- const basePath = resolve14(options.path);
77364
+ const basePath = resolve15(options.path);
77126
77365
  const tags = options.patterns || [...EXTRACT_TAGS];
77127
77366
  const extensions = options.extensions ? new Set(options.extensions.map((e) => e.startsWith(".") ? e : `.${e}`)) : DEFAULT_EXTENSIONS;
77128
77367
  const excludes = options.exclude || [];
@@ -77133,7 +77372,7 @@ function buildCodebaseIndex(options) {
77133
77372
  const fullPath = statSync7(basePath).isFile() ? basePath : join12(basePath, file);
77134
77373
  try {
77135
77374
  const source3 = readFileSync10(fullPath, "utf-8");
77136
- const relPath = statSync7(basePath).isFile() ? relative5(resolve14(basePath, ".."), fullPath) : file;
77375
+ const relPath = statSync7(basePath).isFile() ? relative5(resolve15(basePath, ".."), fullPath) : file;
77137
77376
  indexed.push({
77138
77377
  file: relPath,
77139
77378
  checksum: stableHash(source3).slice(0, 24),
@@ -77153,7 +77392,7 @@ function buildCodebaseIndex(options) {
77153
77392
  };
77154
77393
  }
77155
77394
  function extractTodos(options, db) {
77156
- const basePath = resolve14(options.path);
77395
+ const basePath = resolve15(options.path);
77157
77396
  const tags = options.patterns || [...EXTRACT_TAGS];
77158
77397
  const extensions = options.extensions ? new Set(options.extensions.map((e) => e.startsWith(".") ? e : `.${e}`)) : DEFAULT_EXTENSIONS;
77159
77398
  const excludes = options.exclude || [];
@@ -77164,7 +77403,7 @@ function extractTodos(options, db) {
77164
77403
  const fullPath = statSync7(basePath).isFile() ? basePath : join12(basePath, file);
77165
77404
  try {
77166
77405
  const source3 = readFileSync10(fullPath, "utf-8");
77167
- const relPath = statSync7(basePath).isFile() ? relative5(resolve14(basePath, ".."), fullPath) : file;
77406
+ const relPath = statSync7(basePath).isFile() ? relative5(resolve15(basePath, ".."), fullPath) : file;
77168
77407
  const comments = extractFromSource(source3, relPath, tags);
77169
77408
  allComments.push(...comments);
77170
77409
  } catch {}
@@ -77258,7 +77497,7 @@ async function watchSourceTodos(options, onRun) {
77258
77497
  const interval = Math.max(100, options.interval_ms || 2000);
77259
77498
  const once = options.once !== false && (!options.max_runs || options.max_runs <= 1);
77260
77499
  const maxRuns = options.max_runs ?? (once ? 1 : Number.POSITIVE_INFINITY);
77261
- const root = resolve14(options.path);
77500
+ const root = resolve15(options.path);
77262
77501
  const runs = [];
77263
77502
  let previous = new Map;
77264
77503
  for (let runNumber = 1;runNumber <= maxRuns; runNumber++) {
@@ -78641,8 +78880,8 @@ __export(exports_environment_snapshots, {
78641
78880
  import { createHash as createHash12 } from "crypto";
78642
78881
  import { existsSync as existsSync15, readFileSync as readFileSync11, statSync as statSync8 } from "fs";
78643
78882
  import { hostname as hostname3, platform, arch } from "os";
78644
- import { dirname as dirname9, join as join14, resolve as resolve15 } from "path";
78645
- import { tmpdir as tmpdir2 } from "os";
78883
+ import { dirname as dirname9, join as join14, resolve as resolve16 } from "path";
78884
+ import { tmpdir as tmpdir3 } from "os";
78646
78885
  function sha2566(value) {
78647
78886
  return createHash12("sha256").update(value).digest("hex");
78648
78887
  }
@@ -78755,15 +78994,15 @@ function commandEnv(env, includeValues) {
78755
78994
  function defaultSnapshotDir() {
78756
78995
  const dbPath = getDatabasePath();
78757
78996
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
78758
- return join14(tmpdir2(), "hasna-todos", "environment-snapshots");
78759
- return join14(dirname9(resolve15(dbPath)), "environment-snapshots");
78997
+ return join14(tmpdir3(), "hasna-todos", "environment-snapshots");
78998
+ return join14(dirname9(resolve16(dbPath)), "environment-snapshots");
78760
78999
  }
78761
79000
  function snapshotWithId(snapshot) {
78762
79001
  const digest = sha2566(JSON.stringify(snapshot)).slice(0, 24);
78763
79002
  return { id: `env_${digest}`, ...snapshot };
78764
79003
  }
78765
79004
  function captureEnvironmentSnapshot(input = {}) {
78766
- const root = resolve15(input.root || process.cwd());
79005
+ const root = resolve16(input.root || process.cwd());
78767
79006
  const env = input.env || process.env;
78768
79007
  const warnings = [];
78769
79008
  const manifests = MANIFEST_FILES.map((file) => manifestRecord(root, file)).filter((file) => Boolean(file));
@@ -78803,13 +79042,13 @@ function captureEnvironmentSnapshot(input = {}) {
78803
79042
  });
78804
79043
  }
78805
79044
  function writeEnvironmentSnapshot(snapshot, outputPath) {
78806
- const path = outputPath ? resolve15(outputPath) : join14(defaultSnapshotDir(), `${snapshot.id}.json`);
79045
+ const path = outputPath ? resolve16(outputPath) : join14(defaultSnapshotDir(), `${snapshot.id}.json`);
78807
79046
  ensureDir2(dirname9(path));
78808
79047
  writeJsonFile(path, snapshot);
78809
79048
  return path;
78810
79049
  }
78811
79050
  function readEnvironmentSnapshot(path) {
78812
- const snapshot = readJsonFile(resolve15(path));
79051
+ const snapshot = readJsonFile(resolve16(path));
78813
79052
  if (!snapshot || snapshot.schema_version !== 1 || typeof snapshot.id !== "string") {
78814
79053
  throw new Error(`Invalid environment snapshot: ${path}`);
78815
79054
  }