@hasna/todos 0.13.5 → 0.13.7

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.
@@ -70,7 +70,7 @@ var package_default;
70
70
  var init_package = __esm(() => {
71
71
  package_default = {
72
72
  name: "@hasna/todos",
73
- version: "0.13.5",
73
+ version: "0.13.7",
74
74
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
75
75
  type: "module",
76
76
  main: "dist/index.js",
@@ -1759,6 +1759,10 @@ class PostgresJsonRecordStore {
1759
1759
  conds.push(`payload->>'assigned_to' = ${p(filter.assigned_to)}`);
1760
1760
  if (filter.agent_id !== undefined)
1761
1761
  conds.push(`payload->>'agent_id' = ${p(filter.agent_id)}`);
1762
+ if (filter.created_by !== undefined)
1763
+ conds.push(`LOWER(payload->>'created_by') = LOWER(${p(filter.created_by)})`);
1764
+ if (filter.not_created_by !== undefined)
1765
+ conds.push(`(payload->>'created_by' IS NULL OR LOWER(payload->>'created_by') <> LOWER(${p(filter.not_created_by)}))`);
1762
1766
  if (filter.session_id !== undefined)
1763
1767
  conds.push(`payload->>'session_id' = ${p(filter.session_id)}`);
1764
1768
  if (filter.tags?.length) {
@@ -2272,7 +2276,7 @@ async function createTask(input, store, context) {
2272
2276
  description: input.description ?? null,
2273
2277
  status: input.status ?? "pending",
2274
2278
  priority: input.priority ?? "medium",
2275
- agent_id: input.agent_id ?? null,
2279
+ agent_id: input.agent_id ?? context?.agentId ?? null,
2276
2280
  assigned_to: input.assigned_to ?? null,
2277
2281
  session_id: input.session_id ?? context?.sessionId ?? null,
2278
2282
  working_dir: input.working_dir ?? null,
@@ -2297,7 +2301,8 @@ async function createTask(input, store, context) {
2297
2301
  confidence: input.confidence ?? null,
2298
2302
  reason: input.reason ?? null,
2299
2303
  spawned_from_session: input.spawned_from_session ?? null,
2300
- assigned_by: input.assigned_by ?? null,
2304
+ assigned_by: input.assigned_by ?? input.agent_id ?? context?.agentId ?? null,
2305
+ created_by: input.created_by ?? input.agent_id ?? context?.agentId ?? null,
2301
2306
  assigned_from_project: input.assigned_from_project ?? null,
2302
2307
  task_type: input.task_type ?? null,
2303
2308
  cost_tokens: 0,
@@ -2334,7 +2339,8 @@ async function updateTask(id, input, store) {
2334
2339
  tags: input.tags ?? existing.tags,
2335
2340
  metadata: input.metadata ?? existing.metadata,
2336
2341
  requires_approval: input.requires_approval ?? existing.requires_approval,
2337
- task_list_id: input.task_list_id !== undefined ? input.task_list_id : existing.task_list_id
2342
+ task_list_id: input.task_list_id !== undefined ? input.task_list_id : existing.task_list_id,
2343
+ created_by: existing.created_by
2338
2344
  };
2339
2345
  await store.upsert("tasks", task);
2340
2346
  return task;
@@ -7475,6 +7481,7 @@ function ensureSchema(db) {
7475
7481
  ensureColumn("tasks", "reason", "TEXT");
7476
7482
  ensureColumn("tasks", "spawned_from_session", "TEXT");
7477
7483
  ensureColumn("tasks", "assigned_by", "TEXT");
7484
+ ensureColumn("tasks", "created_by", "TEXT");
7478
7485
  ensureColumn("tasks", "assigned_from_project", "TEXT");
7479
7486
  ensureColumn("tasks", "started_at", "TEXT");
7480
7487
  ensureColumn("tasks", "task_type", "TEXT");
@@ -7637,6 +7644,8 @@ function ensureSchema(db) {
7637
7644
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_sources_project ON project_sources(project_id)");
7638
7645
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_sources_type ON project_sources(type)");
7639
7646
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_assigned_by ON tasks(assigned_by)");
7647
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_created_by ON tasks(created_by)");
7648
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_assigned_created ON tasks(assigned_to, created_by)");
7640
7649
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_rel_source ON task_relationships(source_task_id)");
7641
7650
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_rel_target ON task_relationships(target_task_id)");
7642
7651
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_rel_type ON task_relationships(relationship_type)");
@@ -13283,13 +13292,14 @@ function createTask2(input, db) {
13283
13292
  const timestamp2 = now();
13284
13293
  const tags = input.tags || [];
13285
13294
  const machineId = currentStorageMachineId(d);
13295
+ const createdBy = input.created_by || input.agent_id || null;
13286
13296
  const assignedBy = input.assigned_by || input.agent_id;
13287
13297
  const assignedFromProject = input.assigned_from_project || null;
13288
13298
  let id = uuid();
13289
13299
  for (let attempt = 0;attempt < 3; attempt++) {
13290
13300
  try {
13291
- 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)
13292
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
13301
+ 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, created_by, assigned_from_project, task_type, machine_id)
13302
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
13293
13303
  id,
13294
13304
  null,
13295
13305
  input.project_id || null,
@@ -13325,6 +13335,7 @@ function createTask2(input, db) {
13325
13335
  input.reason || null,
13326
13336
  input.spawned_from_session || null,
13327
13337
  assignedBy || null,
13338
+ createdBy,
13328
13339
  assignedFromProject || null,
13329
13340
  input.task_type || null,
13330
13341
  machineId
@@ -13434,6 +13445,14 @@ function listTasks2(filter = {}, db) {
13434
13445
  conditions.push("agent_id = ?");
13435
13446
  params.push(filter.agent_id);
13436
13447
  }
13448
+ if (filter.created_by) {
13449
+ conditions.push("LOWER(created_by) = LOWER(?)");
13450
+ params.push(filter.created_by);
13451
+ }
13452
+ if (filter.not_created_by) {
13453
+ conditions.push("(created_by IS NULL OR LOWER(created_by) != LOWER(?))");
13454
+ params.push(filter.not_created_by);
13455
+ }
13437
13456
  if (filter.session_id) {
13438
13457
  conditions.push("session_id = ?");
13439
13458
  params.push(filter.session_id);
@@ -13587,6 +13606,14 @@ function countTasks(filter = {}, db) {
13587
13606
  conditions.push("agent_id = ?");
13588
13607
  params.push(filter.agent_id);
13589
13608
  }
13609
+ if (filter.created_by) {
13610
+ conditions.push("LOWER(created_by) = LOWER(?)");
13611
+ params.push(filter.created_by);
13612
+ }
13613
+ if (filter.not_created_by) {
13614
+ conditions.push("(created_by IS NULL OR LOWER(created_by) != LOWER(?))");
13615
+ params.push(filter.not_created_by);
13616
+ }
13590
13617
  if (filter.session_id) {
13591
13618
  conditions.push("session_id = ?");
13592
13619
  params.push(filter.session_id);
@@ -13647,6 +13674,10 @@ function updateTask2(id, input, db) {
13647
13674
  sets.push("description = ?");
13648
13675
  params.push(input.description);
13649
13676
  }
13677
+ if (input.agent_id !== undefined) {
13678
+ sets.push("agent_id = ?");
13679
+ params.push(input.agent_id);
13680
+ }
13650
13681
  if (input.status !== undefined) {
13651
13682
  if (input.status === "completed") {
13652
13683
  checkCompletionGuard(task, task.assigned_to || task.agent_id || null, d);
@@ -18157,11 +18188,15 @@ async function handleCreateTask(req, ctx, json2, taskToSummary2) {
18157
18188
  const body = await req.json();
18158
18189
  if (!body.title)
18159
18190
  return json2({ error: "Missing 'title'" }, 400);
18191
+ const createdBy = body.created_by ?? body.agent_id ?? "dashboard";
18160
18192
  const task = createTask2({
18161
18193
  title: body.title,
18162
18194
  description: body.description,
18163
18195
  priority: body.priority,
18164
- project_id: body.project_id
18196
+ project_id: body.project_id,
18197
+ agent_id: body.agent_id ?? createdBy,
18198
+ created_by: createdBy,
18199
+ ...body.assigned_to ? { assigned_to: body.assigned_to } : {}
18165
18200
  });
18166
18201
  ctx.broadcastEvent({ type: "task", task_id: task.id, action: "created", agent_id: task.agent_id, project_id: task.project_id });
18167
18202
  return json2(taskToSummary2(task), 201);
@@ -19425,7 +19460,11 @@ function createLocalSqliteTodosStorageAdapter(options = {}) {
19425
19460
  sync: true
19426
19461
  },
19427
19462
  tasks: {
19428
- create: (input) => createTask2(input, database()),
19463
+ create: (input, context) => createTask2({
19464
+ ...input,
19465
+ agent_id: input.agent_id ?? context?.agentId,
19466
+ created_by: input.created_by ?? input.agent_id ?? context?.agentId
19467
+ }, database()),
19429
19468
  get: (id) => getTask(id, database()),
19430
19469
  resolveRef: (ref) => resolveTaskRefLocal(database(), ref),
19431
19470
  list: (filter = {}) => listTasksMaybeSearch(filter, database()),
@@ -22036,6 +22075,8 @@ async function handleV1Request(req, url, dependencies = {}) {
22036
22075
  ...url.searchParams.get("task_list_id") ? { task_list_id: url.searchParams.get("task_list_id") } : {},
22037
22076
  ...url.searchParams.get("assigned_to") ? { assigned_to: url.searchParams.get("assigned_to") } : {},
22038
22077
  ...url.searchParams.get("agent_id") ? { agent_id: url.searchParams.get("agent_id") } : {},
22078
+ ...url.searchParams.get("created_by") ? { created_by: url.searchParams.get("created_by") } : {},
22079
+ ...url.searchParams.get("not_created_by") ? { not_created_by: url.searchParams.get("not_created_by") } : {},
22039
22080
  ...url.searchParams.get("tags") ? {
22040
22081
  tags: url.searchParams.get("tags").split(",").map((tag) => tag.trim()).filter(Boolean)
22041
22082
  } : {},
@@ -44205,6 +44246,50 @@ var init_token_utils = __esm(() => {
44205
44246
  };
44206
44247
  });
44207
44248
 
44249
+ // src/lib/creator-identity.ts
44250
+ import { existsSync as existsSync8, rmSync as rmSync2 } from "fs";
44251
+ import { join as join8 } from "path";
44252
+ function identityFilePath() {
44253
+ return join8(getTodosGlobalDir(), "identity.json");
44254
+ }
44255
+ function readPersistedIdentity() {
44256
+ const path = identityFilePath();
44257
+ if (!existsSync8(path))
44258
+ return null;
44259
+ const parsed = readJsonFile(path);
44260
+ if (!parsed || typeof parsed.agent_id !== "string" || !parsed.agent_id.trim())
44261
+ return null;
44262
+ return parsed;
44263
+ }
44264
+ function canonicalAgentRef(value) {
44265
+ return value.trim().toLowerCase();
44266
+ }
44267
+ function isProcessBoundSource(source) {
44268
+ return source === "explicit" || source === "env";
44269
+ }
44270
+ function resolveWritableIdentity(explicit) {
44271
+ const resolved = resolveCreatorIdentity(explicit);
44272
+ if (!isProcessBoundSource(resolved.source))
44273
+ return { agent_id: null, source: "none" };
44274
+ return resolved;
44275
+ }
44276
+ function resolveCreatorIdentity(explicit) {
44277
+ const fromExplicit = explicit?.trim();
44278
+ if (fromExplicit)
44279
+ return { agent_id: canonicalAgentRef(fromExplicit), source: "explicit" };
44280
+ const fromEnv = (process.env["TODOS_AGENT_ID"] || process.env["HASNA_TODOS_AGENT_ID"] || "").trim();
44281
+ if (fromEnv)
44282
+ return { agent_id: canonicalAgentRef(fromEnv), source: "env" };
44283
+ const persisted = readPersistedIdentity();
44284
+ if (persisted) {
44285
+ return { agent_id: canonicalAgentRef(persisted.agent_name || persisted.agent_id), source: "persisted" };
44286
+ }
44287
+ return { agent_id: null, source: "none" };
44288
+ }
44289
+ var init_creator_identity = __esm(() => {
44290
+ init_sync_utils();
44291
+ });
44292
+
44208
44293
  // src/pr-groups/http-client.ts
44209
44294
  function normalizeBaseUrl(value) {
44210
44295
  const url = new URL(value);
@@ -45554,6 +45639,8 @@ function registerTaskCrudTools(server, ctx) {
45554
45639
  project_id: exports_external.string().optional().describe("Project ID"),
45555
45640
  task_list_id: exports_external.string().optional().describe("Task list ID"),
45556
45641
  assigned_to: exports_external.string().optional().describe("Agent ID or name to assign to"),
45642
+ created_by: exports_external.string().optional().describe("Agent who FILED this task. Defaults to the ambient agent identity (todos init / TODOS_AGENT_ID)."),
45643
+ unassigned: exports_external.boolean().optional().describe("Deliberately file with no assignee. Without it, the task defaults to the filer."),
45557
45644
  depends_on: exports_external.array(exports_external.string()).optional().describe("Array of task IDs this task depends on"),
45558
45645
  short_id: exports_external.string().nullable().optional().describe("Short ID (auto-generated if not provided, disabled if null)"),
45559
45646
  tags: exports_external.array(exports_external.string()).optional().describe("Tags for the task"),
@@ -45564,12 +45651,19 @@ function registerTaskCrudTools(server, ctx) {
45564
45651
  retry_count: exports_external.number().optional().describe("Max retry count for agent failures")
45565
45652
  }, async (params) => {
45566
45653
  try {
45567
- const { depends_on, assigned_to, project_id, task_list_id, tags, estimate, confidence, retry_count, deadline, ...rest } = params;
45654
+ const { depends_on, assigned_to, project_id, task_list_id, tags, estimate, confidence, retry_count, deadline, created_by, unassigned, ...rest } = params;
45655
+ const creator = resolveCreatorIdentity(created_by);
45656
+ const router = resolveWritableIdentity(created_by);
45657
+ const assignee = assigned_to || (unassigned ? undefined : router.agent_id || undefined);
45568
45658
  const cloud = getTodosCloudClient();
45569
45659
  if (cloud) {
45570
45660
  const payload = { ...rest };
45571
- if (assigned_to)
45572
- payload.assigned_to = assigned_to;
45661
+ if (creator.agent_id)
45662
+ payload.created_by = creator.agent_id;
45663
+ if (router.agent_id)
45664
+ payload.agent_id = payload.agent_id ?? router.agent_id;
45665
+ if (assignee)
45666
+ payload.assigned_to = assignee;
45573
45667
  if (project_id)
45574
45668
  payload.project_id = project_id;
45575
45669
  if (task_list_id)
@@ -45590,8 +45684,12 @@ function registerTaskCrudTools(server, ctx) {
45590
45684
  return { content: [{ type: "text", text: mutationTaskResponse(created) }] };
45591
45685
  }
45592
45686
  const resolved = { ...rest };
45593
- if (assigned_to)
45594
- resolved.assigned_to = resolveAssignee(assigned_to);
45687
+ if (creator.agent_id)
45688
+ resolved.created_by = creator.agent_id;
45689
+ if (router.agent_id)
45690
+ resolved.agent_id = resolved.agent_id ?? router.agent_id;
45691
+ if (assignee)
45692
+ resolved.assigned_to = resolveAssignee(assignee);
45595
45693
  if (project_id)
45596
45694
  resolved.project_id = resolveId(project_id, "projects");
45597
45695
  if (task_list_id)
@@ -45677,6 +45775,8 @@ function registerTaskCrudTools(server, ctx) {
45677
45775
  project_id: exports_external.string().optional().describe("Filter by project"),
45678
45776
  task_list_id: exports_external.string().optional().describe("Filter by task list"),
45679
45777
  assigned_to: exports_external.string().optional().describe("Filter by assignee (agent ID or name, empty string = unassigned)"),
45778
+ created_by: exports_external.string().optional().describe("Filter by the agent who FILED the task"),
45779
+ not_created_by: exports_external.string().optional().describe('Exclude tasks filed by this agent. With assigned_to=<me> this is the "my inbox, minus my own filings" query.'),
45680
45780
  tags: exports_external.array(exports_external.string()).optional().describe("Filter by tags (AND logic)"),
45681
45781
  created_after: exports_external.string().optional().describe("ISO date \u2014 tasks created after this date"),
45682
45782
  created_before: exports_external.string().optional().describe("ISO date \u2014 tasks created before this date"),
@@ -45869,11 +45969,12 @@ var init_task_crud2 = __esm(() => {
45869
45969
  init_tasks();
45870
45970
  init_types();
45871
45971
  init_token_utils();
45972
+ init_creator_identity();
45872
45973
  init_cloud_router();
45873
45974
  });
45874
45975
 
45875
45976
  // src/lib/project-bootstrap.ts
45876
- import { existsSync as existsSync8, readFileSync as readFileSync3, statSync as statSync4 } from "fs";
45977
+ import { existsSync as existsSync9, readFileSync as readFileSync3, statSync as statSync4 } from "fs";
45877
45978
  import { basename as basename3, dirname as dirname6, resolve as resolve9 } from "path";
45878
45979
  function safeStat(path) {
45879
45980
  try {
@@ -45892,7 +45993,7 @@ function canonicalPath(input) {
45892
45993
  function findUp(start, marker) {
45893
45994
  let current = canonicalPath(start);
45894
45995
  while (true) {
45895
- if (existsSync8(resolve9(current, marker)))
45996
+ if (existsSync9(resolve9(current, marker)))
45896
45997
  return current;
45897
45998
  const parent = dirname6(current);
45898
45999
  if (parent === current)
@@ -45904,7 +46005,7 @@ function readPackageJson(path) {
45904
46005
  if (!path)
45905
46006
  return null;
45906
46007
  const file = resolve9(path, "package.json");
45907
- if (!existsSync8(file))
46008
+ if (!existsSync9(file))
45908
46009
  return null;
45909
46010
  try {
45910
46011
  const parsed = JSON.parse(readFileSync3(file, "utf-8"));
@@ -45926,7 +46027,7 @@ function workspaceMarker(root, rootPackage) {
45926
46027
  if (rootPackage?.workspaces)
45927
46028
  markers.push("package.json#workspaces");
45928
46029
  for (const marker of ["pnpm-workspace.yaml", "turbo.json", "nx.json", "lerna.json", "rush.json", "bun.lock", "bun.lockb"]) {
45929
- if (existsSync8(resolve9(root, marker)))
46030
+ if (existsSync9(resolve9(root, marker)))
45930
46031
  markers.push(marker);
45931
46032
  }
45932
46033
  const kind = markers.find((marker) => marker !== "bun.lock" && marker !== "bun.lockb") ?? null;
@@ -46239,7 +46340,7 @@ var init_tags = __esm(() => {
46239
46340
  });
46240
46341
 
46241
46342
  // src/lib/retention-cleanup.ts
46242
- import { existsSync as existsSync9, unlinkSync } from "fs";
46343
+ import { existsSync as existsSync10, unlinkSync } from "fs";
46243
46344
  function normalizeScopes(scopes) {
46244
46345
  if (!scopes || scopes.length === 0)
46245
46346
  return [...ALL_SCOPES];
@@ -46442,7 +46543,7 @@ function applyRetentionCleanup(input, db) {
46442
46543
  for (const artifact of report.candidates.artifact_files) {
46443
46544
  try {
46444
46545
  const path = artifactStorePath(artifact.relative_path);
46445
- if (!existsSync9(path)) {
46546
+ if (!existsSync10(path)) {
46446
46547
  report.warnings.push(`stored artifact already missing: ${artifact.relative_path}`);
46447
46548
  continue;
46448
46549
  }
@@ -46469,8 +46570,8 @@ var init_retention_cleanup = __esm(() => {
46469
46570
  });
46470
46571
 
46471
46572
  // src/lib/mention-resolver.ts
46472
- import { existsSync as existsSync10, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync5 } from "fs";
46473
- import { basename as basename4, isAbsolute, join as join8, relative as relative3, resolve as resolve10, sep as sep3 } from "path";
46573
+ import { existsSync as existsSync11, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync5 } from "fs";
46574
+ import { basename as basename4, isAbsolute, join as join9, relative as relative3, resolve as resolve10, sep as sep3 } from "path";
46474
46575
  function blankResolution(parsed) {
46475
46576
  return {
46476
46577
  input: parsed.input,
@@ -46568,7 +46669,7 @@ function resolveFile(parsed, workspace) {
46568
46669
  return resolution;
46569
46670
  }
46570
46671
  resolution.path = relPath;
46571
- if (!existsSync10(absolutePath)) {
46672
+ if (!existsSync11(absolutePath)) {
46572
46673
  resolution.warnings.push("file does not exist in the local workspace");
46573
46674
  return resolution;
46574
46675
  }
@@ -46601,7 +46702,7 @@ function walkSourceFiles(root, current = root, files = []) {
46601
46702
  if (SKIP_DIRS.has(entry2.name))
46602
46703
  continue;
46603
46704
  }
46604
- const absolutePath = join8(current, entry2.name);
46705
+ const absolutePath = join9(current, entry2.name);
46605
46706
  if (entry2.isDirectory()) {
46606
46707
  if (!SKIP_DIRS.has(entry2.name))
46607
46708
  walkSourceFiles(root, absolutePath, files);
@@ -50293,7 +50394,7 @@ var init_audit_ledger = __esm(() => {
50293
50394
 
50294
50395
  // src/lib/release-compatibility.ts
50295
50396
  import { readFileSync as readFileSync5 } from "fs";
50296
- import { join as join9, resolve as resolve12 } from "path";
50397
+ import { join as join10, resolve as resolve12 } from "path";
50297
50398
  import { Database as Database2 } from "bun:sqlite";
50298
50399
  function pass(id, message, details) {
50299
50400
  return { id, status: "passed", message, details };
@@ -50305,7 +50406,7 @@ function warn(id, message, details) {
50305
50406
  return { id, status: "warning", message, details };
50306
50407
  }
50307
50408
  function readPackageJson2(root) {
50308
- return JSON.parse(readFileSync5(join9(root, "package.json"), "utf8"));
50409
+ return JSON.parse(readFileSync5(join10(root, "package.json"), "utf8"));
50309
50410
  }
50310
50411
  function sortedKeys(value) {
50311
50412
  return Object.keys(value ?? {}).sort((left, right) => left.localeCompare(right));
@@ -80200,7 +80301,7 @@ var init_agent_run_dispatcher = __esm(() => {
80200
80301
  });
80201
80302
 
80202
80303
  // src/lib/verification-providers.ts
80203
- import { existsSync as existsSync11, readFileSync as readFileSync6 } from "fs";
80304
+ import { existsSync as existsSync12, readFileSync as readFileSync6 } from "fs";
80204
80305
  function normalizeName5(name) {
80205
80306
  const normalized = name.trim().toLowerCase();
80206
80307
  if (!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(normalized)) {
@@ -80352,7 +80453,7 @@ Timed out after ${provider.timeout_ms}ms`);
80352
80453
  };
80353
80454
  }
80354
80455
  function runCiLogProvider(input) {
80355
- const text = input.log_text ?? (input.log_path && existsSync11(input.log_path) ? readFileSync6(input.log_path, "utf-8") : "");
80456
+ const text = input.log_text ?? (input.log_path && existsSync12(input.log_path) ? readFileSync6(input.log_path, "utf-8") : "");
80356
80457
  return {
80357
80458
  status: classifyLog(text),
80358
80459
  attempts: 1,
@@ -80364,7 +80465,7 @@ function runBrowserProvider(input) {
80364
80465
  if (!input.artifact_path) {
80365
80466
  return { status: "unknown", attempts: 1, exit_code: null, output_summary: "browser provider needs a screenshot or artifact path" };
80366
80467
  }
80367
- if (!existsSync11(input.artifact_path)) {
80468
+ if (!existsSync12(input.artifact_path)) {
80368
80469
  return { status: "failed", attempts: 1, exit_code: null, output_summary: `artifact not found: ${input.artifact_path}` };
80369
80470
  }
80370
80471
  return {
@@ -83447,6 +83548,7 @@ function task(input) {
83447
83548
  reason: "Bundled deterministic onboarding fixture",
83448
83549
  spawned_from_session: null,
83449
83550
  assigned_by: null,
83551
+ created_by: null,
83450
83552
  assigned_from_project: null,
83451
83553
  task_type: "onboarding",
83452
83554
  cost_tokens: 0,
@@ -84644,8 +84746,8 @@ __export(exports_local_extensions, {
84644
84746
  discoverLocalExtensions: () => discoverLocalExtensions
84645
84747
  });
84646
84748
  import { createHash as createHash10, createVerify } from "crypto";
84647
- import { existsSync as existsSync12, readdirSync as readdirSync3, readFileSync as readFileSync8, statSync as statSync6 } from "fs";
84648
- import { basename as basename5, join as join10, resolve as resolve14 } from "path";
84749
+ import { existsSync as existsSync13, readdirSync as readdirSync3, readFileSync as readFileSync8, statSync as statSync6 } from "fs";
84750
+ import { basename as basename5, join as join11, resolve as resolve14 } from "path";
84649
84751
  function isObject3(value) {
84650
84752
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
84651
84753
  }
@@ -84904,10 +85006,10 @@ function verifyExtensionSignature(input) {
84904
85006
  }
84905
85007
  function inspectExtensionSource(source3) {
84906
85008
  const resolved = resolve14(source3);
84907
- if (!existsSync12(resolved))
85009
+ if (!existsSync13(resolved))
84908
85010
  throw new Error(`extension source not found: ${source3}`);
84909
85011
  const stat = statSync6(resolved);
84910
- const manifestPath = stat.isDirectory() ? [join10(resolved, "todos.extension.json"), join10(resolved, "extension.json")].find(existsSync12) : resolved;
85012
+ const manifestPath = stat.isDirectory() ? [join11(resolved, "todos.extension.json"), join11(resolved, "extension.json")].find(existsSync13) : resolved;
84911
85013
  if (!manifestPath)
84912
85014
  throw new Error(`extension directory ${source3} is missing todos.extension.json`);
84913
85015
  const raw = readFileSync8(manifestPath);
@@ -85003,20 +85105,20 @@ function projectExtensionSources(projectPath) {
85003
85105
  return [];
85004
85106
  const root = resolve14(projectPath);
85005
85107
  const candidates = [
85006
- join10(root, "todos.extension.json"),
85007
- join10(root, ".todos", "todos.extension.json")
85108
+ join11(root, "todos.extension.json"),
85109
+ join11(root, ".todos", "todos.extension.json")
85008
85110
  ];
85009
- const extensionDir = join10(root, ".todos", "extensions");
85010
- if (existsSync12(extensionDir)) {
85111
+ const extensionDir = join11(root, ".todos", "extensions");
85112
+ if (existsSync13(extensionDir)) {
85011
85113
  for (const entry2 of readdirSync3(extensionDir)) {
85012
85114
  if (entry2.startsWith("."))
85013
85115
  continue;
85014
- const full = join10(extensionDir, entry2);
85116
+ const full = join11(extensionDir, entry2);
85015
85117
  if (statSync6(full).isDirectory() || entry2.endsWith(".json"))
85016
85118
  candidates.push(full);
85017
85119
  }
85018
85120
  }
85019
- return candidates.filter(existsSync12);
85121
+ return candidates.filter(existsSync13);
85020
85122
  }
85021
85123
  function discoverLocalExtensions(options = {}) {
85022
85124
  const config2 = loadConfig();
@@ -89423,9 +89525,9 @@ __export(exports_extract, {
89423
89525
  buildCodebaseIndex: () => buildCodebaseIndex,
89424
89526
  EXTRACT_TAGS: () => EXTRACT_TAGS
89425
89527
  });
89426
- import { existsSync as existsSync13, readFileSync as readFileSync9, statSync as statSync7 } from "fs";
89528
+ import { existsSync as existsSync14, readFileSync as readFileSync9, statSync as statSync7 } from "fs";
89427
89529
  import { createHash as createHash12 } from "crypto";
89428
- import { relative as relative5, resolve as resolve15, join as join11 } from "path";
89530
+ import { relative as relative5, resolve as resolve15, join as join12 } from "path";
89429
89531
  function stableHash(value) {
89430
89532
  return createHash12("sha256").update(value).digest("hex");
89431
89533
  }
@@ -89434,8 +89536,8 @@ function normalizePathForMatch(value) {
89434
89536
  }
89435
89537
  function readGitignorePatterns(basePath) {
89436
89538
  const root = statSync7(basePath).isFile() ? resolve15(basePath, "..") : basePath;
89437
- const gitignorePath = join11(root, ".gitignore");
89438
- if (!existsSync13(gitignorePath))
89539
+ const gitignorePath = join12(root, ".gitignore");
89540
+ if (!existsSync14(gitignorePath))
89439
89541
  return [];
89440
89542
  try {
89441
89543
  return readFileSync9(gitignorePath, "utf-8").split(`
@@ -89577,7 +89679,7 @@ function buildCodebaseIndex(options) {
89577
89679
  const files = collectFiles(basePath, extensions, excludes, respectGitignore);
89578
89680
  const indexed = [];
89579
89681
  for (const file of files) {
89580
- const fullPath = statSync7(basePath).isFile() ? basePath : join11(basePath, file);
89682
+ const fullPath = statSync7(basePath).isFile() ? basePath : join12(basePath, file);
89581
89683
  try {
89582
89684
  const source3 = readFileSync9(fullPath, "utf-8");
89583
89685
  const relPath = statSync7(basePath).isFile() ? relative5(resolve15(basePath, ".."), fullPath) : file;
@@ -89608,7 +89710,7 @@ function extractTodos(options, db) {
89608
89710
  const files = collectFiles(basePath, extensions, excludes, respectGitignore);
89609
89711
  const allComments = [];
89610
89712
  for (const file of files) {
89611
- const fullPath = statSync7(basePath).isFile() ? basePath : join11(basePath, file);
89713
+ const fullPath = statSync7(basePath).isFile() ? basePath : join12(basePath, file);
89612
89714
  try {
89613
89715
  const source3 = readFileSync9(fullPath, "utf-8");
89614
89716
  const relPath = statSync7(basePath).isFile() ? relative5(resolve15(basePath, ".."), fullPath) : file;
@@ -90559,7 +90661,7 @@ __export(exports_builtin_templates, {
90559
90661
  BUILTIN_TEMPLATES: () => BUILTIN_TEMPLATES
90560
90662
  });
90561
90663
  import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync4 } from "fs";
90562
- import { join as join12 } from "path";
90664
+ import { join as join13 } from "path";
90563
90665
  function templateMetadata(template) {
90564
90666
  return {
90565
90667
  source: BUILTIN_TEMPLATE_LIBRARY_SOURCE,
@@ -90618,7 +90720,7 @@ function writeBuiltinTemplateFiles(directory) {
90618
90720
  mkdirSync7(directory, { recursive: true });
90619
90721
  const files = [];
90620
90722
  for (const entry2 of exportBuiltinTemplateFiles()) {
90621
- const path = join12(directory, entry2.filename);
90723
+ const path = join13(directory, entry2.filename);
90622
90724
  writeFileSync4(path, `${JSON.stringify(entry2.template, null, 2)}
90623
90725
  `, "utf-8");
90624
90726
  files.push(path);
@@ -91144,16 +91246,16 @@ __export(exports_environment_snapshots, {
91144
91246
  captureEnvironmentSnapshot: () => captureEnvironmentSnapshot
91145
91247
  });
91146
91248
  import { createHash as createHash13 } from "crypto";
91147
- import { existsSync as existsSync14, readFileSync as readFileSync10, statSync as statSync8 } from "fs";
91249
+ import { existsSync as existsSync15, readFileSync as readFileSync10, statSync as statSync8 } from "fs";
91148
91250
  import { hostname as hostname3, platform, arch } from "os";
91149
- import { dirname as dirname8, join as join13, resolve as resolve16 } from "path";
91251
+ import { dirname as dirname8, join as join14, resolve as resolve16 } from "path";
91150
91252
  import { tmpdir as tmpdir3 } from "os";
91151
91253
  function sha2567(value) {
91152
91254
  return createHash13("sha256").update(value).digest("hex");
91153
91255
  }
91154
91256
  function fileRecord(root, relativePath) {
91155
- const path = join13(root, relativePath);
91156
- if (!existsSync14(path))
91257
+ const path = join14(root, relativePath);
91258
+ if (!existsSync15(path))
91157
91259
  return null;
91158
91260
  const stat = statSync8(path);
91159
91261
  if (!stat.isFile())
@@ -91165,7 +91267,7 @@ function manifestRecord(root, relativePath) {
91165
91267
  const base = fileRecord(root, relativePath);
91166
91268
  if (!base)
91167
91269
  return null;
91168
- const parsed = readJsonFile(join13(root, relativePath));
91270
+ const parsed = readJsonFile(join14(root, relativePath));
91169
91271
  if (!parsed)
91170
91272
  return { ...base, redacted: {} };
91171
91273
  const redacted = redactValue({
@@ -91260,8 +91362,8 @@ function commandEnv(env, includeValues) {
91260
91362
  function defaultSnapshotDir() {
91261
91363
  const dbPath = getDatabasePath();
91262
91364
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
91263
- return join13(tmpdir3(), "hasna-todos", "environment-snapshots");
91264
- return join13(dirname8(resolve16(dbPath)), "environment-snapshots");
91365
+ return join14(tmpdir3(), "hasna-todos", "environment-snapshots");
91366
+ return join14(dirname8(resolve16(dbPath)), "environment-snapshots");
91265
91367
  }
91266
91368
  function snapshotWithId(snapshot) {
91267
91369
  const digest = sha2567(JSON.stringify(snapshot)).slice(0, 24);
@@ -91308,7 +91410,7 @@ function captureEnvironmentSnapshot(input = {}) {
91308
91410
  });
91309
91411
  }
91310
91412
  function writeEnvironmentSnapshot(snapshot, outputPath) {
91311
- const path = outputPath ? resolve16(outputPath) : join13(defaultSnapshotDir(), `${snapshot.id}.json`);
91413
+ const path = outputPath ? resolve16(outputPath) : join14(defaultSnapshotDir(), `${snapshot.id}.json`);
91312
91414
  ensureDir(dirname8(path));
91313
91415
  writeJsonFile(path, snapshot);
91314
91416
  return path;
@@ -92231,27 +92333,27 @@ __export(exports_serve, {
92231
92333
  SECURITY_HEADERS: () => SECURITY_HEADERS,
92232
92334
  MIME_TYPES: () => MIME_TYPES
92233
92335
  });
92234
- import { existsSync as existsSync15 } from "fs";
92235
- import { join as join14, dirname as dirname9, extname } from "path";
92336
+ import { existsSync as existsSync16 } from "fs";
92337
+ import { join as join15, dirname as dirname9, extname } from "path";
92236
92338
  import { fileURLToPath } from "url";
92237
92339
  function resolveDashboardDir() {
92238
92340
  const candidates = [];
92239
92341
  try {
92240
92342
  const scriptDir = dirname9(fileURLToPath(import.meta.url));
92241
- candidates.push(join14(scriptDir, "..", "dashboard", "dist"));
92242
- candidates.push(join14(scriptDir, "..", "..", "dashboard", "dist"));
92343
+ candidates.push(join15(scriptDir, "..", "dashboard", "dist"));
92344
+ candidates.push(join15(scriptDir, "..", "..", "dashboard", "dist"));
92243
92345
  } catch {}
92244
92346
  if (process.argv[1]) {
92245
92347
  const mainDir = dirname9(process.argv[1]);
92246
- candidates.push(join14(mainDir, "..", "dashboard", "dist"));
92247
- candidates.push(join14(mainDir, "..", "..", "dashboard", "dist"));
92348
+ candidates.push(join15(mainDir, "..", "dashboard", "dist"));
92349
+ candidates.push(join15(mainDir, "..", "..", "dashboard", "dist"));
92248
92350
  }
92249
- candidates.push(join14(process.cwd(), "dashboard", "dist"));
92351
+ candidates.push(join15(process.cwd(), "dashboard", "dist"));
92250
92352
  for (const candidate of candidates) {
92251
- if (existsSync15(candidate))
92353
+ if (existsSync16(candidate))
92252
92354
  return candidate;
92253
92355
  }
92254
- return join14(process.cwd(), "dashboard", "dist");
92356
+ return join15(process.cwd(), "dashboard", "dist");
92255
92357
  }
92256
92358
  function getProvidedApiKey(req) {
92257
92359
  const headerKey = req.headers.get("x-api-key");
@@ -92323,7 +92425,7 @@ function json(data, status = 200, headers) {
92323
92425
  });
92324
92426
  }
92325
92427
  function serveStaticFile(filePath) {
92326
- if (!existsSync15(filePath))
92428
+ if (!existsSync16(filePath))
92327
92429
  return null;
92328
92430
  const ext = extname(filePath);
92329
92431
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
@@ -92424,7 +92526,7 @@ data: ${data}
92424
92526
  filteredSseClients.delete(client);
92425
92527
  }
92426
92528
  const dashboardDir = resolveDashboardDir();
92427
- const dashboardExists = existsSync15(dashboardDir);
92529
+ const dashboardExists = existsSync16(dashboardDir);
92428
92530
  if (!dashboardExists) {
92429
92531
  console.error(`
92430
92532
  Dashboard not found at: ${dashboardDir}`);