@hasna/todos 0.13.4 → 0.13.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mcp/index.js CHANGED
@@ -2497,6 +2497,7 @@ function ensureSchema(db) {
2497
2497
  ensureColumn("tasks", "reason", "TEXT");
2498
2498
  ensureColumn("tasks", "spawned_from_session", "TEXT");
2499
2499
  ensureColumn("tasks", "assigned_by", "TEXT");
2500
+ ensureColumn("tasks", "created_by", "TEXT");
2500
2501
  ensureColumn("tasks", "assigned_from_project", "TEXT");
2501
2502
  ensureColumn("tasks", "started_at", "TEXT");
2502
2503
  ensureColumn("tasks", "task_type", "TEXT");
@@ -2659,6 +2660,8 @@ function ensureSchema(db) {
2659
2660
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_sources_project ON project_sources(project_id)");
2660
2661
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_sources_type ON project_sources(type)");
2661
2662
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_assigned_by ON tasks(assigned_by)");
2663
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_created_by ON tasks(created_by)");
2664
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_assigned_created ON tasks(assigned_to, created_by)");
2662
2665
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_rel_source ON task_relationships(source_task_id)");
2663
2666
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_rel_target ON task_relationships(target_task_id)");
2664
2667
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_rel_type ON task_relationships(relationship_type)");
@@ -13413,13 +13416,14 @@ function createTask(input, db) {
13413
13416
  const timestamp2 = now();
13414
13417
  const tags = input.tags || [];
13415
13418
  const machineId = currentStorageMachineId(d);
13419
+ const createdBy = input.created_by || input.agent_id || null;
13416
13420
  const assignedBy = input.assigned_by || input.agent_id;
13417
13421
  const assignedFromProject = input.assigned_from_project || null;
13418
13422
  let id = uuid();
13419
13423
  for (let attempt = 0;attempt < 3; attempt++) {
13420
13424
  try {
13421
- 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)
13422
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
13425
+ 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)
13426
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
13423
13427
  id,
13424
13428
  null,
13425
13429
  input.project_id || null,
@@ -13455,6 +13459,7 @@ function createTask(input, db) {
13455
13459
  input.reason || null,
13456
13460
  input.spawned_from_session || null,
13457
13461
  assignedBy || null,
13462
+ createdBy,
13458
13463
  assignedFromProject || null,
13459
13464
  input.task_type || null,
13460
13465
  machineId
@@ -13564,6 +13569,14 @@ function listTasks(filter = {}, db) {
13564
13569
  conditions.push("agent_id = ?");
13565
13570
  params.push(filter.agent_id);
13566
13571
  }
13572
+ if (filter.created_by) {
13573
+ conditions.push("LOWER(created_by) = LOWER(?)");
13574
+ params.push(filter.created_by);
13575
+ }
13576
+ if (filter.not_created_by) {
13577
+ conditions.push("(created_by IS NULL OR LOWER(created_by) != LOWER(?))");
13578
+ params.push(filter.not_created_by);
13579
+ }
13567
13580
  if (filter.session_id) {
13568
13581
  conditions.push("session_id = ?");
13569
13582
  params.push(filter.session_id);
@@ -13717,6 +13730,14 @@ function countTasks(filter = {}, db) {
13717
13730
  conditions.push("agent_id = ?");
13718
13731
  params.push(filter.agent_id);
13719
13732
  }
13733
+ if (filter.created_by) {
13734
+ conditions.push("LOWER(created_by) = LOWER(?)");
13735
+ params.push(filter.created_by);
13736
+ }
13737
+ if (filter.not_created_by) {
13738
+ conditions.push("(created_by IS NULL OR LOWER(created_by) != LOWER(?))");
13739
+ params.push(filter.not_created_by);
13740
+ }
13720
13741
  if (filter.session_id) {
13721
13742
  conditions.push("session_id = ?");
13722
13743
  params.push(filter.session_id);
@@ -17844,6 +17865,41 @@ var init_token_utils = __esm(() => {
17844
17865
  };
17845
17866
  });
17846
17867
 
17868
+ // src/lib/creator-identity.ts
17869
+ import { existsSync as existsSync7, rmSync as rmSync2 } from "fs";
17870
+ import { join as join6 } from "path";
17871
+ function identityFilePath() {
17872
+ return join6(getTodosGlobalDir(), "identity.json");
17873
+ }
17874
+ function readPersistedIdentity() {
17875
+ const path = identityFilePath();
17876
+ if (!existsSync7(path))
17877
+ return null;
17878
+ const parsed = readJsonFile(path);
17879
+ if (!parsed || typeof parsed.agent_id !== "string" || !parsed.agent_id.trim())
17880
+ return null;
17881
+ return parsed;
17882
+ }
17883
+ function canonicalAgentRef(value) {
17884
+ return value.trim().toLowerCase();
17885
+ }
17886
+ function resolveCreatorIdentity(explicit) {
17887
+ const fromExplicit = explicit?.trim();
17888
+ if (fromExplicit)
17889
+ return { agent_id: canonicalAgentRef(fromExplicit), source: "explicit" };
17890
+ const fromEnv = (process.env["TODOS_AGENT_ID"] || process.env["HASNA_TODOS_AGENT_ID"] || "").trim();
17891
+ if (fromEnv)
17892
+ return { agent_id: canonicalAgentRef(fromEnv), source: "env" };
17893
+ const persisted = readPersistedIdentity();
17894
+ if (persisted) {
17895
+ return { agent_id: canonicalAgentRef(persisted.agent_name || persisted.agent_id), source: "persisted" };
17896
+ }
17897
+ return { agent_id: null, source: "none" };
17898
+ }
17899
+ var init_creator_identity = __esm(() => {
17900
+ init_sync_utils();
17901
+ });
17902
+
17847
17903
  // src/pr-groups/types.ts
17848
17904
  var PR_GROUP_LEDGER_SCHEMA_VERSION = 1, PR_GROUP_REPAIR_CYCLE_LIMIT = 2, PrGroupLedgerError;
17849
17905
  var init_types3 = __esm(() => {
@@ -20594,6 +20650,8 @@ function registerTaskCrudTools(server, ctx) {
20594
20650
  project_id: exports_external.string().optional().describe("Project ID"),
20595
20651
  task_list_id: exports_external.string().optional().describe("Task list ID"),
20596
20652
  assigned_to: exports_external.string().optional().describe("Agent ID or name to assign to"),
20653
+ created_by: exports_external.string().optional().describe("Agent who FILED this task. Defaults to the ambient agent identity (todos init / TODOS_AGENT_ID)."),
20654
+ unassigned: exports_external.boolean().optional().describe("Deliberately file with no assignee. Without it, the task defaults to the filer."),
20597
20655
  depends_on: exports_external.array(exports_external.string()).optional().describe("Array of task IDs this task depends on"),
20598
20656
  short_id: exports_external.string().nullable().optional().describe("Short ID (auto-generated if not provided, disabled if null)"),
20599
20657
  tags: exports_external.array(exports_external.string()).optional().describe("Tags for the task"),
@@ -20604,12 +20662,18 @@ function registerTaskCrudTools(server, ctx) {
20604
20662
  retry_count: exports_external.number().optional().describe("Max retry count for agent failures")
20605
20663
  }, async (params) => {
20606
20664
  try {
20607
- const { depends_on, assigned_to, project_id, task_list_id, tags, estimate, confidence, retry_count, deadline, ...rest } = params;
20665
+ const { depends_on, assigned_to, project_id, task_list_id, tags, estimate, confidence, retry_count, deadline, created_by, unassigned, ...rest } = params;
20666
+ const creator = resolveCreatorIdentity(created_by);
20667
+ const assignee = assigned_to || (unassigned ? undefined : creator.agent_id || undefined);
20608
20668
  const cloud = getTodosCloudClient();
20609
20669
  if (cloud) {
20610
20670
  const payload = { ...rest };
20611
- if (assigned_to)
20612
- payload.assigned_to = assigned_to;
20671
+ if (creator.agent_id) {
20672
+ payload.created_by = creator.agent_id;
20673
+ payload.agent_id = payload.agent_id ?? creator.agent_id;
20674
+ }
20675
+ if (assignee)
20676
+ payload.assigned_to = assignee;
20613
20677
  if (project_id)
20614
20678
  payload.project_id = project_id;
20615
20679
  if (task_list_id)
@@ -20630,8 +20694,12 @@ function registerTaskCrudTools(server, ctx) {
20630
20694
  return { content: [{ type: "text", text: mutationTaskResponse(created) }] };
20631
20695
  }
20632
20696
  const resolved = { ...rest };
20633
- if (assigned_to)
20634
- resolved.assigned_to = resolveAssignee(assigned_to);
20697
+ if (creator.agent_id) {
20698
+ resolved.created_by = creator.agent_id;
20699
+ resolved.agent_id = resolved.agent_id ?? creator.agent_id;
20700
+ }
20701
+ if (assignee)
20702
+ resolved.assigned_to = resolveAssignee(assignee);
20635
20703
  if (project_id)
20636
20704
  resolved.project_id = resolveId(project_id, "projects");
20637
20705
  if (task_list_id)
@@ -20717,6 +20785,8 @@ function registerTaskCrudTools(server, ctx) {
20717
20785
  project_id: exports_external.string().optional().describe("Filter by project"),
20718
20786
  task_list_id: exports_external.string().optional().describe("Filter by task list"),
20719
20787
  assigned_to: exports_external.string().optional().describe("Filter by assignee (agent ID or name, empty string = unassigned)"),
20788
+ created_by: exports_external.string().optional().describe("Filter by the agent who FILED the task"),
20789
+ 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.'),
20720
20790
  tags: exports_external.array(exports_external.string()).optional().describe("Filter by tags (AND logic)"),
20721
20791
  created_after: exports_external.string().optional().describe("ISO date \u2014 tasks created after this date"),
20722
20792
  created_before: exports_external.string().optional().describe("ISO date \u2014 tasks created before this date"),
@@ -20909,11 +20979,12 @@ var init_task_crud2 = __esm(() => {
20909
20979
  init_tasks();
20910
20980
  init_types();
20911
20981
  init_token_utils();
20982
+ init_creator_identity();
20912
20983
  init_cloud_router();
20913
20984
  });
20914
20985
 
20915
20986
  // src/lib/project-bootstrap.ts
20916
- import { existsSync as existsSync7, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
20987
+ import { existsSync as existsSync8, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
20917
20988
  import { basename as basename2, dirname as dirname5, resolve as resolve8 } from "path";
20918
20989
  function safeStat(path) {
20919
20990
  try {
@@ -20932,7 +21003,7 @@ function canonicalPath(input) {
20932
21003
  function findUp(start, marker) {
20933
21004
  let current = canonicalPath(start);
20934
21005
  while (true) {
20935
- if (existsSync7(resolve8(current, marker)))
21006
+ if (existsSync8(resolve8(current, marker)))
20936
21007
  return current;
20937
21008
  const parent = dirname5(current);
20938
21009
  if (parent === current)
@@ -20944,7 +21015,7 @@ function readPackageJson(path) {
20944
21015
  if (!path)
20945
21016
  return null;
20946
21017
  const file = resolve8(path, "package.json");
20947
- if (!existsSync7(file))
21018
+ if (!existsSync8(file))
20948
21019
  return null;
20949
21020
  try {
20950
21021
  const parsed = JSON.parse(readFileSync3(file, "utf-8"));
@@ -20966,7 +21037,7 @@ function workspaceMarker(root, rootPackage) {
20966
21037
  if (rootPackage?.workspaces)
20967
21038
  markers.push("package.json#workspaces");
20968
21039
  for (const marker of ["pnpm-workspace.yaml", "turbo.json", "nx.json", "lerna.json", "rush.json", "bun.lock", "bun.lockb"]) {
20969
- if (existsSync7(resolve8(root, marker)))
21040
+ if (existsSync8(resolve8(root, marker)))
20970
21041
  markers.push(marker);
20971
21042
  }
20972
21043
  const kind = markers.find((marker) => marker !== "bun.lock" && marker !== "bun.lockb") ?? null;
@@ -21279,7 +21350,7 @@ var init_tags = __esm(() => {
21279
21350
  });
21280
21351
 
21281
21352
  // src/lib/retention-cleanup.ts
21282
- import { existsSync as existsSync8, unlinkSync } from "fs";
21353
+ import { existsSync as existsSync9, unlinkSync } from "fs";
21283
21354
  function normalizeScopes(scopes) {
21284
21355
  if (!scopes || scopes.length === 0)
21285
21356
  return [...ALL_SCOPES];
@@ -21482,7 +21553,7 @@ function applyRetentionCleanup(input, db) {
21482
21553
  for (const artifact of report.candidates.artifact_files) {
21483
21554
  try {
21484
21555
  const path = artifactStorePath(artifact.relative_path);
21485
- if (!existsSync8(path)) {
21556
+ if (!existsSync9(path)) {
21486
21557
  report.warnings.push(`stored artifact already missing: ${artifact.relative_path}`);
21487
21558
  continue;
21488
21559
  }
@@ -21509,8 +21580,8 @@ var init_retention_cleanup = __esm(() => {
21509
21580
  });
21510
21581
 
21511
21582
  // src/lib/mention-resolver.ts
21512
- import { existsSync as existsSync9, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync4 } from "fs";
21513
- import { basename as basename3, isAbsolute, join as join6, relative as relative3, resolve as resolve9, sep as sep2 } from "path";
21583
+ import { existsSync as existsSync10, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync4 } from "fs";
21584
+ import { basename as basename3, isAbsolute, join as join7, relative as relative3, resolve as resolve9, sep as sep2 } from "path";
21514
21585
  function blankResolution(parsed) {
21515
21586
  return {
21516
21587
  input: parsed.input,
@@ -21608,7 +21679,7 @@ function resolveFile(parsed, workspace) {
21608
21679
  return resolution;
21609
21680
  }
21610
21681
  resolution.path = relPath;
21611
- if (!existsSync9(absolutePath)) {
21682
+ if (!existsSync10(absolutePath)) {
21612
21683
  resolution.warnings.push("file does not exist in the local workspace");
21613
21684
  return resolution;
21614
21685
  }
@@ -21641,7 +21712,7 @@ function walkSourceFiles(root, current = root, files = []) {
21641
21712
  if (SKIP_DIRS.has(entry.name))
21642
21713
  continue;
21643
21714
  }
21644
- const absolutePath = join6(current, entry.name);
21715
+ const absolutePath = join7(current, entry.name);
21645
21716
  if (entry.isDirectory()) {
21646
21717
  if (!SKIP_DIRS.has(entry.name))
21647
21718
  walkSourceFiles(root, absolutePath, files);
@@ -25333,7 +25404,7 @@ var init_audit_ledger = __esm(() => {
25333
25404
 
25334
25405
  // src/lib/release-compatibility.ts
25335
25406
  import { readFileSync as readFileSync5 } from "fs";
25336
- import { join as join7, resolve as resolve11 } from "path";
25407
+ import { join as join8, resolve as resolve11 } from "path";
25337
25408
  import { Database as Database2 } from "bun:sqlite";
25338
25409
  function pass(id, message, details) {
25339
25410
  return { id, status: "passed", message, details };
@@ -25345,7 +25416,7 @@ function warn(id, message, details) {
25345
25416
  return { id, status: "warning", message, details };
25346
25417
  }
25347
25418
  function readPackageJson2(root) {
25348
- return JSON.parse(readFileSync5(join7(root, "package.json"), "utf8"));
25419
+ return JSON.parse(readFileSync5(join8(root, "package.json"), "utf8"));
25349
25420
  }
25350
25421
  function sortedKeys(value) {
25351
25422
  return Object.keys(value ?? {}).sort((left, right) => left.localeCompare(right));
@@ -29358,8 +29429,8 @@ var exports_doctor = {};
29358
29429
  __export(exports_doctor, {
29359
29430
  runTodosDoctor: () => runTodosDoctor
29360
29431
  });
29361
- import { chmodSync, copyFileSync, existsSync as existsSync10, mkdirSync as mkdirSync5, statSync as statSync5 } from "fs";
29362
- import { basename as basename4, dirname as dirname6, join as join8 } from "path";
29432
+ import { chmodSync, copyFileSync, existsSync as existsSync11, mkdirSync as mkdirSync5, statSync as statSync5 } from "fs";
29433
+ import { basename as basename4, dirname as dirname6, join as join9 } from "path";
29363
29434
  function tableExists2(db, table) {
29364
29435
  return Boolean(db.query("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(table));
29365
29436
  }
@@ -29453,7 +29524,7 @@ function findMissingProjectRoots(db) {
29453
29524
  continue;
29454
29525
  if (!row.path.startsWith("/"))
29455
29526
  continue;
29456
- if (!existsSync10(row.path))
29527
+ if (!existsSync11(row.path))
29457
29528
  missing++;
29458
29529
  }
29459
29530
  return missing;
@@ -29513,16 +29584,16 @@ function databasePermissionsAreUnsafe(dbPath) {
29513
29584
  function createBackup(dbPath) {
29514
29585
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
29515
29586
  return;
29516
- if (!existsSync10(dbPath))
29587
+ if (!existsSync11(dbPath))
29517
29588
  return;
29518
29589
  const stamp = now().replace(/[:.]/g, "-");
29519
- const backupDir = join8(dirname6(dbPath), `${basename4(dbPath)}.backup-${stamp}`);
29590
+ const backupDir = join9(dirname6(dbPath), `${basename4(dbPath)}.backup-${stamp}`);
29520
29591
  const files = [];
29521
29592
  mkdirSync5(backupDir, { recursive: true });
29522
29593
  for (const source of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
29523
- if (!existsSync10(source))
29594
+ if (!existsSync11(source))
29524
29595
  continue;
29525
- const target = join8(backupDir, basename4(source));
29596
+ const target = join9(backupDir, basename4(source));
29526
29597
  copyFileSync(source, target);
29527
29598
  files.push(target);
29528
29599
  }
@@ -32258,7 +32329,7 @@ var init_agent_run_dispatcher = __esm(() => {
32258
32329
  });
32259
32330
 
32260
32331
  // src/lib/verification-providers.ts
32261
- import { existsSync as existsSync11, readFileSync as readFileSync6 } from "fs";
32332
+ import { existsSync as existsSync12, readFileSync as readFileSync6 } from "fs";
32262
32333
  function normalizeName5(name) {
32263
32334
  const normalized = name.trim().toLowerCase();
32264
32335
  if (!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(normalized)) {
@@ -32410,7 +32481,7 @@ Timed out after ${provider.timeout_ms}ms`);
32410
32481
  };
32411
32482
  }
32412
32483
  function runCiLogProvider(input) {
32413
- const text = input.log_text ?? (input.log_path && existsSync11(input.log_path) ? readFileSync6(input.log_path, "utf-8") : "");
32484
+ const text = input.log_text ?? (input.log_path && existsSync12(input.log_path) ? readFileSync6(input.log_path, "utf-8") : "");
32414
32485
  return {
32415
32486
  status: classifyLog(text),
32416
32487
  attempts: 1,
@@ -32422,7 +32493,7 @@ function runBrowserProvider(input) {
32422
32493
  if (!input.artifact_path) {
32423
32494
  return { status: "unknown", attempts: 1, exit_code: null, output_summary: "browser provider needs a screenshot or artifact path" };
32424
32495
  }
32425
- if (!existsSync11(input.artifact_path)) {
32496
+ if (!existsSync12(input.artifact_path)) {
32426
32497
  return { status: "failed", attempts: 1, exit_code: null, output_summary: `artifact not found: ${input.artifact_path}` };
32427
32498
  }
32428
32499
  return {
@@ -34571,7 +34642,7 @@ var package_default;
34571
34642
  var init_package = __esm(() => {
34572
34643
  package_default = {
34573
34644
  name: "@hasna/todos",
34574
- version: "0.13.4",
34645
+ version: "0.13.6",
34575
34646
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
34576
34647
  type: "module",
34577
34648
  main: "dist/index.js",
@@ -35641,6 +35712,7 @@ function task(input) {
35641
35712
  reason: "Bundled deterministic onboarding fixture",
35642
35713
  spawned_from_session: null,
35643
35714
  assigned_by: null,
35715
+ created_by: null,
35644
35716
  assigned_from_project: null,
35645
35717
  task_type: "onboarding",
35646
35718
  cost_tokens: 0,
@@ -36838,8 +36910,8 @@ __export(exports_local_extensions, {
36838
36910
  discoverLocalExtensions: () => discoverLocalExtensions
36839
36911
  });
36840
36912
  import { createHash as createHash9, createVerify } from "crypto";
36841
- import { existsSync as existsSync12, readdirSync as readdirSync3, readFileSync as readFileSync8, statSync as statSync6 } from "fs";
36842
- import { basename as basename5, join as join9, resolve as resolve13 } from "path";
36913
+ import { existsSync as existsSync13, readdirSync as readdirSync3, readFileSync as readFileSync8, statSync as statSync6 } from "fs";
36914
+ import { basename as basename5, join as join10, resolve as resolve13 } from "path";
36843
36915
  function isObject2(value) {
36844
36916
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
36845
36917
  }
@@ -37098,10 +37170,10 @@ function verifyExtensionSignature(input) {
37098
37170
  }
37099
37171
  function inspectExtensionSource(source3) {
37100
37172
  const resolved = resolve13(source3);
37101
- if (!existsSync12(resolved))
37173
+ if (!existsSync13(resolved))
37102
37174
  throw new Error(`extension source not found: ${source3}`);
37103
37175
  const stat = statSync6(resolved);
37104
- const manifestPath = stat.isDirectory() ? [join9(resolved, "todos.extension.json"), join9(resolved, "extension.json")].find(existsSync12) : resolved;
37176
+ const manifestPath = stat.isDirectory() ? [join10(resolved, "todos.extension.json"), join10(resolved, "extension.json")].find(existsSync13) : resolved;
37105
37177
  if (!manifestPath)
37106
37178
  throw new Error(`extension directory ${source3} is missing todos.extension.json`);
37107
37179
  const raw = readFileSync8(manifestPath);
@@ -37197,20 +37269,20 @@ function projectExtensionSources(projectPath) {
37197
37269
  return [];
37198
37270
  const root = resolve13(projectPath);
37199
37271
  const candidates = [
37200
- join9(root, "todos.extension.json"),
37201
- join9(root, ".todos", "todos.extension.json")
37272
+ join10(root, "todos.extension.json"),
37273
+ join10(root, ".todos", "todos.extension.json")
37202
37274
  ];
37203
- const extensionDir = join9(root, ".todos", "extensions");
37204
- if (existsSync12(extensionDir)) {
37275
+ const extensionDir = join10(root, ".todos", "extensions");
37276
+ if (existsSync13(extensionDir)) {
37205
37277
  for (const entry of readdirSync3(extensionDir)) {
37206
37278
  if (entry.startsWith("."))
37207
37279
  continue;
37208
- const full = join9(extensionDir, entry);
37280
+ const full = join10(extensionDir, entry);
37209
37281
  if (statSync6(full).isDirectory() || entry.endsWith(".json"))
37210
37282
  candidates.push(full);
37211
37283
  }
37212
37284
  }
37213
- return candidates.filter(existsSync12);
37285
+ return candidates.filter(existsSync13);
37214
37286
  }
37215
37287
  function discoverLocalExtensions(options = {}) {
37216
37288
  const config = loadConfig();
@@ -41617,9 +41689,9 @@ __export(exports_extract, {
41617
41689
  buildCodebaseIndex: () => buildCodebaseIndex,
41618
41690
  EXTRACT_TAGS: () => EXTRACT_TAGS
41619
41691
  });
41620
- import { existsSync as existsSync13, readFileSync as readFileSync9, statSync as statSync7 } from "fs";
41692
+ import { existsSync as existsSync14, readFileSync as readFileSync9, statSync as statSync7 } from "fs";
41621
41693
  import { createHash as createHash11 } from "crypto";
41622
- import { relative as relative5, resolve as resolve14, join as join10 } from "path";
41694
+ import { relative as relative5, resolve as resolve14, join as join11 } from "path";
41623
41695
  function stableHash(value) {
41624
41696
  return createHash11("sha256").update(value).digest("hex");
41625
41697
  }
@@ -41628,8 +41700,8 @@ function normalizePathForMatch(value) {
41628
41700
  }
41629
41701
  function readGitignorePatterns(basePath) {
41630
41702
  const root = statSync7(basePath).isFile() ? resolve14(basePath, "..") : basePath;
41631
- const gitignorePath = join10(root, ".gitignore");
41632
- if (!existsSync13(gitignorePath))
41703
+ const gitignorePath = join11(root, ".gitignore");
41704
+ if (!existsSync14(gitignorePath))
41633
41705
  return [];
41634
41706
  try {
41635
41707
  return readFileSync9(gitignorePath, "utf-8").split(`
@@ -41771,7 +41843,7 @@ function buildCodebaseIndex(options) {
41771
41843
  const files = collectFiles(basePath, extensions, excludes, respectGitignore);
41772
41844
  const indexed = [];
41773
41845
  for (const file of files) {
41774
- const fullPath = statSync7(basePath).isFile() ? basePath : join10(basePath, file);
41846
+ const fullPath = statSync7(basePath).isFile() ? basePath : join11(basePath, file);
41775
41847
  try {
41776
41848
  const source3 = readFileSync9(fullPath, "utf-8");
41777
41849
  const relPath = statSync7(basePath).isFile() ? relative5(resolve14(basePath, ".."), fullPath) : file;
@@ -41802,7 +41874,7 @@ function extractTodos(options, db) {
41802
41874
  const files = collectFiles(basePath, extensions, excludes, respectGitignore);
41803
41875
  const allComments = [];
41804
41876
  for (const file of files) {
41805
- const fullPath = statSync7(basePath).isFile() ? basePath : join10(basePath, file);
41877
+ const fullPath = statSync7(basePath).isFile() ? basePath : join11(basePath, file);
41806
41878
  try {
41807
41879
  const source3 = readFileSync9(fullPath, "utf-8");
41808
41880
  const relPath = statSync7(basePath).isFile() ? relative5(resolve14(basePath, ".."), fullPath) : file;
@@ -42753,7 +42825,7 @@ __export(exports_builtin_templates, {
42753
42825
  BUILTIN_TEMPLATES: () => BUILTIN_TEMPLATES
42754
42826
  });
42755
42827
  import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync4 } from "fs";
42756
- import { join as join11 } from "path";
42828
+ import { join as join12 } from "path";
42757
42829
  function templateMetadata(template) {
42758
42830
  return {
42759
42831
  source: BUILTIN_TEMPLATE_LIBRARY_SOURCE,
@@ -42812,7 +42884,7 @@ function writeBuiltinTemplateFiles(directory) {
42812
42884
  mkdirSync7(directory, { recursive: true });
42813
42885
  const files = [];
42814
42886
  for (const entry of exportBuiltinTemplateFiles()) {
42815
- const path = join11(directory, entry.filename);
42887
+ const path = join12(directory, entry.filename);
42816
42888
  writeFileSync4(path, `${JSON.stringify(entry.template, null, 2)}
42817
42889
  `, "utf-8");
42818
42890
  files.push(path);
@@ -43338,16 +43410,16 @@ __export(exports_environment_snapshots, {
43338
43410
  captureEnvironmentSnapshot: () => captureEnvironmentSnapshot
43339
43411
  });
43340
43412
  import { createHash as createHash12 } from "crypto";
43341
- import { existsSync as existsSync14, readFileSync as readFileSync10, statSync as statSync8 } from "fs";
43413
+ import { existsSync as existsSync15, readFileSync as readFileSync10, statSync as statSync8 } from "fs";
43342
43414
  import { hostname as hostname2, platform, arch } from "os";
43343
- import { dirname as dirname8, join as join12, resolve as resolve15 } from "path";
43415
+ import { dirname as dirname8, join as join13, resolve as resolve15 } from "path";
43344
43416
  import { tmpdir as tmpdir3 } from "os";
43345
43417
  function sha2567(value) {
43346
43418
  return createHash12("sha256").update(value).digest("hex");
43347
43419
  }
43348
43420
  function fileRecord(root, relativePath) {
43349
- const path = join12(root, relativePath);
43350
- if (!existsSync14(path))
43421
+ const path = join13(root, relativePath);
43422
+ if (!existsSync15(path))
43351
43423
  return null;
43352
43424
  const stat = statSync8(path);
43353
43425
  if (!stat.isFile())
@@ -43359,7 +43431,7 @@ function manifestRecord(root, relativePath) {
43359
43431
  const base = fileRecord(root, relativePath);
43360
43432
  if (!base)
43361
43433
  return null;
43362
- const parsed = readJsonFile(join12(root, relativePath));
43434
+ const parsed = readJsonFile(join13(root, relativePath));
43363
43435
  if (!parsed)
43364
43436
  return { ...base, redacted: {} };
43365
43437
  const redacted = redactValue({
@@ -43454,8 +43526,8 @@ function commandEnv(env, includeValues) {
43454
43526
  function defaultSnapshotDir() {
43455
43527
  const dbPath = getDatabasePath();
43456
43528
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
43457
- return join12(tmpdir3(), "hasna-todos", "environment-snapshots");
43458
- return join12(dirname8(resolve15(dbPath)), "environment-snapshots");
43529
+ return join13(tmpdir3(), "hasna-todos", "environment-snapshots");
43530
+ return join13(dirname8(resolve15(dbPath)), "environment-snapshots");
43459
43531
  }
43460
43532
  function snapshotWithId(snapshot) {
43461
43533
  const digest = sha2567(JSON.stringify(snapshot)).slice(0, 24);
@@ -43502,7 +43574,7 @@ function captureEnvironmentSnapshot(input = {}) {
43502
43574
  });
43503
43575
  }
43504
43576
  function writeEnvironmentSnapshot(snapshot, outputPath) {
43505
- const path = outputPath ? resolve15(outputPath) : join12(defaultSnapshotDir(), `${snapshot.id}.json`);
43577
+ const path = outputPath ? resolve15(outputPath) : join13(defaultSnapshotDir(), `${snapshot.id}.json`);
43506
43578
  ensureDir2(dirname8(path));
43507
43579
  writeJsonFile(path, snapshot);
43508
43580
  return path;
@@ -44401,7 +44473,11 @@ function createLocalSqliteTodosStorageAdapter(options = {}) {
44401
44473
  sync: true
44402
44474
  },
44403
44475
  tasks: {
44404
- create: (input) => createTask(input, database()),
44476
+ create: (input, context) => createTask({
44477
+ ...input,
44478
+ agent_id: input.agent_id ?? context?.agentId,
44479
+ created_by: input.created_by ?? input.agent_id ?? context?.agentId
44480
+ }, database()),
44405
44481
  get: (id) => getTask(id, database()),
44406
44482
  resolveRef: (ref) => resolveTaskRefLocal(database(), ref),
44407
44483
  list: (filter = {}) => listTasksMaybeSearch(filter, database()),
@@ -45540,6 +45616,10 @@ class PostgresJsonRecordStore {
45540
45616
  conds.push(`payload->>'assigned_to' = ${p(filter.assigned_to)}`);
45541
45617
  if (filter.agent_id !== undefined)
45542
45618
  conds.push(`payload->>'agent_id' = ${p(filter.agent_id)}`);
45619
+ if (filter.created_by !== undefined)
45620
+ conds.push(`LOWER(payload->>'created_by') = LOWER(${p(filter.created_by)})`);
45621
+ if (filter.not_created_by !== undefined)
45622
+ conds.push(`(payload->>'created_by' IS NULL OR LOWER(payload->>'created_by') <> LOWER(${p(filter.not_created_by)}))`);
45543
45623
  if (filter.session_id !== undefined)
45544
45624
  conds.push(`payload->>'session_id' = ${p(filter.session_id)}`);
45545
45625
  if (filter.tags?.length) {
@@ -46053,7 +46133,7 @@ async function createTask3(input, store, context) {
46053
46133
  description: input.description ?? null,
46054
46134
  status: input.status ?? "pending",
46055
46135
  priority: input.priority ?? "medium",
46056
- agent_id: input.agent_id ?? null,
46136
+ agent_id: input.agent_id ?? context?.agentId ?? null,
46057
46137
  assigned_to: input.assigned_to ?? null,
46058
46138
  session_id: input.session_id ?? context?.sessionId ?? null,
46059
46139
  working_dir: input.working_dir ?? null,
@@ -46078,7 +46158,8 @@ async function createTask3(input, store, context) {
46078
46158
  confidence: input.confidence ?? null,
46079
46159
  reason: input.reason ?? null,
46080
46160
  spawned_from_session: input.spawned_from_session ?? null,
46081
- assigned_by: input.assigned_by ?? null,
46161
+ assigned_by: input.assigned_by ?? input.agent_id ?? context?.agentId ?? null,
46162
+ created_by: input.created_by ?? input.agent_id ?? context?.agentId ?? null,
46082
46163
  assigned_from_project: input.assigned_from_project ?? null,
46083
46164
  task_type: input.task_type ?? null,
46084
46165
  cost_tokens: 0,
@@ -46115,7 +46196,8 @@ async function updateTask2(id, input, store) {
46115
46196
  tags: input.tags ?? existing.tags,
46116
46197
  metadata: input.metadata ?? existing.metadata,
46117
46198
  requires_approval: input.requires_approval ?? existing.requires_approval,
46118
- task_list_id: input.task_list_id !== undefined ? input.task_list_id : existing.task_list_id
46199
+ task_list_id: input.task_list_id !== undefined ? input.task_list_id : existing.task_list_id,
46200
+ created_by: existing.created_by
46119
46201
  };
46120
46202
  await store.upsert("tasks", task2);
46121
46203
  return task2;
@@ -47798,7 +47880,7 @@ var init_headless_boundaries = __esm(() => {
47798
47880
  });
47799
47881
 
47800
47882
  // src/server/routes.ts
47801
- import { join as join13, resolve as resolve16, sep as sep3 } from "path";
47883
+ import { join as join14, resolve as resolve16, sep as sep3 } from "path";
47802
47884
  function parseFieldsParam(url) {
47803
47885
  const fieldsParam = url.searchParams.get("fields");
47804
47886
  return fieldsParam ? fieldsParam.split(",").map((f) => f.trim()).filter(Boolean) : undefined;
@@ -47979,11 +48061,15 @@ async function handleCreateTask(req, ctx, json2, taskToSummary2) {
47979
48061
  const body = await req.json();
47980
48062
  if (!body.title)
47981
48063
  return json2({ error: "Missing 'title'" }, 400);
48064
+ const createdBy = body.created_by ?? body.agent_id ?? "dashboard";
47982
48065
  const task2 = createTask({
47983
48066
  title: body.title,
47984
48067
  description: body.description,
47985
48068
  priority: body.priority,
47986
- project_id: body.project_id
48069
+ project_id: body.project_id,
48070
+ agent_id: body.agent_id ?? createdBy,
48071
+ created_by: createdBy,
48072
+ ...body.assigned_to ? { assigned_to: body.assigned_to } : {}
47987
48073
  });
47988
48074
  ctx.broadcastEvent({ type: "task", task_id: task2.id, action: "created", agent_id: task2.agent_id, project_id: task2.project_id });
47989
48075
  return json2(taskToSummary2(task2), 201);
@@ -48589,7 +48675,7 @@ function handleStaticFiles(path, method, ctx, json2, serveStaticFile2) {
48589
48675
  if (!ctx.dashboardExists || method !== "GET" && method !== "HEAD")
48590
48676
  return null;
48591
48677
  if (path !== "/") {
48592
- const filePath = join13(ctx.dashboardDir, path);
48678
+ const filePath = join14(ctx.dashboardDir, path);
48593
48679
  const resolvedFile = resolve16(filePath);
48594
48680
  const resolvedBase = resolve16(ctx.dashboardDir);
48595
48681
  if (!resolvedFile.startsWith(resolvedBase + sep3) && resolvedFile !== resolvedBase) {
@@ -48599,7 +48685,7 @@ function handleStaticFiles(path, method, ctx, json2, serveStaticFile2) {
48599
48685
  if (res2)
48600
48686
  return res2;
48601
48687
  }
48602
- const indexPath = join13(ctx.dashboardDir, "index.html");
48688
+ const indexPath = join14(ctx.dashboardDir, "index.html");
48603
48689
  const res = serveStaticFile2(indexPath);
48604
48690
  if (res)
48605
48691
  return res;
@@ -50776,6 +50862,8 @@ async function handleV1Request(req, url, dependencies = {}) {
50776
50862
  ...url.searchParams.get("task_list_id") ? { task_list_id: url.searchParams.get("task_list_id") } : {},
50777
50863
  ...url.searchParams.get("assigned_to") ? { assigned_to: url.searchParams.get("assigned_to") } : {},
50778
50864
  ...url.searchParams.get("agent_id") ? { agent_id: url.searchParams.get("agent_id") } : {},
50865
+ ...url.searchParams.get("created_by") ? { created_by: url.searchParams.get("created_by") } : {},
50866
+ ...url.searchParams.get("not_created_by") ? { not_created_by: url.searchParams.get("not_created_by") } : {},
50779
50867
  ...url.searchParams.get("tags") ? {
50780
50868
  tags: url.searchParams.get("tags").split(",").map((tag) => tag.trim()).filter(Boolean)
50781
50869
  } : {},
@@ -51734,27 +51822,27 @@ __export(exports_serve, {
51734
51822
  SECURITY_HEADERS: () => SECURITY_HEADERS,
51735
51823
  MIME_TYPES: () => MIME_TYPES
51736
51824
  });
51737
- import { existsSync as existsSync15 } from "fs";
51738
- import { join as join14, dirname as dirname9, extname } from "path";
51825
+ import { existsSync as existsSync16 } from "fs";
51826
+ import { join as join15, dirname as dirname9, extname } from "path";
51739
51827
  import { fileURLToPath } from "url";
51740
51828
  function resolveDashboardDir() {
51741
51829
  const candidates = [];
51742
51830
  try {
51743
51831
  const scriptDir = dirname9(fileURLToPath(import.meta.url));
51744
- candidates.push(join14(scriptDir, "..", "dashboard", "dist"));
51745
- candidates.push(join14(scriptDir, "..", "..", "dashboard", "dist"));
51832
+ candidates.push(join15(scriptDir, "..", "dashboard", "dist"));
51833
+ candidates.push(join15(scriptDir, "..", "..", "dashboard", "dist"));
51746
51834
  } catch {}
51747
51835
  if (process.argv[1]) {
51748
51836
  const mainDir = dirname9(process.argv[1]);
51749
- candidates.push(join14(mainDir, "..", "dashboard", "dist"));
51750
- candidates.push(join14(mainDir, "..", "..", "dashboard", "dist"));
51837
+ candidates.push(join15(mainDir, "..", "dashboard", "dist"));
51838
+ candidates.push(join15(mainDir, "..", "..", "dashboard", "dist"));
51751
51839
  }
51752
- candidates.push(join14(process.cwd(), "dashboard", "dist"));
51840
+ candidates.push(join15(process.cwd(), "dashboard", "dist"));
51753
51841
  for (const candidate of candidates) {
51754
- if (existsSync15(candidate))
51842
+ if (existsSync16(candidate))
51755
51843
  return candidate;
51756
51844
  }
51757
- return join14(process.cwd(), "dashboard", "dist");
51845
+ return join15(process.cwd(), "dashboard", "dist");
51758
51846
  }
51759
51847
  function getProvidedApiKey(req) {
51760
51848
  const headerKey = req.headers.get("x-api-key");
@@ -51826,7 +51914,7 @@ function json(data, status = 200, headers) {
51826
51914
  });
51827
51915
  }
51828
51916
  function serveStaticFile(filePath) {
51829
- if (!existsSync15(filePath))
51917
+ if (!existsSync16(filePath))
51830
51918
  return null;
51831
51919
  const ext = extname(filePath);
51832
51920
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
@@ -51927,7 +52015,7 @@ data: ${data}
51927
52015
  filteredSseClients.delete(client);
51928
52016
  }
51929
52017
  const dashboardDir = resolveDashboardDir();
51930
- const dashboardExists = existsSync15(dashboardDir);
52018
+ const dashboardExists = existsSync16(dashboardDir);
51931
52019
  if (!dashboardExists) {
51932
52020
  console.error(`
51933
52021
  Dashboard not found at: ${dashboardDir}`);