@hasna/todos 0.11.44 → 0.11.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/README.md +695 -11
  2. package/dist/cli/commands/agent-reliability-commands.d.ts +3 -0
  3. package/dist/cli/commands/agent-reliability-commands.d.ts.map +1 -0
  4. package/dist/cli/commands/audit-ledger-commands.d.ts +3 -0
  5. package/dist/cli/commands/audit-ledger-commands.d.ts.map +1 -0
  6. package/dist/cli/commands/capacity-commands.d.ts +3 -0
  7. package/dist/cli/commands/capacity-commands.d.ts.map +1 -0
  8. package/dist/cli/commands/config-serve-commands.d.ts.map +1 -1
  9. package/dist/cli/commands/help-commands.d.ts +3 -0
  10. package/dist/cli/commands/help-commands.d.ts.map +1 -0
  11. package/dist/cli/commands/knowledge-commands.d.ts +3 -0
  12. package/dist/cli/commands/knowledge-commands.d.ts.map +1 -0
  13. package/dist/cli/commands/local-backup-commands.d.ts +3 -0
  14. package/dist/cli/commands/local-backup-commands.d.ts.map +1 -0
  15. package/dist/cli/commands/local-snapshot-commands.d.ts +3 -0
  16. package/dist/cli/commands/local-snapshot-commands.d.ts.map +1 -0
  17. package/dist/cli/commands/machines.d.ts.map +1 -1
  18. package/dist/cli/commands/mcp-hooks-commands.d.ts.map +1 -1
  19. package/dist/cli/commands/onboarding-commands.d.ts +3 -0
  20. package/dist/cli/commands/onboarding-commands.d.ts.map +1 -0
  21. package/dist/cli/commands/project-commands.d.ts.map +1 -1
  22. package/dist/cli/commands/query-commands.d.ts.map +1 -1
  23. package/dist/cli/commands/release-compatibility-commands.d.ts +3 -0
  24. package/dist/cli/commands/release-compatibility-commands.d.ts.map +1 -0
  25. package/dist/cli/commands/retrospective-commands.d.ts +3 -0
  26. package/dist/cli/commands/retrospective-commands.d.ts.map +1 -0
  27. package/dist/cli/commands/review-queue-commands.d.ts +3 -0
  28. package/dist/cli/commands/review-queue-commands.d.ts.map +1 -0
  29. package/dist/cli/commands/risk-commands.d.ts +3 -0
  30. package/dist/cli/commands/risk-commands.d.ts.map +1 -0
  31. package/dist/cli/commands/roadmap-commands.d.ts +3 -0
  32. package/dist/cli/commands/roadmap-commands.d.ts.map +1 -0
  33. package/dist/cli/commands/scale-hardening-commands.d.ts +3 -0
  34. package/dist/cli/commands/scale-hardening-commands.d.ts.map +1 -0
  35. package/dist/cli/commands/sdk-fixture-commands.d.ts +3 -0
  36. package/dist/cli/commands/sdk-fixture-commands.d.ts.map +1 -0
  37. package/dist/cli/commands/task-commands.d.ts.map +1 -1
  38. package/dist/cli/commands/usage-ledger-commands.d.ts +3 -0
  39. package/dist/cli/commands/usage-ledger-commands.d.ts.map +1 -0
  40. package/dist/cli/components/Dashboard.d.ts.map +1 -1
  41. package/dist/cli/index.js +39446 -19769
  42. package/dist/cli-mcp-parity.d.ts +1 -1
  43. package/dist/cli-mcp-parity.d.ts.map +1 -1
  44. package/dist/contracts.d.ts +25 -0
  45. package/dist/contracts.d.ts.map +1 -1
  46. package/dist/contracts.js +14818 -3885
  47. package/dist/db/agent-metrics.d.ts +101 -0
  48. package/dist/db/agent-metrics.d.ts.map +1 -1
  49. package/dist/db/agent-names.d.ts +2 -1
  50. package/dist/db/agent-names.d.ts.map +1 -1
  51. package/dist/db/boards.d.ts +56 -0
  52. package/dist/db/boards.d.ts.map +1 -0
  53. package/dist/db/calendar.d.ts +52 -0
  54. package/dist/db/calendar.d.ts.map +1 -0
  55. package/dist/db/comments.d.ts.map +1 -1
  56. package/dist/db/handoffs.d.ts +25 -0
  57. package/dist/db/handoffs.d.ts.map +1 -1
  58. package/dist/db/machines.d.ts +19 -6
  59. package/dist/db/machines.d.ts.map +1 -1
  60. package/dist/db/migrations.d.ts.map +1 -1
  61. package/dist/db/project-knowledge.d.ts +88 -0
  62. package/dist/db/project-knowledge.d.ts.map +1 -0
  63. package/dist/db/project-risks.d.ts +139 -0
  64. package/dist/db/project-risks.d.ts.map +1 -0
  65. package/dist/db/retrospectives.d.ts +98 -0
  66. package/dist/db/retrospectives.d.ts.map +1 -0
  67. package/dist/db/schema.d.ts.map +1 -1
  68. package/dist/db/task-crud.d.ts.map +1 -1
  69. package/dist/db/task-relations.d.ts +69 -9
  70. package/dist/db/task-relations.d.ts.map +1 -1
  71. package/dist/db/task-runs.d.ts +3 -0
  72. package/dist/db/task-runs.d.ts.map +1 -1
  73. package/dist/db/tasks.d.ts +6 -2
  74. package/dist/db/tasks.d.ts.map +1 -1
  75. package/dist/index.d.ts +74 -11
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.js +24662 -11829
  78. package/dist/json-contracts.d.ts.map +1 -1
  79. package/dist/lib/agent-replay-simulator.d.ts +66 -0
  80. package/dist/lib/agent-replay-simulator.d.ts.map +1 -0
  81. package/dist/lib/audit-ledger.d.ts +59 -0
  82. package/dist/lib/audit-ledger.d.ts.map +1 -0
  83. package/dist/lib/branch-work-plans.d.ts +46 -0
  84. package/dist/lib/branch-work-plans.d.ts.map +1 -0
  85. package/dist/lib/capacity-forecasts.d.ts +70 -0
  86. package/dist/lib/capacity-forecasts.d.ts.map +1 -0
  87. package/dist/lib/cli-help.d.ts +38 -0
  88. package/dist/lib/cli-help.d.ts.map +1 -0
  89. package/dist/lib/config.d.ts +217 -0
  90. package/dist/lib/config.d.ts.map +1 -1
  91. package/dist/lib/context-packs.d.ts +26 -3
  92. package/dist/lib/context-packs.d.ts.map +1 -1
  93. package/dist/lib/event-hooks.d.ts +1 -1
  94. package/dist/lib/event-hooks.d.ts.map +1 -1
  95. package/dist/lib/external-issue-importers.d.ts +60 -0
  96. package/dist/lib/external-issue-importers.d.ts.map +1 -0
  97. package/dist/lib/extract.d.ts +57 -0
  98. package/dist/lib/extract.d.ts.map +1 -1
  99. package/dist/lib/local-backups.d.ts +129 -0
  100. package/dist/lib/local-backups.d.ts.map +1 -0
  101. package/dist/lib/local-bridge.d.ts +3 -1
  102. package/dist/lib/local-bridge.d.ts.map +1 -1
  103. package/dist/lib/local-extensions.d.ts +92 -0
  104. package/dist/lib/local-extensions.d.ts.map +1 -0
  105. package/dist/lib/local-notifications.d.ts +55 -0
  106. package/dist/lib/local-notifications.d.ts.map +1 -0
  107. package/dist/lib/local-reports.d.ts +149 -0
  108. package/dist/lib/local-reports.d.ts.map +1 -0
  109. package/dist/lib/local-snapshots.d.ts +66 -0
  110. package/dist/lib/local-snapshots.d.ts.map +1 -0
  111. package/dist/lib/mention-resolver.d.ts +43 -0
  112. package/dist/lib/mention-resolver.d.ts.map +1 -0
  113. package/dist/lib/natural-language-intake.d.ts +56 -0
  114. package/dist/lib/natural-language-intake.d.ts.map +1 -0
  115. package/dist/lib/onboarding-fixtures.d.ts +31 -0
  116. package/dist/lib/onboarding-fixtures.d.ts.map +1 -0
  117. package/dist/lib/public-release-gate.d.ts +7 -0
  118. package/dist/lib/public-release-gate.d.ts.map +1 -1
  119. package/dist/lib/redaction.d.ts +9 -0
  120. package/dist/lib/redaction.d.ts.map +1 -1
  121. package/dist/lib/release-compatibility.d.ts +59 -0
  122. package/dist/lib/release-compatibility.d.ts.map +1 -0
  123. package/dist/lib/release-notes.d.ts +81 -0
  124. package/dist/lib/release-notes.d.ts.map +1 -0
  125. package/dist/lib/retention-cleanup.d.ts +63 -0
  126. package/dist/lib/retention-cleanup.d.ts.map +1 -0
  127. package/dist/lib/review-queues.d.ts +98 -0
  128. package/dist/lib/review-queues.d.ts.map +1 -0
  129. package/dist/lib/roadmaps.d.ts +133 -0
  130. package/dist/lib/roadmaps.d.ts.map +1 -0
  131. package/dist/lib/scale-hardening.d.ts +74 -0
  132. package/dist/lib/scale-hardening.d.ts.map +1 -0
  133. package/dist/lib/sdk-integration-fixtures.d.ts +65 -0
  134. package/dist/lib/sdk-integration-fixtures.d.ts.map +1 -0
  135. package/dist/lib/terminal-notifications.d.ts +53 -0
  136. package/dist/lib/terminal-notifications.d.ts.map +1 -0
  137. package/dist/lib/todos-md.d.ts.map +1 -1
  138. package/dist/lib/tui-dashboard.d.ts +49 -0
  139. package/dist/lib/tui-dashboard.d.ts.map +1 -0
  140. package/dist/lib/usage-ledger.d.ts +82 -0
  141. package/dist/lib/usage-ledger.d.ts.map +1 -0
  142. package/dist/lib/workflow-prompts.d.ts +38 -0
  143. package/dist/lib/workflow-prompts.d.ts.map +1 -0
  144. package/dist/lib/workflow-states.d.ts +70 -0
  145. package/dist/lib/workflow-states.d.ts.map +1 -0
  146. package/dist/mcp/index.d.ts.map +1 -1
  147. package/dist/mcp/index.js +25922 -13067
  148. package/dist/mcp/token-utils.d.ts.map +1 -1
  149. package/dist/mcp/tools/code-tools.d.ts.map +1 -1
  150. package/dist/mcp/tools/machines.d.ts.map +1 -1
  151. package/dist/mcp/tools/task-adv-tools.d.ts.map +1 -1
  152. package/dist/mcp/tools/task-auto-tools.d.ts.map +1 -1
  153. package/dist/mcp/tools/task-crud.d.ts.map +1 -1
  154. package/dist/mcp/tools/task-meta-tools.d.ts.map +1 -1
  155. package/dist/mcp/tools/task-project-tools.d.ts.map +1 -1
  156. package/dist/mcp/tools/task-rel-tools.d.ts.map +1 -1
  157. package/dist/mcp/tools/task-resources.d.ts.map +1 -1
  158. package/dist/mcp/tools/workflow-prompts.d.ts +3 -0
  159. package/dist/mcp/tools/workflow-prompts.d.ts.map +1 -0
  160. package/dist/mcp.js +107 -2
  161. package/dist/registry.js +15919 -6050
  162. package/dist/server/index.js +645 -143
  163. package/dist/storage.js +2516 -159
  164. package/dist/types/index.d.ts +214 -0
  165. package/dist/types/index.d.ts.map +1 -1
  166. package/package.json +1 -1
  167. package/dist/release-provenance.json +0 -7
