@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.
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);
@@ -13777,6 +13798,10 @@ function updateTask(id, input, db) {
13777
13798
  sets.push("description = ?");
13778
13799
  params.push(input.description);
13779
13800
  }
13801
+ if (input.agent_id !== undefined) {
13802
+ sets.push("agent_id = ?");
13803
+ params.push(input.agent_id);
13804
+ }
13780
13805
  if (input.status !== undefined) {
13781
13806
  if (input.status === "completed") {
13782
13807
  checkCompletionGuard(task, task.assigned_to || task.agent_id || null, d);
@@ -17844,6 +17869,50 @@ var init_token_utils = __esm(() => {
17844
17869
  };
17845
17870
  });
17846
17871
 
17872
+ // src/lib/creator-identity.ts
17873
+ import { existsSync as existsSync7, rmSync as rmSync2 } from "fs";
17874
+ import { join as join6 } from "path";
17875
+ function identityFilePath() {
17876
+ return join6(getTodosGlobalDir(), "identity.json");
17877
+ }
17878
+ function readPersistedIdentity() {
17879
+ const path = identityFilePath();
17880
+ if (!existsSync7(path))
17881
+ return null;
17882
+ const parsed = readJsonFile(path);
17883
+ if (!parsed || typeof parsed.agent_id !== "string" || !parsed.agent_id.trim())
17884
+ return null;
17885
+ return parsed;
17886
+ }
17887
+ function canonicalAgentRef(value) {
17888
+ return value.trim().toLowerCase();
17889
+ }
17890
+ function isProcessBoundSource(source) {
17891
+ return source === "explicit" || source === "env";
17892
+ }
17893
+ function resolveWritableIdentity(explicit) {
17894
+ const resolved = resolveCreatorIdentity(explicit);
17895
+ if (!isProcessBoundSource(resolved.source))
17896
+ return { agent_id: null, source: "none" };
17897
+ return resolved;
17898
+ }
17899
+ function resolveCreatorIdentity(explicit) {
17900
+ const fromExplicit = explicit?.trim();
17901
+ if (fromExplicit)
17902
+ return { agent_id: canonicalAgentRef(fromExplicit), source: "explicit" };
17903
+ const fromEnv = (process.env["TODOS_AGENT_ID"] || process.env["HASNA_TODOS_AGENT_ID"] || "").trim();
17904
+ if (fromEnv)
17905
+ return { agent_id: canonicalAgentRef(fromEnv), source: "env" };
17906
+ const persisted = readPersistedIdentity();
17907
+ if (persisted) {
17908
+ return { agent_id: canonicalAgentRef(persisted.agent_name || persisted.agent_id), source: "persisted" };
17909
+ }
17910
+ return { agent_id: null, source: "none" };
17911
+ }
17912
+ var init_creator_identity = __esm(() => {
17913
+ init_sync_utils();
17914
+ });
17915
+
17847
17916
  // src/pr-groups/types.ts
17848
17917
  var PR_GROUP_LEDGER_SCHEMA_VERSION = 1, PR_GROUP_REPAIR_CYCLE_LIMIT = 2, PrGroupLedgerError;
17849
17918
  var init_types3 = __esm(() => {
@@ -20594,6 +20663,8 @@ function registerTaskCrudTools(server, ctx) {
20594
20663
  project_id: exports_external.string().optional().describe("Project ID"),
20595
20664
  task_list_id: exports_external.string().optional().describe("Task list ID"),
20596
20665
  assigned_to: exports_external.string().optional().describe("Agent ID or name to assign to"),
20666
+ created_by: exports_external.string().optional().describe("Agent who FILED this task. Defaults to the ambient agent identity (todos init / TODOS_AGENT_ID)."),
20667
+ unassigned: exports_external.boolean().optional().describe("Deliberately file with no assignee. Without it, the task defaults to the filer."),
20597
20668
  depends_on: exports_external.array(exports_external.string()).optional().describe("Array of task IDs this task depends on"),
20598
20669
  short_id: exports_external.string().nullable().optional().describe("Short ID (auto-generated if not provided, disabled if null)"),
20599
20670
  tags: exports_external.array(exports_external.string()).optional().describe("Tags for the task"),
@@ -20604,12 +20675,19 @@ function registerTaskCrudTools(server, ctx) {
20604
20675
  retry_count: exports_external.number().optional().describe("Max retry count for agent failures")
20605
20676
  }, async (params) => {
20606
20677
  try {
20607
- const { depends_on, assigned_to, project_id, task_list_id, tags, estimate, confidence, retry_count, deadline, ...rest } = params;
20678
+ const { depends_on, assigned_to, project_id, task_list_id, tags, estimate, confidence, retry_count, deadline, created_by, unassigned, ...rest } = params;
20679
+ const creator = resolveCreatorIdentity(created_by);
20680
+ const router = resolveWritableIdentity(created_by);
20681
+ const assignee = assigned_to || (unassigned ? undefined : router.agent_id || undefined);
20608
20682
  const cloud = getTodosCloudClient();
20609
20683
  if (cloud) {
20610
20684
  const payload = { ...rest };
20611
- if (assigned_to)
20612
- payload.assigned_to = assigned_to;
20685
+ if (creator.agent_id)
20686
+ payload.created_by = creator.agent_id;
20687
+ if (router.agent_id)
20688
+ payload.agent_id = payload.agent_id ?? router.agent_id;
20689
+ if (assignee)
20690
+ payload.assigned_to = assignee;
20613
20691
  if (project_id)
20614
20692
  payload.project_id = project_id;
20615
20693
  if (task_list_id)
@@ -20630,8 +20708,12 @@ function registerTaskCrudTools(server, ctx) {
20630
20708
  return { content: [{ type: "text", text: mutationTaskResponse(created) }] };
20631
20709
  }
20632
20710
  const resolved = { ...rest };
20633
- if (assigned_to)
20634
- resolved.assigned_to = resolveAssignee(assigned_to);
20711
+ if (creator.agent_id)
20712
+ resolved.created_by = creator.agent_id;
20713
+ if (router.agent_id)
20714
+ resolved.agent_id = resolved.agent_id ?? router.agent_id;
20715
+ if (assignee)
20716
+ resolved.assigned_to = resolveAssignee(assignee);
20635
20717
  if (project_id)
20636
20718
  resolved.project_id = resolveId(project_id, "projects");
20637
20719
  if (task_list_id)
@@ -20717,6 +20799,8 @@ function registerTaskCrudTools(server, ctx) {
20717
20799
  project_id: exports_external.string().optional().describe("Filter by project"),
20718
20800
  task_list_id: exports_external.string().optional().describe("Filter by task list"),
20719
20801
  assigned_to: exports_external.string().optional().describe("Filter by assignee (agent ID or name, empty string = unassigned)"),
20802
+ created_by: exports_external.string().optional().describe("Filter by the agent who FILED the task"),
20803
+ 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
20804
  tags: exports_external.array(exports_external.string()).optional().describe("Filter by tags (AND logic)"),
20721
20805
  created_after: exports_external.string().optional().describe("ISO date \u2014 tasks created after this date"),
20722
20806
  created_before: exports_external.string().optional().describe("ISO date \u2014 tasks created before this date"),
@@ -20909,11 +20993,12 @@ var init_task_crud2 = __esm(() => {
20909
20993
  init_tasks();
20910
20994
  init_types();
20911
20995
  init_token_utils();
20996
+ init_creator_identity();
20912
20997
  init_cloud_router();
20913
20998
  });
20914
20999
 
20915
21000
  // src/lib/project-bootstrap.ts
20916
- import { existsSync as existsSync7, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
21001
+ import { existsSync as existsSync8, readFileSync as readFileSync3, statSync as statSync3 } from "fs";
20917
21002
  import { basename as basename2, dirname as dirname5, resolve as resolve8 } from "path";
20918
21003
  function safeStat(path) {
20919
21004
  try {
@@ -20932,7 +21017,7 @@ function canonicalPath(input) {
20932
21017
  function findUp(start, marker) {
20933
21018
  let current = canonicalPath(start);
20934
21019
  while (true) {
20935
- if (existsSync7(resolve8(current, marker)))
21020
+ if (existsSync8(resolve8(current, marker)))
20936
21021
  return current;
20937
21022
  const parent = dirname5(current);
20938
21023
  if (parent === current)
@@ -20944,7 +21029,7 @@ function readPackageJson(path) {
20944
21029
  if (!path)
20945
21030
  return null;
20946
21031
  const file = resolve8(path, "package.json");
20947
- if (!existsSync7(file))
21032
+ if (!existsSync8(file))
20948
21033
  return null;
20949
21034
  try {
20950
21035
  const parsed = JSON.parse(readFileSync3(file, "utf-8"));
@@ -20966,7 +21051,7 @@ function workspaceMarker(root, rootPackage) {
20966
21051
  if (rootPackage?.workspaces)
20967
21052
  markers.push("package.json#workspaces");
20968
21053
  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)))
21054
+ if (existsSync8(resolve8(root, marker)))
20970
21055
  markers.push(marker);
20971
21056
  }
20972
21057
  const kind = markers.find((marker) => marker !== "bun.lock" && marker !== "bun.lockb") ?? null;
@@ -21279,7 +21364,7 @@ var init_tags = __esm(() => {
21279
21364
  });
21280
21365
 
21281
21366
  // src/lib/retention-cleanup.ts
21282
- import { existsSync as existsSync8, unlinkSync } from "fs";
21367
+ import { existsSync as existsSync9, unlinkSync } from "fs";
21283
21368
  function normalizeScopes(scopes) {
21284
21369
  if (!scopes || scopes.length === 0)
21285
21370
  return [...ALL_SCOPES];
@@ -21482,7 +21567,7 @@ function applyRetentionCleanup(input, db) {
21482
21567
  for (const artifact of report.candidates.artifact_files) {
21483
21568
  try {
21484
21569
  const path = artifactStorePath(artifact.relative_path);
21485
- if (!existsSync8(path)) {
21570
+ if (!existsSync9(path)) {
21486
21571
  report.warnings.push(`stored artifact already missing: ${artifact.relative_path}`);
21487
21572
  continue;
21488
21573
  }
@@ -21509,8 +21594,8 @@ var init_retention_cleanup = __esm(() => {
21509
21594
  });
21510
21595
 
21511
21596
  // 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";
21597
+ import { existsSync as existsSync10, readdirSync as readdirSync2, readFileSync as readFileSync4, statSync as statSync4 } from "fs";
21598
+ import { basename as basename3, isAbsolute, join as join7, relative as relative3, resolve as resolve9, sep as sep2 } from "path";
21514
21599
  function blankResolution(parsed) {
21515
21600
  return {
21516
21601
  input: parsed.input,
@@ -21608,7 +21693,7 @@ function resolveFile(parsed, workspace) {
21608
21693
  return resolution;
21609
21694
  }
21610
21695
  resolution.path = relPath;
21611
- if (!existsSync9(absolutePath)) {
21696
+ if (!existsSync10(absolutePath)) {
21612
21697
  resolution.warnings.push("file does not exist in the local workspace");
21613
21698
  return resolution;
21614
21699
  }
@@ -21641,7 +21726,7 @@ function walkSourceFiles(root, current = root, files = []) {
21641
21726
  if (SKIP_DIRS.has(entry.name))
21642
21727
  continue;
21643
21728
  }
21644
- const absolutePath = join6(current, entry.name);
21729
+ const absolutePath = join7(current, entry.name);
21645
21730
  if (entry.isDirectory()) {
21646
21731
  if (!SKIP_DIRS.has(entry.name))
21647
21732
  walkSourceFiles(root, absolutePath, files);
@@ -25333,7 +25418,7 @@ var init_audit_ledger = __esm(() => {
25333
25418
 
25334
25419
  // src/lib/release-compatibility.ts
25335
25420
  import { readFileSync as readFileSync5 } from "fs";
25336
- import { join as join7, resolve as resolve11 } from "path";
25421
+ import { join as join8, resolve as resolve11 } from "path";
25337
25422
  import { Database as Database2 } from "bun:sqlite";
25338
25423
  function pass(id, message, details) {
25339
25424
  return { id, status: "passed", message, details };
@@ -25345,7 +25430,7 @@ function warn(id, message, details) {
25345
25430
  return { id, status: "warning", message, details };
25346
25431
  }
25347
25432
  function readPackageJson2(root) {
25348
- return JSON.parse(readFileSync5(join7(root, "package.json"), "utf8"));
25433
+ return JSON.parse(readFileSync5(join8(root, "package.json"), "utf8"));
25349
25434
  }
25350
25435
  function sortedKeys(value) {
25351
25436
  return Object.keys(value ?? {}).sort((left, right) => left.localeCompare(right));
@@ -29358,8 +29443,8 @@ var exports_doctor = {};
29358
29443
  __export(exports_doctor, {
29359
29444
  runTodosDoctor: () => runTodosDoctor
29360
29445
  });
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";
29446
+ import { chmodSync, copyFileSync, existsSync as existsSync11, mkdirSync as mkdirSync5, statSync as statSync5 } from "fs";
29447
+ import { basename as basename4, dirname as dirname6, join as join9 } from "path";
29363
29448
  function tableExists2(db, table) {
29364
29449
  return Boolean(db.query("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(table));
29365
29450
  }
@@ -29453,7 +29538,7 @@ function findMissingProjectRoots(db) {
29453
29538
  continue;
29454
29539
  if (!row.path.startsWith("/"))
29455
29540
  continue;
29456
- if (!existsSync10(row.path))
29541
+ if (!existsSync11(row.path))
29457
29542
  missing++;
29458
29543
  }
29459
29544
  return missing;
@@ -29513,16 +29598,16 @@ function databasePermissionsAreUnsafe(dbPath) {
29513
29598
  function createBackup(dbPath) {
29514
29599
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
29515
29600
  return;
29516
- if (!existsSync10(dbPath))
29601
+ if (!existsSync11(dbPath))
29517
29602
  return;
29518
29603
  const stamp = now().replace(/[:.]/g, "-");
29519
- const backupDir = join8(dirname6(dbPath), `${basename4(dbPath)}.backup-${stamp}`);
29604
+ const backupDir = join9(dirname6(dbPath), `${basename4(dbPath)}.backup-${stamp}`);
29520
29605
  const files = [];
29521
29606
  mkdirSync5(backupDir, { recursive: true });
29522
29607
  for (const source of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
29523
- if (!existsSync10(source))
29608
+ if (!existsSync11(source))
29524
29609
  continue;
29525
- const target = join8(backupDir, basename4(source));
29610
+ const target = join9(backupDir, basename4(source));
29526
29611
  copyFileSync(source, target);
29527
29612
  files.push(target);
29528
29613
  }
@@ -32258,7 +32343,7 @@ var init_agent_run_dispatcher = __esm(() => {
32258
32343
  });
32259
32344
 
32260
32345
  // src/lib/verification-providers.ts
32261
- import { existsSync as existsSync11, readFileSync as readFileSync6 } from "fs";
32346
+ import { existsSync as existsSync12, readFileSync as readFileSync6 } from "fs";
32262
32347
  function normalizeName5(name) {
32263
32348
  const normalized = name.trim().toLowerCase();
32264
32349
  if (!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(normalized)) {
@@ -32410,7 +32495,7 @@ Timed out after ${provider.timeout_ms}ms`);
32410
32495
  };
32411
32496
  }
32412
32497
  function runCiLogProvider(input) {
32413
- const text = input.log_text ?? (input.log_path && existsSync11(input.log_path) ? readFileSync6(input.log_path, "utf-8") : "");
32498
+ const text = input.log_text ?? (input.log_path && existsSync12(input.log_path) ? readFileSync6(input.log_path, "utf-8") : "");
32414
32499
  return {
32415
32500
  status: classifyLog(text),
32416
32501
  attempts: 1,
@@ -32422,7 +32507,7 @@ function runBrowserProvider(input) {
32422
32507
  if (!input.artifact_path) {
32423
32508
  return { status: "unknown", attempts: 1, exit_code: null, output_summary: "browser provider needs a screenshot or artifact path" };
32424
32509
  }
32425
- if (!existsSync11(input.artifact_path)) {
32510
+ if (!existsSync12(input.artifact_path)) {
32426
32511
  return { status: "failed", attempts: 1, exit_code: null, output_summary: `artifact not found: ${input.artifact_path}` };
32427
32512
  }
32428
32513
  return {
@@ -34571,7 +34656,7 @@ var package_default;
34571
34656
  var init_package = __esm(() => {
34572
34657
  package_default = {
34573
34658
  name: "@hasna/todos",
34574
- version: "0.13.5",
34659
+ version: "0.13.7",
34575
34660
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
34576
34661
  type: "module",
34577
34662
  main: "dist/index.js",
@@ -35641,6 +35726,7 @@ function task(input) {
35641
35726
  reason: "Bundled deterministic onboarding fixture",
35642
35727
  spawned_from_session: null,
35643
35728
  assigned_by: null,
35729
+ created_by: null,
35644
35730
  assigned_from_project: null,
35645
35731
  task_type: "onboarding",
35646
35732
  cost_tokens: 0,
@@ -36838,8 +36924,8 @@ __export(exports_local_extensions, {
36838
36924
  discoverLocalExtensions: () => discoverLocalExtensions
36839
36925
  });
36840
36926
  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";
36927
+ import { existsSync as existsSync13, readdirSync as readdirSync3, readFileSync as readFileSync8, statSync as statSync6 } from "fs";
36928
+ import { basename as basename5, join as join10, resolve as resolve13 } from "path";
36843
36929
  function isObject2(value) {
36844
36930
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
36845
36931
  }
@@ -37098,10 +37184,10 @@ function verifyExtensionSignature(input) {
37098
37184
  }
37099
37185
  function inspectExtensionSource(source3) {
37100
37186
  const resolved = resolve13(source3);
37101
- if (!existsSync12(resolved))
37187
+ if (!existsSync13(resolved))
37102
37188
  throw new Error(`extension source not found: ${source3}`);
37103
37189
  const stat = statSync6(resolved);
37104
- const manifestPath = stat.isDirectory() ? [join9(resolved, "todos.extension.json"), join9(resolved, "extension.json")].find(existsSync12) : resolved;
37190
+ const manifestPath = stat.isDirectory() ? [join10(resolved, "todos.extension.json"), join10(resolved, "extension.json")].find(existsSync13) : resolved;
37105
37191
  if (!manifestPath)
37106
37192
  throw new Error(`extension directory ${source3} is missing todos.extension.json`);
37107
37193
  const raw = readFileSync8(manifestPath);
@@ -37197,20 +37283,20 @@ function projectExtensionSources(projectPath) {
37197
37283
  return [];
37198
37284
  const root = resolve13(projectPath);
37199
37285
  const candidates = [
37200
- join9(root, "todos.extension.json"),
37201
- join9(root, ".todos", "todos.extension.json")
37286
+ join10(root, "todos.extension.json"),
37287
+ join10(root, ".todos", "todos.extension.json")
37202
37288
  ];
37203
- const extensionDir = join9(root, ".todos", "extensions");
37204
- if (existsSync12(extensionDir)) {
37289
+ const extensionDir = join10(root, ".todos", "extensions");
37290
+ if (existsSync13(extensionDir)) {
37205
37291
  for (const entry of readdirSync3(extensionDir)) {
37206
37292
  if (entry.startsWith("."))
37207
37293
  continue;
37208
- const full = join9(extensionDir, entry);
37294
+ const full = join10(extensionDir, entry);
37209
37295
  if (statSync6(full).isDirectory() || entry.endsWith(".json"))
37210
37296
  candidates.push(full);
37211
37297
  }
37212
37298
  }
37213
- return candidates.filter(existsSync12);
37299
+ return candidates.filter(existsSync13);
37214
37300
  }
37215
37301
  function discoverLocalExtensions(options = {}) {
37216
37302
  const config = loadConfig();
@@ -41617,9 +41703,9 @@ __export(exports_extract, {
41617
41703
  buildCodebaseIndex: () => buildCodebaseIndex,
41618
41704
  EXTRACT_TAGS: () => EXTRACT_TAGS
41619
41705
  });
41620
- import { existsSync as existsSync13, readFileSync as readFileSync9, statSync as statSync7 } from "fs";
41706
+ import { existsSync as existsSync14, readFileSync as readFileSync9, statSync as statSync7 } from "fs";
41621
41707
  import { createHash as createHash11 } from "crypto";
41622
- import { relative as relative5, resolve as resolve14, join as join10 } from "path";
41708
+ import { relative as relative5, resolve as resolve14, join as join11 } from "path";
41623
41709
  function stableHash(value) {
41624
41710
  return createHash11("sha256").update(value).digest("hex");
41625
41711
  }
@@ -41628,8 +41714,8 @@ function normalizePathForMatch(value) {
41628
41714
  }
41629
41715
  function readGitignorePatterns(basePath) {
41630
41716
  const root = statSync7(basePath).isFile() ? resolve14(basePath, "..") : basePath;
41631
- const gitignorePath = join10(root, ".gitignore");
41632
- if (!existsSync13(gitignorePath))
41717
+ const gitignorePath = join11(root, ".gitignore");
41718
+ if (!existsSync14(gitignorePath))
41633
41719
  return [];
41634
41720
  try {
41635
41721
  return readFileSync9(gitignorePath, "utf-8").split(`
@@ -41771,7 +41857,7 @@ function buildCodebaseIndex(options) {
41771
41857
  const files = collectFiles(basePath, extensions, excludes, respectGitignore);
41772
41858
  const indexed = [];
41773
41859
  for (const file of files) {
41774
- const fullPath = statSync7(basePath).isFile() ? basePath : join10(basePath, file);
41860
+ const fullPath = statSync7(basePath).isFile() ? basePath : join11(basePath, file);
41775
41861
  try {
41776
41862
  const source3 = readFileSync9(fullPath, "utf-8");
41777
41863
  const relPath = statSync7(basePath).isFile() ? relative5(resolve14(basePath, ".."), fullPath) : file;
@@ -41802,7 +41888,7 @@ function extractTodos(options, db) {
41802
41888
  const files = collectFiles(basePath, extensions, excludes, respectGitignore);
41803
41889
  const allComments = [];
41804
41890
  for (const file of files) {
41805
- const fullPath = statSync7(basePath).isFile() ? basePath : join10(basePath, file);
41891
+ const fullPath = statSync7(basePath).isFile() ? basePath : join11(basePath, file);
41806
41892
  try {
41807
41893
  const source3 = readFileSync9(fullPath, "utf-8");
41808
41894
  const relPath = statSync7(basePath).isFile() ? relative5(resolve14(basePath, ".."), fullPath) : file;
@@ -42753,7 +42839,7 @@ __export(exports_builtin_templates, {
42753
42839
  BUILTIN_TEMPLATES: () => BUILTIN_TEMPLATES
42754
42840
  });
42755
42841
  import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync4 } from "fs";
42756
- import { join as join11 } from "path";
42842
+ import { join as join12 } from "path";
42757
42843
  function templateMetadata(template) {
42758
42844
  return {
42759
42845
  source: BUILTIN_TEMPLATE_LIBRARY_SOURCE,
@@ -42812,7 +42898,7 @@ function writeBuiltinTemplateFiles(directory) {
42812
42898
  mkdirSync7(directory, { recursive: true });
42813
42899
  const files = [];
42814
42900
  for (const entry of exportBuiltinTemplateFiles()) {
42815
- const path = join11(directory, entry.filename);
42901
+ const path = join12(directory, entry.filename);
42816
42902
  writeFileSync4(path, `${JSON.stringify(entry.template, null, 2)}
42817
42903
  `, "utf-8");
42818
42904
  files.push(path);
@@ -43338,16 +43424,16 @@ __export(exports_environment_snapshots, {
43338
43424
  captureEnvironmentSnapshot: () => captureEnvironmentSnapshot
43339
43425
  });
43340
43426
  import { createHash as createHash12 } from "crypto";
43341
- import { existsSync as existsSync14, readFileSync as readFileSync10, statSync as statSync8 } from "fs";
43427
+ import { existsSync as existsSync15, readFileSync as readFileSync10, statSync as statSync8 } from "fs";
43342
43428
  import { hostname as hostname2, platform, arch } from "os";
43343
- import { dirname as dirname8, join as join12, resolve as resolve15 } from "path";
43429
+ import { dirname as dirname8, join as join13, resolve as resolve15 } from "path";
43344
43430
  import { tmpdir as tmpdir3 } from "os";
43345
43431
  function sha2567(value) {
43346
43432
  return createHash12("sha256").update(value).digest("hex");
43347
43433
  }
43348
43434
  function fileRecord(root, relativePath) {
43349
- const path = join12(root, relativePath);
43350
- if (!existsSync14(path))
43435
+ const path = join13(root, relativePath);
43436
+ if (!existsSync15(path))
43351
43437
  return null;
43352
43438
  const stat = statSync8(path);
43353
43439
  if (!stat.isFile())
@@ -43359,7 +43445,7 @@ function manifestRecord(root, relativePath) {
43359
43445
  const base = fileRecord(root, relativePath);
43360
43446
  if (!base)
43361
43447
  return null;
43362
- const parsed = readJsonFile(join12(root, relativePath));
43448
+ const parsed = readJsonFile(join13(root, relativePath));
43363
43449
  if (!parsed)
43364
43450
  return { ...base, redacted: {} };
43365
43451
  const redacted = redactValue({
@@ -43454,8 +43540,8 @@ function commandEnv(env, includeValues) {
43454
43540
  function defaultSnapshotDir() {
43455
43541
  const dbPath = getDatabasePath();
43456
43542
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
43457
- return join12(tmpdir3(), "hasna-todos", "environment-snapshots");
43458
- return join12(dirname8(resolve15(dbPath)), "environment-snapshots");
43543
+ return join13(tmpdir3(), "hasna-todos", "environment-snapshots");
43544
+ return join13(dirname8(resolve15(dbPath)), "environment-snapshots");
43459
43545
  }
43460
43546
  function snapshotWithId(snapshot) {
43461
43547
  const digest = sha2567(JSON.stringify(snapshot)).slice(0, 24);
@@ -43502,7 +43588,7 @@ function captureEnvironmentSnapshot(input = {}) {
43502
43588
  });
43503
43589
  }
43504
43590
  function writeEnvironmentSnapshot(snapshot, outputPath) {
43505
- const path = outputPath ? resolve15(outputPath) : join12(defaultSnapshotDir(), `${snapshot.id}.json`);
43591
+ const path = outputPath ? resolve15(outputPath) : join13(defaultSnapshotDir(), `${snapshot.id}.json`);
43506
43592
  ensureDir2(dirname8(path));
43507
43593
  writeJsonFile(path, snapshot);
43508
43594
  return path;
@@ -44401,7 +44487,11 @@ function createLocalSqliteTodosStorageAdapter(options = {}) {
44401
44487
  sync: true
44402
44488
  },
44403
44489
  tasks: {
44404
- create: (input) => createTask(input, database()),
44490
+ create: (input, context) => createTask({
44491
+ ...input,
44492
+ agent_id: input.agent_id ?? context?.agentId,
44493
+ created_by: input.created_by ?? input.agent_id ?? context?.agentId
44494
+ }, database()),
44405
44495
  get: (id) => getTask(id, database()),
44406
44496
  resolveRef: (ref) => resolveTaskRefLocal(database(), ref),
44407
44497
  list: (filter = {}) => listTasksMaybeSearch(filter, database()),
@@ -45540,6 +45630,10 @@ class PostgresJsonRecordStore {
45540
45630
  conds.push(`payload->>'assigned_to' = ${p(filter.assigned_to)}`);
45541
45631
  if (filter.agent_id !== undefined)
45542
45632
  conds.push(`payload->>'agent_id' = ${p(filter.agent_id)}`);
45633
+ if (filter.created_by !== undefined)
45634
+ conds.push(`LOWER(payload->>'created_by') = LOWER(${p(filter.created_by)})`);
45635
+ if (filter.not_created_by !== undefined)
45636
+ conds.push(`(payload->>'created_by' IS NULL OR LOWER(payload->>'created_by') <> LOWER(${p(filter.not_created_by)}))`);
45543
45637
  if (filter.session_id !== undefined)
45544
45638
  conds.push(`payload->>'session_id' = ${p(filter.session_id)}`);
45545
45639
  if (filter.tags?.length) {
@@ -46053,7 +46147,7 @@ async function createTask3(input, store, context) {
46053
46147
  description: input.description ?? null,
46054
46148
  status: input.status ?? "pending",
46055
46149
  priority: input.priority ?? "medium",
46056
- agent_id: input.agent_id ?? null,
46150
+ agent_id: input.agent_id ?? context?.agentId ?? null,
46057
46151
  assigned_to: input.assigned_to ?? null,
46058
46152
  session_id: input.session_id ?? context?.sessionId ?? null,
46059
46153
  working_dir: input.working_dir ?? null,
@@ -46078,7 +46172,8 @@ async function createTask3(input, store, context) {
46078
46172
  confidence: input.confidence ?? null,
46079
46173
  reason: input.reason ?? null,
46080
46174
  spawned_from_session: input.spawned_from_session ?? null,
46081
- assigned_by: input.assigned_by ?? null,
46175
+ assigned_by: input.assigned_by ?? input.agent_id ?? context?.agentId ?? null,
46176
+ created_by: input.created_by ?? input.agent_id ?? context?.agentId ?? null,
46082
46177
  assigned_from_project: input.assigned_from_project ?? null,
46083
46178
  task_type: input.task_type ?? null,
46084
46179
  cost_tokens: 0,
@@ -46115,7 +46210,8 @@ async function updateTask2(id, input, store) {
46115
46210
  tags: input.tags ?? existing.tags,
46116
46211
  metadata: input.metadata ?? existing.metadata,
46117
46212
  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
46213
+ task_list_id: input.task_list_id !== undefined ? input.task_list_id : existing.task_list_id,
46214
+ created_by: existing.created_by
46119
46215
  };
46120
46216
  await store.upsert("tasks", task2);
46121
46217
  return task2;
@@ -47798,7 +47894,7 @@ var init_headless_boundaries = __esm(() => {
47798
47894
  });
47799
47895
 
47800
47896
  // src/server/routes.ts
47801
- import { join as join13, resolve as resolve16, sep as sep3 } from "path";
47897
+ import { join as join14, resolve as resolve16, sep as sep3 } from "path";
47802
47898
  function parseFieldsParam(url) {
47803
47899
  const fieldsParam = url.searchParams.get("fields");
47804
47900
  return fieldsParam ? fieldsParam.split(",").map((f) => f.trim()).filter(Boolean) : undefined;
@@ -47979,11 +48075,15 @@ async function handleCreateTask(req, ctx, json2, taskToSummary2) {
47979
48075
  const body = await req.json();
47980
48076
  if (!body.title)
47981
48077
  return json2({ error: "Missing 'title'" }, 400);
48078
+ const createdBy = body.created_by ?? body.agent_id ?? "dashboard";
47982
48079
  const task2 = createTask({
47983
48080
  title: body.title,
47984
48081
  description: body.description,
47985
48082
  priority: body.priority,
47986
- project_id: body.project_id
48083
+ project_id: body.project_id,
48084
+ agent_id: body.agent_id ?? createdBy,
48085
+ created_by: createdBy,
48086
+ ...body.assigned_to ? { assigned_to: body.assigned_to } : {}
47987
48087
  });
47988
48088
  ctx.broadcastEvent({ type: "task", task_id: task2.id, action: "created", agent_id: task2.agent_id, project_id: task2.project_id });
47989
48089
  return json2(taskToSummary2(task2), 201);
@@ -48589,7 +48689,7 @@ function handleStaticFiles(path, method, ctx, json2, serveStaticFile2) {
48589
48689
  if (!ctx.dashboardExists || method !== "GET" && method !== "HEAD")
48590
48690
  return null;
48591
48691
  if (path !== "/") {
48592
- const filePath = join13(ctx.dashboardDir, path);
48692
+ const filePath = join14(ctx.dashboardDir, path);
48593
48693
  const resolvedFile = resolve16(filePath);
48594
48694
  const resolvedBase = resolve16(ctx.dashboardDir);
48595
48695
  if (!resolvedFile.startsWith(resolvedBase + sep3) && resolvedFile !== resolvedBase) {
@@ -48599,7 +48699,7 @@ function handleStaticFiles(path, method, ctx, json2, serveStaticFile2) {
48599
48699
  if (res2)
48600
48700
  return res2;
48601
48701
  }
48602
- const indexPath = join13(ctx.dashboardDir, "index.html");
48702
+ const indexPath = join14(ctx.dashboardDir, "index.html");
48603
48703
  const res = serveStaticFile2(indexPath);
48604
48704
  if (res)
48605
48705
  return res;
@@ -50776,6 +50876,8 @@ async function handleV1Request(req, url, dependencies = {}) {
50776
50876
  ...url.searchParams.get("task_list_id") ? { task_list_id: url.searchParams.get("task_list_id") } : {},
50777
50877
  ...url.searchParams.get("assigned_to") ? { assigned_to: url.searchParams.get("assigned_to") } : {},
50778
50878
  ...url.searchParams.get("agent_id") ? { agent_id: url.searchParams.get("agent_id") } : {},
50879
+ ...url.searchParams.get("created_by") ? { created_by: url.searchParams.get("created_by") } : {},
50880
+ ...url.searchParams.get("not_created_by") ? { not_created_by: url.searchParams.get("not_created_by") } : {},
50779
50881
  ...url.searchParams.get("tags") ? {
50780
50882
  tags: url.searchParams.get("tags").split(",").map((tag) => tag.trim()).filter(Boolean)
50781
50883
  } : {},
@@ -51734,27 +51836,27 @@ __export(exports_serve, {
51734
51836
  SECURITY_HEADERS: () => SECURITY_HEADERS,
51735
51837
  MIME_TYPES: () => MIME_TYPES
51736
51838
  });
51737
- import { existsSync as existsSync15 } from "fs";
51738
- import { join as join14, dirname as dirname9, extname } from "path";
51839
+ import { existsSync as existsSync16 } from "fs";
51840
+ import { join as join15, dirname as dirname9, extname } from "path";
51739
51841
  import { fileURLToPath } from "url";
51740
51842
  function resolveDashboardDir() {
51741
51843
  const candidates = [];
51742
51844
  try {
51743
51845
  const scriptDir = dirname9(fileURLToPath(import.meta.url));
51744
- candidates.push(join14(scriptDir, "..", "dashboard", "dist"));
51745
- candidates.push(join14(scriptDir, "..", "..", "dashboard", "dist"));
51846
+ candidates.push(join15(scriptDir, "..", "dashboard", "dist"));
51847
+ candidates.push(join15(scriptDir, "..", "..", "dashboard", "dist"));
51746
51848
  } catch {}
51747
51849
  if (process.argv[1]) {
51748
51850
  const mainDir = dirname9(process.argv[1]);
51749
- candidates.push(join14(mainDir, "..", "dashboard", "dist"));
51750
- candidates.push(join14(mainDir, "..", "..", "dashboard", "dist"));
51851
+ candidates.push(join15(mainDir, "..", "dashboard", "dist"));
51852
+ candidates.push(join15(mainDir, "..", "..", "dashboard", "dist"));
51751
51853
  }
51752
- candidates.push(join14(process.cwd(), "dashboard", "dist"));
51854
+ candidates.push(join15(process.cwd(), "dashboard", "dist"));
51753
51855
  for (const candidate of candidates) {
51754
- if (existsSync15(candidate))
51856
+ if (existsSync16(candidate))
51755
51857
  return candidate;
51756
51858
  }
51757
- return join14(process.cwd(), "dashboard", "dist");
51859
+ return join15(process.cwd(), "dashboard", "dist");
51758
51860
  }
51759
51861
  function getProvidedApiKey(req) {
51760
51862
  const headerKey = req.headers.get("x-api-key");
@@ -51826,7 +51928,7 @@ function json(data, status = 200, headers) {
51826
51928
  });
51827
51929
  }
51828
51930
  function serveStaticFile(filePath) {
51829
- if (!existsSync15(filePath))
51931
+ if (!existsSync16(filePath))
51830
51932
  return null;
51831
51933
  const ext = extname(filePath);
51832
51934
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
@@ -51927,7 +52029,7 @@ data: ${data}
51927
52029
  filteredSseClients.delete(client);
51928
52030
  }
51929
52031
  const dashboardDir = resolveDashboardDir();
51930
- const dashboardExists = existsSync15(dashboardDir);
52032
+ const dashboardExists = existsSync16(dashboardDir);
51931
52033
  if (!dashboardExists) {
51932
52034
  console.error(`
51933
52035
  Dashboard not found at: ${dashboardDir}`);