@@ -994,6 +994,152 @@ var init_migrations = __esm(() => {
994
994
  );
995
995
  CREATE INDEX IF NOT EXISTS idx_saved_search_views_scope ON saved_search_views(scope);
996
996
  INSERT OR IGNORE INTO _migrations (id) VALUES (55);
997
+ `,
998
+ `
999
+ ALTER TABLE task_time_logs ADD COLUMN run_id TEXT;
1000
+ ALTER TABLE task_time_logs ADD COLUMN focus_session_id TEXT;
1001
+ CREATE INDEX IF NOT EXISTS idx_task_time_logs_run ON task_time_logs(run_id);
1002
+ CREATE INDEX IF NOT EXISTS idx_task_time_logs_focus_session ON task_time_logs(focus_session_id);
1003
+ CREATE TABLE IF NOT EXISTS focus_sessions (
1004
+ id TEXT PRIMARY KEY,
1005
+ task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
1006
+ plan_id TEXT REFERENCES plans(id) ON DELETE SET NULL,
1007
+ run_id TEXT,
1008
+ agent_id TEXT,
1009
+ title TEXT,
1010
+ status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'paused', 'completed', 'cancelled')),
1011
+ started_at TEXT NOT NULL,
1012
+ last_resumed_at TEXT,
1013
+ paused_at TEXT,
1014
+ ended_at TEXT,
1015
+ actual_minutes INTEGER NOT NULL DEFAULT 0,
1016
+ idle_after_minutes INTEGER,
1017
+ notes TEXT,
1018
+ metadata TEXT DEFAULT '{}',
1019
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
1020
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
1021
+ );
1022
+ CREATE INDEX IF NOT EXISTS idx_focus_sessions_task ON focus_sessions(task_id);
1023
+ CREATE INDEX IF NOT EXISTS idx_focus_sessions_plan ON focus_sessions(plan_id);
1024
+ CREATE INDEX IF NOT EXISTS idx_focus_sessions_run ON focus_sessions(run_id);
1025
+ CREATE INDEX IF NOT EXISTS idx_focus_sessions_agent ON focus_sessions(agent_id);
1026
+ CREATE INDEX IF NOT EXISTS idx_focus_sessions_status ON focus_sessions(status);
1027
+ INSERT OR IGNORE INTO _migrations (id) VALUES (56);
1028
+ `,
1029
+ `
1030
+ CREATE TABLE IF NOT EXISTS task_boards (
1031
+ id TEXT PRIMARY KEY,
1032
+ name TEXT NOT NULL UNIQUE,
1033
+ scope TEXT NOT NULL DEFAULT 'tasks' CHECK(scope IN ('tasks', 'plans')),
1034
+ project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
1035
+ task_list_id TEXT REFERENCES task_lists(id) ON DELETE SET NULL,
1036
+ plan_id TEXT REFERENCES plans(id) ON DELETE SET NULL,
1037
+ agent_id TEXT,
1038
+ lanes TEXT NOT NULL DEFAULT '[]',
1039
+ filters TEXT NOT NULL DEFAULT '{}',
1040
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
1041
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
1042
+ );
1043
+ CREATE INDEX IF NOT EXISTS idx_task_boards_scope ON task_boards(scope);
1044
+ CREATE INDEX IF NOT EXISTS idx_task_boards_project ON task_boards(project_id);
1045
+ CREATE INDEX IF NOT EXISTS idx_task_boards_plan ON task_boards(plan_id);
1046
+ INSERT OR IGNORE INTO _migrations (id) VALUES (57);
1047
+ `,
1048
+ `
1049
+ CREATE TABLE IF NOT EXISTS local_calendar_items (
1050
+ id TEXT PRIMARY KEY,
1051
+ kind TEXT NOT NULL CHECK(kind IN ('task_due', 'task_sla', 'task_reminder', 'milestone', 'work_block', 'run', 'imported')),
1052
+ title TEXT NOT NULL,
1053
+ description TEXT,
1054
+ starts_at TEXT NOT NULL,
1055
+ ends_at TEXT,
1056
+ timezone TEXT,
1057
+ project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
1058
+ task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
1059
+ plan_id TEXT REFERENCES plans(id) ON DELETE SET NULL,
1060
+ run_id TEXT,
1061
+ recurrence_rule TEXT,
1062
+ metadata TEXT DEFAULT '{}',
1063
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
1064
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
1065
+ );
1066
+ CREATE INDEX IF NOT EXISTS idx_local_calendar_items_time ON local_calendar_items(starts_at, ends_at);
1067
+ CREATE INDEX IF NOT EXISTS idx_local_calendar_items_task ON local_calendar_items(task_id);
1068
+ CREATE INDEX IF NOT EXISTS idx_local_calendar_items_project ON local_calendar_items(project_id);
1069
+ CREATE INDEX IF NOT EXISTS idx_local_calendar_items_kind ON local_calendar_items(kind);
1070
+ INSERT OR IGNORE INTO _migrations (id) VALUES (58);
1071
+ `,
1072
+ `
1073
+ CREATE TABLE IF NOT EXISTS project_knowledge_records (
1074
+ id TEXT PRIMARY KEY,
1075
+ record_type TEXT NOT NULL CHECK(record_type IN ('decision','architecture_note','tradeoff','context_snapshot')),
1076
+ title TEXT NOT NULL,
1077
+ content TEXT,
1078
+ decision TEXT,
1079
+ rationale TEXT,
1080
+ alternatives TEXT DEFAULT '[]',
1081
+ task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
1082
+ project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
1083
+ plan_id TEXT REFERENCES plans(id) ON DELETE SET NULL,
1084
+ agent_id TEXT,
1085
+ snapshot_id TEXT REFERENCES context_snapshots(id) ON DELETE SET NULL,
1086
+ tags TEXT DEFAULT '[]',
1087
+ metadata TEXT DEFAULT '{}',
1088
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
1089
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
1090
+ );
1091
+ CREATE INDEX IF NOT EXISTS idx_project_knowledge_type ON project_knowledge_records(record_type);
1092
+ CREATE INDEX IF NOT EXISTS idx_project_knowledge_project ON project_knowledge_records(project_id);
1093
+ CREATE INDEX IF NOT EXISTS idx_project_knowledge_task ON project_knowledge_records(task_id);
1094
+ CREATE INDEX IF NOT EXISTS idx_project_knowledge_plan ON project_knowledge_records(plan_id);
1095
+ CREATE INDEX IF NOT EXISTS idx_project_knowledge_agent ON project_knowledge_records(agent_id);
1096
+ CREATE INDEX IF NOT EXISTS idx_project_knowledge_snapshot ON project_knowledge_records(snapshot_id);
1097
+ INSERT OR IGNORE INTO _migrations (id) VALUES (59);
1098
+ `,
1099
+ `
1100
+ CREATE TABLE IF NOT EXISTS project_risks (
1101
+ id TEXT PRIMARY KEY,
1102
+ title TEXT NOT NULL,
1103
+ description TEXT,
1104
+ status TEXT NOT NULL DEFAULT 'open' CHECK(status IN ('open','mitigating','resolved','accepted')),
1105
+ severity TEXT NOT NULL DEFAULT 'medium' CHECK(severity IN ('low','medium','high','critical')),
1106
+ probability TEXT NOT NULL DEFAULT 'medium' CHECK(probability IN ('low','medium','high')),
1107
+ owner TEXT,
1108
+ mitigation TEXT,
1109
+ due_at TEXT,
1110
+ project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
1111
+ plan_id TEXT REFERENCES plans(id) ON DELETE SET NULL,
1112
+ task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
1113
+ tags TEXT DEFAULT '[]',
1114
+ metadata TEXT DEFAULT '{}',
1115
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
1116
+ updated_at TEXT NOT NULL DEFAULT (datetime('now')),
1117
+ closed_at TEXT
1118
+ );
1119
+ CREATE INDEX IF NOT EXISTS idx_project_risks_status ON project_risks(status);
1120
+ CREATE INDEX IF NOT EXISTS idx_project_risks_severity ON project_risks(severity);
1121
+ CREATE INDEX IF NOT EXISTS idx_project_risks_project ON project_risks(project_id);
1122
+ CREATE INDEX IF NOT EXISTS idx_project_risks_plan ON project_risks(plan_id);
1123
+ CREATE INDEX IF NOT EXISTS idx_project_risks_task ON project_risks(task_id);
1124
+ CREATE INDEX IF NOT EXISTS idx_project_risks_due ON project_risks(due_at);
1125
+ INSERT OR IGNORE INTO _migrations (id) VALUES (60);
1126
+ `,
1127
+ `
1128
+ CREATE TABLE IF NOT EXISTS local_retrospectives (
1129
+ id TEXT PRIMARY KEY,
1130
+ title TEXT NOT NULL,
1131
+ scope TEXT NOT NULL CHECK(scope IN ('project','plan')),
1132
+ project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
1133
+ plan_id TEXT REFERENCES plans(id) ON DELETE SET NULL,
1134
+ agent_id TEXT,
1135
+ report_json TEXT NOT NULL,
1136
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
1137
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
1138
+ );
1139
+ CREATE INDEX IF NOT EXISTS idx_local_retrospectives_project ON local_retrospectives(project_id);
1140
+ CREATE INDEX IF NOT EXISTS idx_local_retrospectives_plan ON local_retrospectives(plan_id);
1141
+ CREATE INDEX IF NOT EXISTS idx_local_retrospectives_agent ON local_retrospectives(agent_id);
1142
+ INSERT OR IGNORE INTO _migrations (id) VALUES (61);
997
1143
  `
998
1144
  ];
999
1145
  });
@@ -1181,6 +1327,89 @@ function ensureSchema(db) {
1181
1327
  updated_at TEXT NOT NULL DEFAULT (datetime('now'))
1182
1328
  )`);
1183
1329
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_saved_search_views_scope ON saved_search_views(scope)");
1330
+ ensureTable("context_snapshots", `
1331
+ CREATE TABLE context_snapshots (
1332
+ id TEXT PRIMARY KEY,
1333
+ agent_id TEXT,
1334
+ task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
1335
+ project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
1336
+ snapshot_type TEXT NOT NULL CHECK(snapshot_type IN ('interrupt','complete','handoff','checkpoint')),
1337
+ plan_summary TEXT,
1338
+ files_open TEXT DEFAULT '[]',
1339
+ attempts TEXT DEFAULT '[]',
1340
+ blockers TEXT DEFAULT '[]',
1341
+ next_steps TEXT,
1342
+ metadata TEXT DEFAULT '{}',
1343
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
1344
+ )`);
1345
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_snapshots_agent ON context_snapshots(agent_id)");
1346
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_snapshots_task ON context_snapshots(task_id)");
1347
+ ensureTable("project_knowledge_records", `
1348
+ CREATE TABLE project_knowledge_records (
1349
+ id TEXT PRIMARY KEY,
1350
+ record_type TEXT NOT NULL CHECK(record_type IN ('decision','architecture_note','tradeoff','context_snapshot')),
1351
+ title TEXT NOT NULL,
1352
+ content TEXT,
1353
+ decision TEXT,
1354
+ rationale TEXT,
1355
+ alternatives TEXT DEFAULT '[]',
1356
+ task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
1357
+ project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
1358
+ plan_id TEXT REFERENCES plans(id) ON DELETE SET NULL,
1359
+ agent_id TEXT,
1360
+ snapshot_id TEXT REFERENCES context_snapshots(id) ON DELETE SET NULL,
1361
+ tags TEXT DEFAULT '[]',
1362
+ metadata TEXT DEFAULT '{}',
1363
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
1364
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
1365
+ )`);
1366
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_knowledge_type ON project_knowledge_records(record_type)");
1367
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_knowledge_project ON project_knowledge_records(project_id)");
1368
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_knowledge_task ON project_knowledge_records(task_id)");
1369
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_knowledge_plan ON project_knowledge_records(plan_id)");
1370
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_knowledge_agent ON project_knowledge_records(agent_id)");
1371
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_knowledge_snapshot ON project_knowledge_records(snapshot_id)");
1372
+ ensureTable("project_risks", `
1373
+ CREATE TABLE project_risks (
1374
+ id TEXT PRIMARY KEY,
1375
+ title TEXT NOT NULL,
1376
+ description TEXT,
1377
+ status TEXT NOT NULL DEFAULT 'open' CHECK(status IN ('open','mitigating','resolved','accepted')),
1378
+ severity TEXT NOT NULL DEFAULT 'medium' CHECK(severity IN ('low','medium','high','critical')),
1379
+ probability TEXT NOT NULL DEFAULT 'medium' CHECK(probability IN ('low','medium','high')),
1380
+ owner TEXT,
1381
+ mitigation TEXT,
1382
+ due_at TEXT,
1383
+ project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
1384
+ plan_id TEXT REFERENCES plans(id) ON DELETE SET NULL,
1385
+ task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
1386
+ tags TEXT DEFAULT '[]',
1387
+ metadata TEXT DEFAULT '{}',
1388
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
1389
+ updated_at TEXT NOT NULL DEFAULT (datetime('now')),
1390
+ closed_at TEXT
1391
+ )`);
1392
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_risks_status ON project_risks(status)");
1393
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_risks_severity ON project_risks(severity)");
1394
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_risks_project ON project_risks(project_id)");
1395
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_risks_plan ON project_risks(plan_id)");
1396
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_risks_task ON project_risks(task_id)");
1397
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_risks_due ON project_risks(due_at)");
1398
+ ensureTable("local_retrospectives", `
1399
+ CREATE TABLE local_retrospectives (
1400
+ id TEXT PRIMARY KEY,
1401
+ title TEXT NOT NULL,
1402
+ scope TEXT NOT NULL CHECK(scope IN ('project','plan')),
1403
+ project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
1404
+ plan_id TEXT REFERENCES plans(id) ON DELETE SET NULL,
1405
+ agent_id TEXT,
1406
+ report_json TEXT NOT NULL,
1407
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
1408
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
1409
+ )`);
1410
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_local_retrospectives_project ON local_retrospectives(project_id)");
1411
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_local_retrospectives_plan ON local_retrospectives(plan_id)");
1412
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_local_retrospectives_agent ON local_retrospectives(agent_id)");
1184
1413
  ensureTable("task_relationships", `
1185
1414
  CREATE TABLE task_relationships (
1186
1415
  id TEXT PRIMARY KEY,
@@ -1482,6 +1711,10 @@ function ensureSchema(db) {
1482
1711
  ensureColumn("dispatches", "machine_id", "TEXT");
1483
1712
  ensureColumn("dispatches", "synced_at", "TEXT");
1484
1713
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_plan ON tasks(plan_id)");
1714
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_project ON tasks(project_id)");
1715
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status)");
1716
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_archived_at ON tasks(archived_at)");
1717
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_updated_at ON tasks(updated_at)");
1485
1718
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_task_list ON tasks(task_list_id)");
1486
1719
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_due_at ON tasks(due_at)");
1487
1720
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_short_id ON tasks(short_id) WHERE short_id IS NOT NULL");
@@ -1517,6 +1750,8 @@ function ensureSchema(db) {
1517
1750
  CREATE TABLE task_time_logs (
1518
1751
  id TEXT PRIMARY KEY,
1519
1752
  task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
1753
+ run_id TEXT,
1754
+ focus_session_id TEXT,
1520
1755
  agent_id TEXT,
1521
1756
  started_at TEXT,
1522
1757
  ended_at TEXT,
@@ -1524,9 +1759,77 @@ function ensureSchema(db) {
1524
1759
  notes TEXT,
1525
1760
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
1526
1761
  )`);
1762
+ ensureColumn("task_time_logs", "run_id", "TEXT");
1763
+ ensureColumn("task_time_logs", "focus_session_id", "TEXT");
1527
1764
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_time_logs_task ON task_time_logs(task_id)");
1528
1765
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_time_logs_agent ON task_time_logs(agent_id)");
1766
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_time_logs_run ON task_time_logs(run_id)");
1767
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_time_logs_focus_session ON task_time_logs(focus_session_id)");
1529
1768
  ensureColumn("tasks", "actual_minutes", "INTEGER");
1769
+ ensureTable("focus_sessions", `
1770
+ CREATE TABLE focus_sessions (
1771
+ id TEXT PRIMARY KEY,
1772
+ task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
1773
+ plan_id TEXT REFERENCES plans(id) ON DELETE SET NULL,
1774
+ run_id TEXT,
1775
+ agent_id TEXT,
1776
+ title TEXT,
1777
+ status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'paused', 'completed', 'cancelled')),
1778
+ started_at TEXT NOT NULL,
1779
+ last_resumed_at TEXT,
1780
+ paused_at TEXT,
1781
+ ended_at TEXT,
1782
+ actual_minutes INTEGER NOT NULL DEFAULT 0,
1783
+ idle_after_minutes INTEGER,
1784
+ notes TEXT,
1785
+ metadata TEXT DEFAULT '{}',
1786
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
1787
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
1788
+ )`);
1789
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_focus_sessions_task ON focus_sessions(task_id)");
1790
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_focus_sessions_plan ON focus_sessions(plan_id)");
1791
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_focus_sessions_run ON focus_sessions(run_id)");
1792
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_focus_sessions_agent ON focus_sessions(agent_id)");
1793
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_focus_sessions_status ON focus_sessions(status)");
1794
+ ensureTable("task_boards", `
1795
+ CREATE TABLE task_boards (
1796
+ id TEXT PRIMARY KEY,
1797
+ name TEXT NOT NULL UNIQUE,
1798
+ scope TEXT NOT NULL DEFAULT 'tasks' CHECK(scope IN ('tasks', 'plans')),
1799
+ project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
1800
+ task_list_id TEXT REFERENCES task_lists(id) ON DELETE SET NULL,
1801
+ plan_id TEXT REFERENCES plans(id) ON DELETE SET NULL,
1802
+ agent_id TEXT,
1803
+ lanes TEXT NOT NULL DEFAULT '[]',
1804
+ filters TEXT NOT NULL DEFAULT '{}',
1805
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
1806
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
1807
+ )`);
1808
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_boards_scope ON task_boards(scope)");
1809
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_boards_project ON task_boards(project_id)");
1810
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_boards_plan ON task_boards(plan_id)");
1811
+ ensureTable("local_calendar_items", `
1812
+ CREATE TABLE local_calendar_items (
1813
+ id TEXT PRIMARY KEY,
1814
+ kind TEXT NOT NULL CHECK(kind IN ('task_due', 'task_sla', 'task_reminder', 'milestone', 'work_block', 'run', 'imported')),
1815
+ title TEXT NOT NULL,
1816
+ description TEXT,
1817
+ starts_at TEXT NOT NULL,
1818
+ ends_at TEXT,
1819
+ timezone TEXT,
1820
+ project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
1821
+ task_id TEXT REFERENCES tasks(id) ON DELETE SET NULL,
1822
+ plan_id TEXT REFERENCES plans(id) ON DELETE SET NULL,
1823
+ run_id TEXT,
1824
+ recurrence_rule TEXT,
1825
+ metadata TEXT DEFAULT '{}',
1826
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
1827
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
1828
+ )`);
1829
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_local_calendar_items_time ON local_calendar_items(starts_at, ends_at)");
1830
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_local_calendar_items_task ON local_calendar_items(task_id)");
1831
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_local_calendar_items_project ON local_calendar_items(project_id)");
1832
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_local_calendar_items_kind ON local_calendar_items(kind)");
1530
1833
  ensureTable("task_watchers", `
1531
1834
  CREATE TABLE task_watchers (
1532
1835
  id TEXT PRIMARY KEY,
@@ -1606,12 +1909,22 @@ var init_schema = __esm(() => {
1606
1909
  });
1607
1910
 
1608
1911
  // src/db/machines.ts
1609
- import { hostname as osHostname, platform as osPlatform } from "os";
1912
+ import { hostname as osHostname, platform as osPlatform, arch as osArch } from "os";
1913
+ function parseMetadata(value) {
1914
+ if (!value)
1915
+ return {};
1916
+ try {
1917
+ const parsed = JSON.parse(value);
1918
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
1919
+ } catch {
1920
+ return {};
1921
+ }
1922
+ }
1610
1923
  function rowToMachine(row) {
1611
1924
  return {
1612
1925
  ...row,
1613
1926
  is_primary: !!row.is_primary,
1614
- metadata: row.metadata ? JSON.parse(row.metadata) : {}
1927
+ metadata: parseMetadata(row.metadata)
1615
1928
  };
1616
1929
  }
1617
1930
  function getOrCreateLocalMachine(db) {
@@ -1830,7 +2143,7 @@ var LOCK_EXPIRY_MINUTES = 30, _db = null, ALLOWED_TABLES;
1830
2143
  var init_database = __esm(() => {
1831
2144
  init_schema();
1832
2145
  init_machines();
1833
- ALLOWED_TABLES = new Set(["tasks", "projects", "agents", "plans", "task_lists", "task_templates"]);
2146
+ ALLOWED_TABLES = new Set(["tasks", "projects", "agents", "plans", "task_lists", "task_templates", "project_knowledge_records", "project_risks", "local_retrospectives"]);
1834
2147
  });
1835
2148
 
1836
2149
  // src/lib/recurrence.ts
@@ -2622,8 +2935,56 @@ import { dirname as dirname4, resolve as resolve4 } from "path";
2622
2935
  import { createConnection } from "net";
2623
2936
 
2624
2937
  // src/lib/redaction.ts
2938
+ var DEFAULT_SECRET_PATTERNS = [
2939
+ { name: "aws-access-key", regex: /\b(AKIA|ASIA)[0-9A-Z]{16}\b/g, replacement: "[REDACTED_AWS_KEY]" },
2940
+ { name: "private-key", regex: /-----BEGIN (?:RSA |EC |OPENSSH |)PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH |)PRIVATE KEY-----/g, replacement: "[REDACTED_PRIVATE_KEY]" },
2941
+ { name: "openai-token", regex: /\bsk-[A-Za-z0-9_-]{12,}\b/g, replacement: "[REDACTED_TOKEN]" },
2942
+ { name: "env-secret-assignment", regex: /\b([A-Za-z0-9_]*(?:API_KEY|TOKEN|SECRET|PASSWORD)[A-Za-z0-9_]*)\s*=\s*['"]?[^'"\s]{8,}/gi, replacement: "$1=[REDACTED]" },
2943
+ { name: "bearer-token", regex: /\b(bearer)\s+[A-Za-z0-9._~+/=-]{12,}/gi, replacement: "$1 [REDACTED]" }
2944
+ ];
2945
+ var DEFAULT_SECRET_KEY_PATTERN = /api[_-]?key|token|secret|password/i;
2946
+ var NON_SECRET_USAGE_KEYS = new Set([
2947
+ "tokens",
2948
+ "total_tokens",
2949
+ "token_count",
2950
+ "input_tokens",
2951
+ "output_tokens",
2952
+ "prompt_tokens",
2953
+ "completion_tokens"
2954
+ ]);
2955
+ function unique(values) {
2956
+ return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
2957
+ }
2958
+ function cloneRegex(regex) {
2959
+ return new RegExp(regex.source, regex.flags.includes("g") ? regex.flags : `${regex.flags}g`);
2960
+ }
2961
+ function customPatterns() {
2962
+ return unique(loadConfig().secret_safety?.redaction_patterns).flatMap((pattern) => {
2963
+ try {
2964
+ return [{ name: `custom:${pattern}`, regex: new RegExp(pattern, "g") }];
2965
+ } catch {
2966
+ return [];
2967
+ }
2968
+ });
2969
+ }
2970
+ function secretPatterns() {
2971
+ return [...customPatterns(), ...DEFAULT_SECRET_PATTERNS];
2972
+ }
2973
+ function isSecretKey(key) {
2974
+ if (NON_SECRET_USAGE_KEYS.has(key.toLowerCase()))
2975
+ return false;
2976
+ if (DEFAULT_SECRET_KEY_PATTERN.test(key))
2977
+ return true;
2978
+ return unique(loadConfig().secret_safety?.redaction_keys).some((pattern) => key.toLowerCase().includes(pattern.toLowerCase()));
2979
+ }
2625
2980
  function redactEvidenceText(value) {
2626
- return value.replace(/\b(AKIA|ASIA)[0-9A-Z]{16}\b/g, "[REDACTED_AWS_KEY]").replace(/-----BEGIN (?:RSA |EC |OPENSSH |)PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH |)PRIVATE KEY-----/g, "[REDACTED_PRIVATE_KEY]").replace(/\bsk-[A-Za-z0-9_-]{12,}\b/g, "[REDACTED_TOKEN]").replace(/\b([A-Za-z0-9_]*(?:API_KEY|TOKEN|SECRET|PASSWORD)[A-Za-z0-9_]*)\s*=\s*['"]?[^'"\s]{8,}/gi, "$1=[REDACTED]").replace(/\b(bearer)\s+[A-Za-z0-9._~+/=-]{12,}/gi, "$1 [REDACTED]");
2981
+ let redacted = value;
2982
+ for (const pattern of secretPatterns()) {
2983
+ const regex = cloneRegex(pattern.regex);
2984
+ const replacement = pattern.replacement ?? "[REDACTED]";
2985
+ redacted = typeof replacement === "string" ? redacted.replace(regex, replacement) : redacted.replace(regex, replacement);
2986
+ }
2987
+ return redacted;
2627
2988
  }
2628
2989
  function redactValue(value) {
2629
2990
  if (typeof value === "string")
@@ -2633,7 +2994,7 @@ function redactValue(value) {
2633
2994
  if (value && typeof value === "object") {
2634
2995
  const redacted = {};
2635
2996
  for (const [key, child] of Object.entries(value)) {
2636
- if (/api[_-]?key|token|secret|password/i.test(key)) {
2997
+ if (isSecretKey(key)) {
2637
2998
  redacted[key] = "[REDACTED]";
2638
2999
  } else {
2639
3000
  redacted[key] = redactValue(child);
@@ -2696,7 +3057,7 @@ var PRESET_DEFAULTS = {
2696
3057
  function normalizePath(path) {
2697
3058
  return resolve2(path);
2698
3059
  }
2699
- function unique(values) {
3060
+ function unique2(values) {
2700
3061
  return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
2701
3062
  }
2702
3063
  function defaultProfile(root, preset) {
@@ -2749,7 +3110,7 @@ function writeAllowed(profile, root, writePath) {
2749
3110
  function redactedEnvKeys(profile, env) {
2750
3111
  if (!env)
2751
3112
  return [];
2752
- const patterns = unique([...DEFAULT_ENV_REDACTIONS, ...profile.env_redactions]).map((item) => item.toUpperCase());
3113
+ const patterns = unique2([...DEFAULT_ENV_REDACTIONS, ...profile.env_redactions]).map((item) => item.toUpperCase());
2753
3114
  return Object.keys(env).filter((key) => patterns.some((pattern) => key.toUpperCase().includes(pattern)));
2754
3115
  }
2755
3116
  function checkWorkspacePermission(input = {}) {
@@ -2788,7 +3149,7 @@ var DEFAULT_ENV_REDACTIONS2 = ["API_KEY", "TOKEN", "SECRET", "PASSWORD", "AUTH"]
2788
3149
  function normalizePath2(path) {
2789
3150
  return resolve3(path);
2790
3151
  }
2791
- function unique2(values) {
3152
+ function unique3(values) {
2792
3153
  return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
2793
3154
  }
2794
3155
  function configuredProfiles2(config = loadConfig()) {
@@ -2844,7 +3205,7 @@ function profileByName(name, path) {
2844
3205
  function redactedEnvKeys2(profile, env) {
2845
3206
  if (!env)
2846
3207
  return [];
2847
- const patterns = unique2([...DEFAULT_ENV_REDACTIONS2, ...profile.env_redactions]).map((item) => item.toUpperCase());
3208
+ const patterns = unique3([...DEFAULT_ENV_REDACTIONS2, ...profile.env_redactions]).map((item) => item.toUpperCase());
2848
3209
  return Object.keys(env).filter((key) => patterns.some((pattern) => key.toUpperCase().includes(pattern)));
2849
3210
  }
2850
3211
  function omittedEnvKeys(profile, env) {
@@ -2896,7 +3257,7 @@ function checkRunnerSandbox(input = {}) {
2896
3257
  const redacted = redactedEnvKeys2(profile, input.env);
2897
3258
  const omitted = omittedEnvKeys(profile, input.env);
2898
3259
  const effective = Object.keys(input.env || {}).filter((key) => !omitted.includes(key));
2899
- const uniqueReasons = unique2(reasons);
3260
+ const uniqueReasons = unique3(reasons);
2900
3261
  const allowed = uniqueReasons.length === 0;
2901
3262
  return {
2902
3263
  allowed,
@@ -3333,8 +3694,8 @@ function createTask(input, db) {
3333
3694
  let id = uuid();
3334
3695
  for (let attempt = 0;attempt < 3; attempt++) {
3335
3696
  try {
3336
- 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, 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)
3337
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
3697
+ 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)
3698
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
3338
3699
  id,
3339
3700
  null,
3340
3701
  input.project_id || null,
@@ -3356,6 +3717,7 @@ function createTask(input, db) {
3356
3717
  timestamp,
3357
3718
  input.due_at || null,
3358
3719
  input.estimated_minutes || null,
3720
+ input.sla_minutes ?? null,
3359
3721
  input.confidence ?? null,
3360
3722
  input.retry_count ?? 0,
3361
3723
  input.max_retries ?? 3,
@@ -3635,6 +3997,10 @@ function updateTask(id, input, db) {
3635
3997
  sets.push("estimated_minutes = ?");
3636
3998
  params.push(input.estimated_minutes);
3637
3999
  }
4000
+ if (input.sla_minutes !== undefined) {
4001
+ sets.push("sla_minutes = ?");
4002
+ params.push(input.sla_minutes);
4003
+ }
3638
4004
  if (input.actual_minutes !== undefined) {
3639
4005
  sets.push("actual_minutes = ?");
3640
4006
  params.push(input.actual_minutes);
@@ -3716,6 +4082,7 @@ function updateTask(id, input, db) {
3716
4082
  version: task.version + 1,
3717
4083
  updated_at: timestamp,
3718
4084
  completed_at: input.status === "completed" ? completionTimestamp : input.completed_at !== undefined ? input.completed_at : task.completed_at,
4085
+ sla_minutes: input.sla_minutes !== undefined ? input.sla_minutes : task.sla_minutes,
3719
4086
  actual_minutes: input.actual_minutes ?? task.actual_minutes,
3720
4087
  confidence: input.confidence !== undefined ? input.confidence : task.confidence,
3721
4088
  retry_count: input.retry_count ?? task.retry_count,
@@ -3957,7 +4324,7 @@ function completeTask(id, agentId, db, options) {
3957
4324
  emitLocalEventHooksQuiet({ type: "task.completed", payload: { id, agent_id: agentId, title: task.title, completed_at: timestamp } });
3958
4325
  let spawnedTask = null;
3959
4326
  if (task.recurrence_rule && !options?.skip_recurrence) {
3960
- spawnedTask = spawnNextRecurrence(task, d);
4327
+ spawnedTask = spawnNextRecurrence(task, d, timestamp);
3961
4328
  }
3962
4329
  let spawnedFromTemplate = null;
3963
4330
  if (task.spawns_template_id) {
@@ -4176,8 +4543,9 @@ function getStaleTasks(staleQuery = 30, filters, db) {
4176
4543
  const rows = d.query(`SELECT * FROM tasks WHERE ${where} ORDER BY updated_at ASC`).all(...params);
4177
4544
  return rows.map(rowToTask);
4178
4545
  }
4179
- function spawnNextRecurrence(completedTask, db) {
4180
- const dueAt = nextOccurrence(completedTask.recurrence_rule, new Date);
4546
+ function spawnNextRecurrence(completedTask, db, completedAt) {
4547
+ const recurrenceBase = completedTask.due_at ? new Date(completedTask.due_at) : new Date(completedAt);
4548
+ const dueAt = nextOccurrence(completedTask.recurrence_rule, recurrenceBase);
4181
4549
  let title = completedTask.title;
4182
4550
  if (completedTask.short_id && title.startsWith(completedTask.short_id + ": ")) {
4183
4551
  title = title.slice(completedTask.short_id.length + 2);
@@ -4194,6 +4562,7 @@ function spawnNextRecurrence(completedTask, db) {
4194
4562
  tags: completedTask.tags,
4195
4563
  metadata: completedTask.metadata,
4196
4564
  estimated_minutes: completedTask.estimated_minutes ?? undefined,
4565
+ sla_minutes: completedTask.sla_minutes ?? undefined,
4197
4566
  recurrence_rule: completedTask.recurrence_rule,
4198
4567
  recurrence_parent_id: recurrenceParentId,
4199
4568
  due_at: dueAt
@@ -4285,6 +4654,130 @@ function getTaskStats(filters, db) {
4285
4654
  }
4286
4655
  // src/db/task-relations.ts
4287
4656
  init_database();
4657
+ // src/db/boards.ts
4658
+ init_database();
4659
+
4660
+ // src/db/plans.ts
4661
+ init_database();
4662
+ function createPlan(input, db) {
4663
+ const d = db || getDatabase();
4664
+ const id = uuid();
4665
+ const timestamp = now();
4666
+ d.run(`INSERT INTO plans (id, project_id, task_list_id, agent_id, name, description, status, created_at, updated_at)
4667
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
4668
+ id,
4669
+ input.project_id || null,
4670
+ input.task_list_id || null,
4671
+ input.agent_id || null,
4672
+ input.name,
4673
+ input.description || null,
4674
+ input.status || "active",
4675
+ timestamp,
4676
+ timestamp
4677
+ ]);
4678
+ return getPlan(id, d);
4679
+ }
4680
+ function getPlan(id, db) {
4681
+ const d = db || getDatabase();
4682
+ const row = d.query("SELECT * FROM plans WHERE id = ?").get(id);
4683
+ return row;
4684
+ }
4685
+ function listPlans(projectId, db) {
4686
+ const d = db || getDatabase();
4687
+ if (projectId) {
4688
+ return d.query("SELECT * FROM plans WHERE project_id = ? ORDER BY created_at DESC").all(projectId);
4689
+ }
4690
+ return d.query("SELECT * FROM plans ORDER BY created_at DESC").all();
4691
+ }
4692
+ function updatePlan(id, input, db) {
4693
+ const d = db || getDatabase();
4694
+ const plan = getPlan(id, d);
4695
+ if (!plan)
4696
+ throw new PlanNotFoundError(id);
4697
+ const sets = ["updated_at = ?"];
4698
+ const params = [now()];
4699
+ if (input.name !== undefined) {
4700
+ sets.push("name = ?");
4701
+ params.push(input.name);
4702
+ }
4703
+ if (input.description !== undefined) {
4704
+ sets.push("description = ?");
4705
+ params.push(input.description);
4706
+ }
4707
+ if (input.status !== undefined) {
4708
+ sets.push("status = ?");
4709
+ params.push(input.status);
4710
+ }
4711
+ if (input.task_list_id !== undefined) {
4712
+ sets.push("task_list_id = ?");
4713
+ params.push(input.task_list_id);
4714
+ }
4715
+ if (input.agent_id !== undefined) {
4716
+ sets.push("agent_id = ?");
4717
+ params.push(input.agent_id);
4718
+ }
4719
+ params.push(id);
4720
+ d.run(`UPDATE plans SET ${sets.join(", ")} WHERE id = ?`, params);
4721
+ const updated = getPlan(id, d);
4722
+ emitLocalEventHooksQuiet({
4723
+ type: "plan.updated",
4724
+ payload: { id, old_status: plan.status, new_status: updated.status, name: updated.name, project_id: updated.project_id }
4725
+ });
4726
+ return updated;
4727
+ }
4728
+ function deletePlan(id, db) {
4729
+ const d = db || getDatabase();
4730
+ const result = d.run("DELETE FROM plans WHERE id = ?", [id]);
4731
+ return result.changes > 0;
4732
+ }
4733
+ // src/db/calendar.ts
4734
+ init_database();
4735
+
4736
+ // src/lib/artifact-store.ts
4737
+ init_database();
4738
+
4739
+ // src/db/comments.ts
4740
+ init_database();
4741
+ function addComment(input, db) {
4742
+ const d = db || getDatabase();
4743
+ if (!getTask(input.task_id, d)) {
4744
+ throw new TaskNotFoundError(input.task_id);
4745
+ }
4746
+ const id = uuid();
4747
+ const timestamp = now();
4748
+ d.run(`INSERT INTO task_comments (id, task_id, agent_id, session_id, content, type, progress_pct, created_at)
4749
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
4750
+ id,
4751
+ input.task_id,
4752
+ input.agent_id || null,
4753
+ input.session_id || null,
4754
+ redactEvidenceText(input.content),
4755
+ input.type || "comment",
4756
+ input.progress_pct ?? null,
4757
+ timestamp
4758
+ ]);
4759
+ return getComment(id, d);
4760
+ }
4761
+ function logProgress(taskId, message, pct, agentId, db) {
4762
+ return addComment({ task_id: taskId, content: message, type: "progress", progress_pct: pct, agent_id: agentId }, db);
4763
+ }
4764
+ function getComment(id, db) {
4765
+ const d = db || getDatabase();
4766
+ return d.query("SELECT * FROM task_comments WHERE id = ?").get(id);
4767
+ }
4768
+ function listComments(taskId, db) {
4769
+ const d = db || getDatabase();
4770
+ return d.query("SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at").all(taskId);
4771
+ }
4772
+
4773
+ // src/db/task-runs.ts
4774
+ init_database();
4775
+
4776
+ // src/db/task-files.ts
4777
+ init_database();
4778
+
4779
+ // src/db/task-commits.ts
4780
+ init_database();
4288
4781
  // src/db/agents.ts
4289
4782
  init_database();
4290
4783
 
@@ -4360,10 +4853,63 @@ var NICE_AGENT_NAMES = [
4360
4853
  "vesper",
4361
4854
  "zephyr"
4362
4855
  ];
4856
+ var EXTENDED_AGENT_NAMES = [
4857
+ "agrippa",
4858
+ "antonius",
4859
+ "aurelian",
4860
+ "aurelius",
4861
+ "camillus",
4862
+ "cassius",
4863
+ "celer",
4864
+ "cincinnatus",
4865
+ "corvus",
4866
+ "drusus",
4867
+ "fabius",
4868
+ "faustus",
4869
+ "flaccus",
4870
+ "gallus",
4871
+ "gaius",
4872
+ "horatius",
4873
+ "lucius",
4874
+ "lucullus",
4875
+ "marius",
4876
+ "marcellus",
4877
+ "maximus",
4878
+ "nerva",
4879
+ "pompey",
4880
+ "quintus",
4881
+ "regulus",
4882
+ "romulus",
4883
+ "scipio",
4884
+ "seneca",
4885
+ "sertorius",
4886
+ "sulla",
4887
+ "tacitus",
4888
+ "varro",
4889
+ "vitruvius",
4890
+ "plato",
4891
+ "socrates",
4892
+ "aristotle",
4893
+ "heraclitus",
4894
+ "democritus",
4895
+ "pythagoras",
4896
+ "hipparchus",
4897
+ "euclid",
4898
+ "archimedes",
4899
+ "zeno",
4900
+ "anaximander",
4901
+ "epictetus",
4902
+ "aeschylus",
4903
+ "sophocles",
4904
+ "euripides",
4905
+ "xenophon",
4906
+ "diogenes"
4907
+ ];
4363
4908
  var PREFERRED_AGENT_NAMES = [
4364
4909
  ...ROMAN_AGENT_NAMES,
4365
4910
  ...GREEK_AGENT_NAMES,
4366
- ...NICE_AGENT_NAMES
4911
+ ...NICE_AGENT_NAMES,
4912
+ ...EXTENDED_AGENT_NAMES
4367
4913
  ];
4368
4914
  var RESERVED_GENERIC_NAMES = new Set([
4369
4915
  "agent",
@@ -4401,29 +4947,93 @@ function isGenericAgentName(name) {
4401
4947
  }
4402
4948
  return false;
4403
4949
  }
4404
- function alphabeticSuffix(index) {
4405
- const letters = "abcdefghijklmnopqrstuvwxyz";
4406
- let value = index;
4407
- let suffix = "";
4408
- do {
4409
- suffix = letters[value % letters.length] + suffix;
4410
- value = Math.floor(value / letters.length) - 1;
4411
- } while (value >= 0);
4412
- return suffix;
4950
+ var FALLBACK_PREFIXES = [
4951
+ "arv",
4952
+ "bel",
4953
+ "cyr",
4954
+ "dax",
4955
+ "elun",
4956
+ "feno",
4957
+ "gavor",
4958
+ "hiro",
4959
+ "ivar",
4960
+ "jaro",
4961
+ "kavo",
4962
+ "lumo",
4963
+ "myr",
4964
+ "navo",
4965
+ "prax",
4966
+ "quor",
4967
+ "riven",
4968
+ "sovan",
4969
+ "tavor",
4970
+ "ulmor",
4971
+ "vexo",
4972
+ "wiro",
4973
+ "yaro",
4974
+ "zel"
4975
+ ];
4976
+ var FALLBACK_STEMS = [
4977
+ "al",
4978
+ "ber",
4979
+ "cor",
4980
+ "dren",
4981
+ "el",
4982
+ "far",
4983
+ "gor",
4984
+ "hal",
4985
+ "ion",
4986
+ "jor",
4987
+ "kel",
4988
+ "lor",
4989
+ "mor",
4990
+ "nel",
4991
+ "or",
4992
+ "per",
4993
+ "quil",
4994
+ "ron",
4995
+ "ser",
4996
+ "tor",
4997
+ "um",
4998
+ "ver",
4999
+ "wyn",
5000
+ "xil"
5001
+ ];
5002
+ var FALLBACK_ENDINGS = [
5003
+ "a",
5004
+ "en",
5005
+ "ia",
5006
+ "is",
5007
+ "on",
5008
+ "or",
5009
+ "um",
5010
+ "us",
5011
+ "yn",
5012
+ "ar",
5013
+ "el",
5014
+ "ir"
5015
+ ];
5016
+ function generatedFallbackAgentName(index) {
5017
+ const perPrefix = FALLBACK_STEMS.length * FALLBACK_ENDINGS.length;
5018
+ const total = FALLBACK_PREFIXES.length * perPrefix;
5019
+ if (index >= total)
5020
+ return null;
5021
+ const prefix = FALLBACK_PREFIXES[Math.floor(index / perPrefix)];
5022
+ const rest = index % perPrefix;
5023
+ const stem = FALLBACK_STEMS[Math.floor(rest / FALLBACK_ENDINGS.length)];
5024
+ const ending = FALLBACK_ENDINGS[rest % FALLBACK_ENDINGS.length];
5025
+ return `${prefix}${stem}${ending}`;
4413
5026
  }
4414
5027
  function suggestAgentNames(existingNames = []) {
4415
5028
  const existing = new Set([...existingNames].map(normalizeAgentNameInput));
4416
5029
  const suggestions = PREFERRED_AGENT_NAMES.filter((name) => !existing.has(name));
4417
- for (let suffixIndex = 0;suggestions.length < 20 && suffixIndex < 1000; suffixIndex++) {
4418
- const suffix = alphabeticSuffix(suffixIndex);
4419
- for (const base of PREFERRED_AGENT_NAMES) {
4420
- const candidate = `${base}${suffix}`;
4421
- if (existing.has(candidate) || suggestions.includes(candidate))
4422
- continue;
4423
- suggestions.push(candidate);
4424
- if (suggestions.length >= 20)
4425
- break;
4426
- }
5030
+ for (let index = 0;suggestions.length < 20; index++) {
5031
+ const candidate = generatedFallbackAgentName(index);
5032
+ if (!candidate)
5033
+ break;
5034
+ if (existing.has(candidate) || suggestions.includes(candidate))
5035
+ continue;
5036
+ suggestions.push(candidate);
4427
5037
  }
4428
5038
  return suggestions;
4429
5039
  }
@@ -4686,80 +5296,6 @@ function getOrgChart(db) {
4686
5296
  return buildTree(null);
4687
5297
  }
4688
5298
 
4689
- // src/db/plans.ts
4690
- init_database();
4691
- function createPlan(input, db) {
4692
- const d = db || getDatabase();
4693
- const id = uuid();
4694
- const timestamp = now();
4695
- d.run(`INSERT INTO plans (id, project_id, task_list_id, agent_id, name, description, status, created_at, updated_at)
4696
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
4697
- id,
4698
- input.project_id || null,
4699
- input.task_list_id || null,
4700
- input.agent_id || null,
4701
- input.name,
4702
- input.description || null,
4703
- input.status || "active",
4704
- timestamp,
4705
- timestamp
4706
- ]);
4707
- return getPlan(id, d);
4708
- }
4709
- function getPlan(id, db) {
4710
- const d = db || getDatabase();
4711
- const row = d.query("SELECT * FROM plans WHERE id = ?").get(id);
4712
- return row;
4713
- }
4714
- function listPlans(projectId, db) {
4715
- const d = db || getDatabase();
4716
- if (projectId) {
4717
- return d.query("SELECT * FROM plans WHERE project_id = ? ORDER BY created_at DESC").all(projectId);
4718
- }
4719
- return d.query("SELECT * FROM plans ORDER BY created_at DESC").all();
4720
- }
4721
- function updatePlan(id, input, db) {
4722
- const d = db || getDatabase();
4723
- const plan = getPlan(id, d);
4724
- if (!plan)
4725
- throw new PlanNotFoundError(id);
4726
- const sets = ["updated_at = ?"];
4727
- const params = [now()];
4728
- if (input.name !== undefined) {
4729
- sets.push("name = ?");
4730
- params.push(input.name);
4731
- }
4732
- if (input.description !== undefined) {
4733
- sets.push("description = ?");
4734
- params.push(input.description);
4735
- }
4736
- if (input.status !== undefined) {
4737
- sets.push("status = ?");
4738
- params.push(input.status);
4739
- }
4740
- if (input.task_list_id !== undefined) {
4741
- sets.push("task_list_id = ?");
4742
- params.push(input.task_list_id);
4743
- }
4744
- if (input.agent_id !== undefined) {
4745
- sets.push("agent_id = ?");
4746
- params.push(input.agent_id);
4747
- }
4748
- params.push(id);
4749
- d.run(`UPDATE plans SET ${sets.join(", ")} WHERE id = ?`, params);
4750
- const updated = getPlan(id, d);
4751
- emitLocalEventHooksQuiet({
4752
- type: "plan.updated",
4753
- payload: { id, old_status: plan.status, new_status: updated.status, name: updated.name, project_id: updated.project_id }
4754
- });
4755
- return updated;
4756
- }
4757
- function deletePlan(id, db) {
4758
- const d = db || getDatabase();
4759
- const result = d.run("DELETE FROM plans WHERE id = ?", [id]);
4760
- return result.changes > 0;
4761
- }
4762
-
4763
5299
  // src/db/orgs.ts
4764
5300
  init_database();
4765
5301
  function rowToOrg(row) {
@@ -4809,40 +5345,6 @@ function deleteOrg(id, db) {
4809
5345
  return d.run("DELETE FROM orgs WHERE id = ?", [id]).changes > 0;
4810
5346
  }
4811
5347
 
4812
- // src/db/comments.ts
4813
- init_database();
4814
- function addComment(input, db) {
4815
- const d = db || getDatabase();
4816
- if (!getTask(input.task_id, d)) {
4817
- throw new TaskNotFoundError(input.task_id);
4818
- }
4819
- const id = uuid();
4820
- const timestamp = now();
4821
- d.run(`INSERT INTO task_comments (id, task_id, agent_id, session_id, content, type, progress_pct, created_at)
4822
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
4823
- id,
4824
- input.task_id,
4825
- input.agent_id || null,
4826
- input.session_id || null,
4827
- input.content,
4828
- input.type || "comment",
4829
- input.progress_pct ?? null,
4830
- timestamp
4831
- ]);
4832
- return getComment(id, d);
4833
- }
4834
- function logProgress(taskId, message, pct, agentId, db) {
4835
- return addComment({ task_id: taskId, content: message, type: "progress", progress_pct: pct, agent_id: agentId }, db);
4836
- }
4837
- function getComment(id, db) {
4838
- const d = db || getDatabase();
4839
- return d.query("SELECT * FROM task_comments WHERE id = ?").get(id);
4840
- }
4841
- function listComments(taskId, db) {
4842
- const d = db || getDatabase();
4843
- return d.query("SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at").all(taskId);
4844
- }
4845
-
4846
5348
  // src/server/routes.ts
4847
5349
  import { join as join5, resolve as resolve5, sep } from "path";
4848
5350
  function parseFieldsParam(url) {