@hasna/todos 0.15.41 → 0.15.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.
@@ -8787,7 +8787,7 @@ function getTaskGraph(taskId, direction = "both", db) {
8787
8787
  const deps = getTaskDependencies(t.id, d);
8788
8788
  const hasUnfinishedDeps = deps.some((dep) => {
8789
8789
  const depTask = getTask(dep.depends_on, d);
8790
- return depTask && depTask.status !== "completed";
8790
+ return depTask && isBlockingDependencyStatus(depTask.status);
8791
8791
  });
8792
8792
  return { id: t.id, short_id: t.short_id, title: t.title, status: t.status, priority: t.priority, is_blocked: hasUnfinishedDeps };
8793
8793
  }
@@ -8911,7 +8911,7 @@ function getBlockingDeps(id, db) {
8911
8911
  const blocking = [];
8912
8912
  for (const dep of deps) {
8913
8913
  const task = getTask(dep.depends_on, d);
8914
- if (task && task.status !== "completed")
8914
+ if (task && isBlockingDependencyStatus(task.status))
8915
8915
  blocking.push(task);
8916
8916
  }
8917
8917
  return blocking;
@@ -9235,7 +9235,7 @@ function getNextTask2(agentId, filters, db) {
9235
9235
  conditions.push(`id IN (SELECT task_id FROM task_tags WHERE tag IN (${placeholders}))`);
9236
9236
  params.push(...filters.tags);
9237
9237
  }
9238
- conditions.push("id NOT IN (SELECT td.task_id FROM task_dependencies td JOIN tasks dep ON dep.id = td.depends_on WHERE dep.status != 'completed')");
9238
+ conditions.push("id NOT IN (SELECT td.task_id FROM task_dependencies td JOIN tasks dep ON dep.id = td.depends_on WHERE dep.status NOT IN ('completed', 'cancelled'))");
9239
9239
  const where = conditions.join(" AND ");
9240
9240
  let recentProjectIds = [];
9241
9241
  const assignedAliasParams = [];
@@ -9279,7 +9279,7 @@ function getActiveWork2(filters, db) {
9279
9279
  }
9280
9280
  function getTasksChangedSince(since, filters, db) {
9281
9281
  const d = db || getDatabase();
9282
- const conditions = ["updated_at > ?"];
9282
+ const conditions = ["(julianday(updated_at) IS NULL OR julianday(updated_at) > julianday(?))"];
9283
9283
  const params = [since];
9284
9284
  if (filters?.project_id) {
9285
9285
  conditions.push("project_id = ?");
@@ -12812,7 +12812,7 @@ import { createHash as createHash7 } from "crypto";
12812
12812
  // package.json
12813
12813
  var package_default = {
12814
12814
  name: "@hasna/todos",
12815
- version: "0.15.41",
12815
+ version: "0.15.46",
12816
12816
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
12817
12817
  type: "module",
12818
12818
  main: "dist/index.js",
@@ -12930,7 +12930,7 @@ var package_default = {
12930
12930
  author: "Andrei Hasna <andrei@hasna.com>",
12931
12931
  license: "Apache-2.0",
12932
12932
  dependencies: {
12933
- "@hasna/contracts": "0.13.3",
12933
+ "@hasna/contracts": "0.13.4",
12934
12934
  "@hasna/events": "^0.1.11",
12935
12935
  "@modelcontextprotocol/sdk": "^1.12.1",
12936
12936
  chalk: "^5.4.1",
@@ -12968,6 +12968,57 @@ function normalizeAgentNameInput(name) {
12968
12968
  // src/storage/postgres-adapter.ts
12969
12969
  init_creator_identity();
12970
12970
 
12971
+ // src/lib/instant-compare.ts
12972
+ var SQLITE_STAMP = /^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?(?:Z|([+-])(\d{2}):(\d{2}))?)?$/;
12973
+ var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
12974
+ function isLeapYear(year) {
12975
+ return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
12976
+ }
12977
+ function sqliteJulianDay(value) {
12978
+ const m = SQLITE_STAMP.exec(value);
12979
+ if (!m)
12980
+ return null;
12981
+ const year = Number(m[1]);
12982
+ const month = Number(m[2]);
12983
+ const day = Number(m[3]);
12984
+ const hour = m[4] === undefined ? 0 : Number(m[4]);
12985
+ const minute = m[5] === undefined ? 0 : Number(m[5]);
12986
+ const second = m[6] === undefined ? 0 : Number(m[6]);
12987
+ const frac = m[7];
12988
+ const sign = m[8];
12989
+ const offsetHour = m[9];
12990
+ const offsetMinute = m[10];
12991
+ if (month < 1 || month > 12)
12992
+ return null;
12993
+ if (hour > 23 || minute > 59 || second > 59)
12994
+ return null;
12995
+ const maxDay = (DAYS_IN_MONTH[month - 1] ?? 0) + (month === 2 && isLeapYear(year) ? 1 : 0);
12996
+ if (day < 1 || day > maxDay)
12997
+ return null;
12998
+ let offsetMinutes = 0;
12999
+ if (sign !== undefined) {
13000
+ const oh = Number(offsetHour ?? "0");
13001
+ const om = Number(offsetMinute ?? "0");
13002
+ if (oh > 23 || om > 59)
13003
+ return null;
13004
+ offsetMinutes = oh * 60 + om;
13005
+ if (sign === "-")
13006
+ offsetMinutes = -offsetMinutes;
13007
+ }
13008
+ const micros = frac === undefined ? 0 : Number((frac + "000000").slice(0, 6));
13009
+ const ms = Date.UTC(year, month - 1, day, hour, minute, second);
13010
+ return (ms + micros / 1000) / 86400000 + 2440587.5 - offsetMinutes / 1440;
13011
+ }
13012
+ function changedSinceStampNewer(stamp, since) {
13013
+ const stampJd = sqliteJulianDay(stamp);
13014
+ if (stampJd === null)
13015
+ return true;
13016
+ const sinceJd = sqliteJulianDay(since);
13017
+ if (sinceJd === null)
13018
+ return false;
13019
+ return stampJd > sinceJd;
13020
+ }
13021
+
12971
13022
  // src/lib/plan-project-link-contract.ts
12972
13023
  init_types();
12973
13024
  import { createHash as createHash2 } from "crypto";
@@ -13904,11 +13955,16 @@ class PostgresJsonRecordStore {
13904
13955
  return context?.requestId ?? this.sourceMachineId ?? null;
13905
13956
  }
13906
13957
  async ensureSchema() {
13907
- this.schemaReady ??= (async () => {
13908
- for (const sql of postgresTodosSyncSchemaSql(this.tableName, this.cursorTableName)) {
13909
- await this.options.client.query(sql);
13910
- }
13911
- })();
13958
+ if (!this.schemaReady) {
13959
+ this.schemaReady = (async () => {
13960
+ for (const sql of postgresTodosSyncSchemaSql(this.tableName, this.cursorTableName)) {
13961
+ await this.options.client.query(sql);
13962
+ }
13963
+ })().catch((error) => {
13964
+ this.schemaReady = null;
13965
+ throw error;
13966
+ });
13967
+ }
13912
13968
  await this.schemaReady;
13913
13969
  }
13914
13970
  async get(type, id) {
@@ -15673,7 +15729,7 @@ async function getActiveWork(filters, store) {
15673
15729
  }));
15674
15730
  }
15675
15731
  async function getChangedSince(since, filters, store) {
15676
- return (await listTasks(filters ?? {}, store)).filter((task) => task.updated_at > since);
15732
+ return (await listTasks(filters ?? {}, store)).filter((task) => changedSinceStampNewer(task.updated_at ?? "", since));
15677
15733
  }
15678
15734
  async function createProject(input, store, context) {
15679
15735
  const timestamp = new Date().toISOString();
@@ -16604,14 +16660,23 @@ class PostgresTodosProjectRegistrationBackend {
16604
16660
  this.cursorTableName = safeIdentifier(options.cursorTableName ?? "todos_sync_cursors", "cursorTableName");
16605
16661
  }
16606
16662
  async ensureSchema() {
16607
- this.schemaReady ??= (async () => {
16608
- for (const statement of postgresTodosSyncSchemaSql(this.tableName, this.cursorTableName)) {
16609
- await this.client.query(statement);
16610
- }
16611
- for (const statement of postgresTodosProjectRegistrationSchemaSql()) {
16612
- await this.client.query(statement);
16663
+ if (this.schemaReady === null) {
16664
+ const attempt = (async () => {
16665
+ for (const statement of postgresTodosSyncSchemaSql(this.tableName, this.cursorTableName)) {
16666
+ await this.client.query(statement);
16667
+ }
16668
+ for (const statement of postgresTodosProjectRegistrationSchemaSql()) {
16669
+ await this.client.query(statement);
16670
+ }
16671
+ })();
16672
+ this.schemaReady = attempt;
16673
+ try {
16674
+ await attempt;
16675
+ } catch (error) {
16676
+ this.schemaReady = null;
16677
+ throw error;
16613
16678
  }
16614
- })();
16679
+ }
16615
16680
  await this.schemaReady;
16616
16681
  }
16617
16682
  async transaction(fn) {
@@ -18293,7 +18358,7 @@ function matchesExtraFilters(task, filter) {
18293
18358
  }
18294
18359
  if (filter.tags?.length) {
18295
18360
  const taskTags = new Set(task.tags ?? []);
18296
- if (!filter.tags.every((tag) => taskTags.has(tag)))
18361
+ if (!filter.tags.some((tag) => taskTags.has(tag)))
18297
18362
  return false;
18298
18363
  }
18299
18364
  if (filter.include_subtasks !== true && filter.parent_id === undefined && task.parent_id)
package/dist/registry.js CHANGED
@@ -8651,7 +8651,7 @@ function getTaskGraph(taskId, direction = "both", db) {
8651
8651
  const deps = getTaskDependencies(t.id, d);
8652
8652
  const hasUnfinishedDeps = deps.some((dep) => {
8653
8653
  const depTask = getTask(dep.depends_on, d);
8654
- return depTask && depTask.status !== "completed";
8654
+ return depTask && isBlockingDependencyStatus(depTask.status);
8655
8655
  });
8656
8656
  return { id: t.id, short_id: t.short_id, title: t.title, status: t.status, priority: t.priority, is_blocked: hasUnfinishedDeps };
8657
8657
  }
@@ -8911,7 +8911,7 @@ function getBlockingDeps(id, db) {
8911
8911
  const blocking = [];
8912
8912
  for (const dep of deps) {
8913
8913
  const task = getTask(dep.depends_on, d);
8914
- if (task && task.status !== "completed")
8914
+ if (task && isBlockingDependencyStatus(task.status))
8915
8915
  blocking.push(task);
8916
8916
  }
8917
8917
  return blocking;
@@ -9235,7 +9235,7 @@ function getNextTask(agentId, filters, db) {
9235
9235
  conditions.push(`id IN (SELECT task_id FROM task_tags WHERE tag IN (${placeholders}))`);
9236
9236
  params.push(...filters.tags);
9237
9237
  }
9238
- conditions.push("id NOT IN (SELECT td.task_id FROM task_dependencies td JOIN tasks dep ON dep.id = td.depends_on WHERE dep.status != 'completed')");
9238
+ conditions.push("id NOT IN (SELECT td.task_id FROM task_dependencies td JOIN tasks dep ON dep.id = td.depends_on WHERE dep.status NOT IN ('completed', 'cancelled'))");
9239
9239
  const where = conditions.join(" AND ");
9240
9240
  let recentProjectIds = [];
9241
9241
  const assignedAliasParams = [];
@@ -9279,7 +9279,7 @@ function getActiveWork(filters, db) {
9279
9279
  }
9280
9280
  function getTasksChangedSince(since, filters, db) {
9281
9281
  const d = db || getDatabase();
9282
- const conditions = ["updated_at > ?"];
9282
+ const conditions = ["(julianday(updated_at) IS NULL OR julianday(updated_at) > julianday(?))"];
9283
9283
  const params = [since];
9284
9284
  if (filters?.project_id) {
9285
9285
  conditions.push("project_id = ?");
@@ -12809,7 +12809,7 @@ var init_tasks = __esm(() => {
12809
12809
  // package.json
12810
12810
  var package_default = {
12811
12811
  name: "@hasna/todos",
12812
- version: "0.15.41",
12812
+ version: "0.15.46",
12813
12813
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
12814
12814
  type: "module",
12815
12815
  main: "dist/index.js",
@@ -12927,7 +12927,7 @@ var package_default = {
12927
12927
  author: "Andrei Hasna <andrei@hasna.com>",
12928
12928
  license: "Apache-2.0",
12929
12929
  dependencies: {
12930
- "@hasna/contracts": "0.13.3",
12930
+ "@hasna/contracts": "0.13.4",
12931
12931
  "@hasna/events": "^0.1.11",
12932
12932
  "@modelcontextprotocol/sdk": "^1.12.1",
12933
12933
  chalk: "^5.4.1",
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "packageName": "@hasna/todos",
3
- "packageVersion": "0.15.41",
3
+ "packageVersion": "0.15.46",
4
4
  "repository": "https://github.com/hasna/todos.git",
5
- "gitCommit": "ac5d8ade147fee9eff8ec4ddd674eb784d9c38d7",
6
- "gitTree": "304d3040aaa58c79859bfd770805c3b7fdcfcfb2",
7
- "sourceTreeSha256": "62c6a2e0e541c85250c14c6ce44524291578d8acf977f7a0ab34f26203542aaf",
8
- "generatedAt": "2026-08-22T13:16:30.000Z"
5
+ "gitCommit": "c80f00ce94d3a8a18c6c3810127cf4a7bb7b2353",
6
+ "gitTree": "744ca8f8d9f3b8c2afff7f9c92df419dc8a64ad8",
7
+ "sourceTreeSha256": "60f8de83b04c4b91fbcf564f95f0f9cb5998683b8e35512a4bd42577c1edd8ba",
8
+ "generatedAt": "2026-08-22T22:48:59.000Z"
9
9
  }
@@ -1 +1 @@
1
- {"version":3,"file":"cloud.d.ts","sourceRoot":"","sources":["../../src/server/cloud.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAwB,MAAM,uBAAuB,CAAC;AAG1E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAKvD,OAAO,EAGL,KAAK,iCAAiC,EACvC,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAGL,KAAK,0BAA0B,EAChC,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAGL,KAAK,iCAAiC,EACvC,MAAM,mCAAmC,CAAC;AAQ3C,OAAO,EAEL,KAAK,+BAA+B,EACpC,KAAK,8BAA8B,EACpC,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAEL,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC7B,MAAM,kCAAkC,CAAC;AAE1C,eAAO,MAAM,cAAc,UAAU,CAAC;AAEtC,uFAAuF;AACvF,wBAAgB,uBAAuB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,GAAG,SAAS,CAOhG;AAED,+DAA+D;AAC/D,wBAAgB,oBAAoB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,GAAG,SAAS,CAO7F;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAEzF;AA4BD,4EAA4E;AAC5E,wBAAgB,sBAAsB,IAAI,mBAAmB,CAK5D;AAED,gFAAgF;AAChF,wBAAgB,qBAAqB,IAAI,aAAa,CAIrD;AAED,iFAAiF;AACjF,wBAAgB,oCAAoC,IAAI,iCAAiC,CAYxF;AAED,gFAAgF;AAChF,wBAAgB,6BAA6B,IAAI,0BAA0B,CAO1E;AAED,wFAAwF;AACxF,wBAAgB,oCAAoC,IAAI,iCAAiC,CAOxF;AAuBD,wBAAgB,cAAc,IAAI,WAAW,CAI5C;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,cAAc,CAoBjD;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAsBvD;AAED;;;;GAIG;AACH,wBAAsB,6BAA6B,IAAI,OAAO,CAAC,IAAI,CAAC,CAEnE;AAED;;;GAGG;AACH,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,IAAI,CAAC,CAEjE;AAED;;;GAGG;AACH,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,IAAI,CAAC,CAElE;AAED,uFAAuF;AACvF,wBAAsB,kCAAkC,IAAI,OAAO,CAAC,IAAI,CAAC,CAExE;AAED;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC,CAS9D;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,GAAE,+BAAoC,GAC5C,OAAO,CAAC,8BAA8B,CAAC,CAEzC;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,uBAAuB,CAAC,CAElC;AAED,iEAAiE;AACjE,wBAAsB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAIlD;AAED,4BAA4B;AAC5B,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAWhD"}
1
+ {"version":3,"file":"cloud.d.ts","sourceRoot":"","sources":["../../src/server/cloud.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAE,WAAW,EAAwB,MAAM,uBAAuB,CAAC;AAG1E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAKvD,OAAO,EAGL,KAAK,iCAAiC,EACvC,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAGL,KAAK,0BAA0B,EAChC,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAGL,KAAK,iCAAiC,EACvC,MAAM,mCAAmC,CAAC;AAQ3C,OAAO,EAEL,KAAK,+BAA+B,EACpC,KAAK,8BAA8B,EACpC,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAEL,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC7B,MAAM,kCAAkC,CAAC;AAE1C,eAAO,MAAM,cAAc,UAAU,CAAC;AAEtC,uFAAuF;AACvF,wBAAgB,uBAAuB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,GAAG,SAAS,CAOhG;AAED,+DAA+D;AAC/D,wBAAgB,oBAAoB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,GAAG,SAAS,CAO7F;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAEzF;AA4BD,4EAA4E;AAC5E,wBAAgB,sBAAsB,IAAI,mBAAmB,CAK5D;AAED,gFAAgF;AAChF,wBAAgB,qBAAqB,IAAI,aAAa,CAIrD;AAED,iFAAiF;AACjF,wBAAgB,oCAAoC,IAAI,iCAAiC,CAYxF;AAED,gFAAgF;AAChF,wBAAgB,6BAA6B,IAAI,0BAA0B,CAO1E;AAED,wFAAwF;AACxF,wBAAgB,oCAAoC,IAAI,iCAAiC,CAOxF;AAuBD,wBAAgB,cAAc,IAAI,WAAW,CAI5C;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,cAAc,CAoBjD;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAkCvD;AAED;;;;GAIG;AACH,wBAAsB,6BAA6B,IAAI,OAAO,CAAC,IAAI,CAAC,CAEnE;AAED;;;GAGG;AACH,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,IAAI,CAAC,CAEjE;AAED;;;GAGG;AACH,wBAAsB,4BAA4B,IAAI,OAAO,CAAC,IAAI,CAAC,CAElE;AAED,uFAAuF;AACvF,wBAAsB,kCAAkC,IAAI,OAAO,CAAC,IAAI,CAAC,CAExE;AAED;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC,CAS9D;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,GAAE,+BAAoC,GAC5C,OAAO,CAAC,8BAA8B,CAAC,CAEzC;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,uBAAuB,CAAC,CAElC;AAED,iEAAiE;AACjE,wBAAsB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAIlD;AAED,4BAA4B;AAC5B,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAWhD"}
@@ -70,7 +70,7 @@ var package_default;
70
70
  var init_package = __esm(() => {
71
71
  package_default = {
72
72
  name: "@hasna/todos",
73
- version: "0.15.41",
73
+ version: "0.15.46",
74
74
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
75
75
  type: "module",
76
76
  main: "dist/index.js",
@@ -188,7 +188,7 @@ var init_package = __esm(() => {
188
188
  author: "Andrei Hasna <andrei@hasna.com>",
189
189
  license: "Apache-2.0",
190
190
  dependencies: {
191
- "@hasna/contracts": "0.13.3",
191
+ "@hasna/contracts": "0.13.4",
192
192
  "@hasna/events": "^0.1.11",
193
193
  "@modelcontextprotocol/sdk": "^1.12.1",
194
194
  chalk: "^5.4.1",
@@ -223,7 +223,7 @@ var init_package_version = __esm(() => {
223
223
  init_package();
224
224
  });
225
225
 
226
- // node_modules/.bun/@hasna+contracts@0.13.3+86f0f4a9d69523e0/node_modules/@hasna/contracts/dist/auth/index.js
226
+ // node_modules/.bun/@hasna+contracts@0.13.4+ad7b1171e6eea7eb/node_modules/@hasna/contracts/dist/auth/index.js
227
227
  import { createHash, createHmac, randomBytes, timingSafeEqual } from "crypto";
228
228
  function isValidTenantId(value) {
229
229
  return typeof value === "string" && TENANT_ID_PATTERN.test(value);
@@ -1385,6 +1385,60 @@ var init_creator_identity = __esm(() => {
1385
1385
  init_sync_utils();
1386
1386
  });
1387
1387
 
1388
+ // src/lib/instant-compare.ts
1389
+ function isLeapYear(year) {
1390
+ return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
1391
+ }
1392
+ function sqliteJulianDay(value) {
1393
+ const m = SQLITE_STAMP.exec(value);
1394
+ if (!m)
1395
+ return null;
1396
+ const year = Number(m[1]);
1397
+ const month = Number(m[2]);
1398
+ const day = Number(m[3]);
1399
+ const hour = m[4] === undefined ? 0 : Number(m[4]);
1400
+ const minute = m[5] === undefined ? 0 : Number(m[5]);
1401
+ const second = m[6] === undefined ? 0 : Number(m[6]);
1402
+ const frac = m[7];
1403
+ const sign = m[8];
1404
+ const offsetHour = m[9];
1405
+ const offsetMinute = m[10];
1406
+ if (month < 1 || month > 12)
1407
+ return null;
1408
+ if (hour > 23 || minute > 59 || second > 59)
1409
+ return null;
1410
+ const maxDay = (DAYS_IN_MONTH[month - 1] ?? 0) + (month === 2 && isLeapYear(year) ? 1 : 0);
1411
+ if (day < 1 || day > maxDay)
1412
+ return null;
1413
+ let offsetMinutes = 0;
1414
+ if (sign !== undefined) {
1415
+ const oh = Number(offsetHour ?? "0");
1416
+ const om = Number(offsetMinute ?? "0");
1417
+ if (oh > 23 || om > 59)
1418
+ return null;
1419
+ offsetMinutes = oh * 60 + om;
1420
+ if (sign === "-")
1421
+ offsetMinutes = -offsetMinutes;
1422
+ }
1423
+ const micros = frac === undefined ? 0 : Number((frac + "000000").slice(0, 6));
1424
+ const ms = Date.UTC(year, month - 1, day, hour, minute, second);
1425
+ return (ms + micros / 1000) / 86400000 + 2440587.5 - offsetMinutes / 1440;
1426
+ }
1427
+ function changedSinceStampNewer(stamp, since) {
1428
+ const stampJd = sqliteJulianDay(stamp);
1429
+ if (stampJd === null)
1430
+ return true;
1431
+ const sinceJd = sqliteJulianDay(since);
1432
+ if (sinceJd === null)
1433
+ return false;
1434
+ return stampJd > sinceJd;
1435
+ }
1436
+ var SQLITE_STAMP, DAYS_IN_MONTH;
1437
+ var init_instant_compare = __esm(() => {
1438
+ SQLITE_STAMP = /^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?(?:Z|([+-])(\d{2}):(\d{2}))?)?$/;
1439
+ DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
1440
+ });
1441
+
1388
1442
  // src/lib/plan-project-link-contract.ts
1389
1443
  import { createHash as createHash2 } from "crypto";
1390
1444
  function canonicalPlanProjectLinkJson(value) {
@@ -2898,11 +2952,16 @@ class PostgresJsonRecordStore {
2898
2952
  return context?.requestId ?? this.sourceMachineId ?? null;
2899
2953
  }
2900
2954
  async ensureSchema() {
2901
- this.schemaReady ??= (async () => {
2902
- for (const sql of postgresTodosSyncSchemaSql(this.tableName, this.cursorTableName)) {
2903
- await this.options.client.query(sql);
2904
- }
2905
- })();
2955
+ if (!this.schemaReady) {
2956
+ this.schemaReady = (async () => {
2957
+ for (const sql of postgresTodosSyncSchemaSql(this.tableName, this.cursorTableName)) {
2958
+ await this.options.client.query(sql);
2959
+ }
2960
+ })().catch((error) => {
2961
+ this.schemaReady = null;
2962
+ throw error;
2963
+ });
2964
+ }
2906
2965
  await this.schemaReady;
2907
2966
  }
2908
2967
  async get(type, id) {
@@ -4664,7 +4723,7 @@ async function getActiveWork(filters, store) {
4664
4723
  }));
4665
4724
  }
4666
4725
  async function getChangedSince(since, filters, store) {
4667
- return (await listTasks(filters ?? {}, store)).filter((task) => task.updated_at > since);
4726
+ return (await listTasks(filters ?? {}, store)).filter((task) => changedSinceStampNewer(task.updated_at ?? "", since));
4668
4727
  }
4669
4728
  async function createProject(input, store, context) {
4670
4729
  const timestamp = new Date().toISOString();
@@ -5223,6 +5282,7 @@ var CLOUD_LOCK_EXPIRY_MINUTES = 30, TASK_ORDER_TIEBREAK = "CASE payload->>'prior
5223
5282
  var init_postgres_adapter = __esm(() => {
5224
5283
  init_types();
5225
5284
  init_creator_identity();
5285
+ init_instant_compare();
5226
5286
  init_plan_project_link_contract();
5227
5287
  init_stale_lock_handoff();
5228
5288
  init_postgres_sync();
@@ -7054,10 +7114,19 @@ class PostgresPrGroupLedgerPersistence {
7054
7114
  this.client = client;
7055
7115
  }
7056
7116
  async ensureSchema() {
7057
- this.schemaReady ??= (async () => {
7058
- for (const statement of postgresPrGroupSchemaSql())
7059
- await this.client.query(statement);
7060
- })();
7117
+ if (this.schemaReady === null) {
7118
+ const attempt = (async () => {
7119
+ for (const statement of postgresPrGroupSchemaSql())
7120
+ await this.client.query(statement);
7121
+ })();
7122
+ this.schemaReady = attempt;
7123
+ try {
7124
+ await attempt;
7125
+ } catch (error) {
7126
+ this.schemaReady = null;
7127
+ throw error;
7128
+ }
7129
+ }
7061
7130
  return this.schemaReady;
7062
7131
  }
7063
7132
  async transaction(fn) {
@@ -7702,14 +7771,23 @@ class PostgresTodosProjectRegistrationBackend {
7702
7771
  this.cursorTableName = safeIdentifier(options.cursorTableName ?? "todos_sync_cursors", "cursorTableName");
7703
7772
  }
7704
7773
  async ensureSchema() {
7705
- this.schemaReady ??= (async () => {
7706
- for (const statement of postgresTodosSyncSchemaSql(this.tableName, this.cursorTableName)) {
7707
- await this.client.query(statement);
7708
- }
7709
- for (const statement of postgresTodosProjectRegistrationSchemaSql()) {
7710
- await this.client.query(statement);
7774
+ if (this.schemaReady === null) {
7775
+ const attempt = (async () => {
7776
+ for (const statement of postgresTodosSyncSchemaSql(this.tableName, this.cursorTableName)) {
7777
+ await this.client.query(statement);
7778
+ }
7779
+ for (const statement of postgresTodosProjectRegistrationSchemaSql()) {
7780
+ await this.client.query(statement);
7781
+ }
7782
+ })();
7783
+ this.schemaReady = attempt;
7784
+ try {
7785
+ await attempt;
7786
+ } catch (error) {
7787
+ this.schemaReady = null;
7788
+ throw error;
7711
7789
  }
7712
- })();
7790
+ }
7713
7791
  await this.schemaReady;
7714
7792
  }
7715
7793
  async transaction(fn) {
@@ -15288,7 +15366,7 @@ function getTaskGraph(taskId, direction = "both", db) {
15288
15366
  const deps = getTaskDependencies(t.id, d);
15289
15367
  const hasUnfinishedDeps = deps.some((dep) => {
15290
15368
  const depTask = getTask(dep.depends_on, d);
15291
- return depTask && depTask.status !== "completed";
15369
+ return depTask && isBlockingDependencyStatus(depTask.status);
15292
15370
  });
15293
15371
  return { id: t.id, short_id: t.short_id, title: t.title, status: t.status, priority: t.priority, is_blocked: hasUnfinishedDeps };
15294
15372
  }
@@ -15412,7 +15490,7 @@ function getBlockingDeps(id, db) {
15412
15490
  const blocking = [];
15413
15491
  for (const dep of deps) {
15414
15492
  const task = getTask(dep.depends_on, d);
15415
- if (task && task.status !== "completed")
15493
+ if (task && isBlockingDependencyStatus(task.status))
15416
15494
  blocking.push(task);
15417
15495
  }
15418
15496
  return blocking;
@@ -15736,7 +15814,7 @@ function getNextTask2(agentId, filters, db) {
15736
15814
  conditions.push(`id IN (SELECT task_id FROM task_tags WHERE tag IN (${placeholders}))`);
15737
15815
  params.push(...filters.tags);
15738
15816
  }
15739
- conditions.push("id NOT IN (SELECT td.task_id FROM task_dependencies td JOIN tasks dep ON dep.id = td.depends_on WHERE dep.status != 'completed')");
15817
+ conditions.push("id NOT IN (SELECT td.task_id FROM task_dependencies td JOIN tasks dep ON dep.id = td.depends_on WHERE dep.status NOT IN ('completed', 'cancelled'))");
15740
15818
  const where = conditions.join(" AND ");
15741
15819
  let recentProjectIds = [];
15742
15820
  const assignedAliasParams = [];
@@ -15780,7 +15858,7 @@ function getActiveWork2(filters, db) {
15780
15858
  }
15781
15859
  function getTasksChangedSince(since, filters, db) {
15782
15860
  const d = db || getDatabase();
15783
- const conditions = ["updated_at > ?"];
15861
+ const conditions = ["(julianday(updated_at) IS NULL OR julianday(updated_at) > julianday(?))"];
15784
15862
  const params = [since];
15785
15863
  if (filters?.project_id) {
15786
15864
  conditions.push("project_id = ?");
@@ -21078,7 +21156,7 @@ function matchesExtraFilters(task, filter) {
21078
21156
  }
21079
21157
  if (filter.tags?.length) {
21080
21158
  const taskTags = new Set(task.tags ?? []);
21081
- if (!filter.tags.every((tag) => taskTags.has(tag)))
21159
+ if (!filter.tags.some((tag) => taskTags.has(tag)))
21082
21160
  return false;
21083
21161
  }
21084
21162
  if (filter.include_subtasks !== true && filter.parent_id === undefined && task.parent_id)
@@ -27812,12 +27890,21 @@ class PostgresTodosTaskManifestBackend {
27812
27890
  this.tenantId = options.tenantId ?? "default";
27813
27891
  }
27814
27892
  async ensureSchema() {
27815
- this.schemaReady ??= (async () => {
27816
- for (const sql of postgresTodosSyncSchemaSql(this.tableName))
27817
- await this.client.query(sql);
27818
- for (const sql of postgresTodosTaskManifestSchemaSql(this.tenantId))
27819
- await this.client.query(sql);
27820
- })();
27893
+ if (this.schemaReady === null) {
27894
+ const attempt = (async () => {
27895
+ for (const sql of postgresTodosSyncSchemaSql(this.tableName))
27896
+ await this.client.query(sql);
27897
+ for (const sql of postgresTodosTaskManifestSchemaSql(this.tenantId))
27898
+ await this.client.query(sql);
27899
+ })();
27900
+ this.schemaReady = attempt;
27901
+ try {
27902
+ await attempt;
27903
+ } catch (error) {
27904
+ this.schemaReady = null;
27905
+ throw error;
27906
+ }
27907
+ }
27821
27908
  await this.schemaReady;
27822
27909
  }
27823
27910
  async insertSync(tx, objectType2, objectId, payload, now3) {
@@ -29117,12 +29204,21 @@ class PostgresTodosTaskSubtreeTransferBackend {
29117
29204
  this.tenantId = options.tenantId ?? "default";
29118
29205
  }
29119
29206
  async ensureSchema() {
29120
- this.schemaReady ??= (async () => {
29121
- for (const sql of postgresTodosSyncSchemaSql(this.tableName))
29122
- await this.client.query(sql);
29123
- for (const sql of postgresTodosTaskSubtreeTransferSchemaSql())
29124
- await this.client.query(sql);
29125
- })();
29207
+ if (this.schemaReady === null) {
29208
+ const attempt = (async () => {
29209
+ for (const sql of postgresTodosSyncSchemaSql(this.tableName))
29210
+ await this.client.query(sql);
29211
+ for (const sql of postgresTodosTaskSubtreeTransferSchemaSql())
29212
+ await this.client.query(sql);
29213
+ })();
29214
+ this.schemaReady = attempt;
29215
+ try {
29216
+ await attempt;
29217
+ } catch (error) {
29218
+ this.schemaReady = null;
29219
+ throw error;
29220
+ }
29221
+ }
29126
29222
  await this.schemaReady;
29127
29223
  }
29128
29224
  async snapshot(client, input, forUpdate = false) {
@@ -30221,7 +30317,10 @@ async function ensureCloudSchema() {
30221
30317
  await client.query(sql);
30222
30318
  }
30223
30319
  await getApiKeyStore().ensureSchema();
30224
- })();
30320
+ })().catch((error) => {
30321
+ schemaEnsured = null;
30322
+ throw error;
30323
+ });
30225
30324
  return schemaEnsured;
30226
30325
  }
30227
30326
  async function ensureCloudCommentCursorIndex() {
@@ -52868,7 +52967,7 @@ var require_formats = __commonJS((exports) => {
52868
52967
  email: /^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i
52869
52968
  };
52870
52969
  exports.formatNames = Object.keys(exports.fullFormats);
52871
- function isLeapYear(year) {
52970
+ function isLeapYear2(year) {
52872
52971
  return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
52873
52972
  }
52874
52973
  var DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
@@ -52880,7 +52979,7 @@ var require_formats = __commonJS((exports) => {
52880
52979
  const year = +matches[1];
52881
52980
  const month = +matches[2];
52882
52981
  const day = +matches[3];
52883
- return month >= 1 && month <= 12 && day >= 1 && day <= (month === 2 && isLeapYear(year) ? 29 : DAYS[month]);
52982
+ return month >= 1 && month <= 12 && day >= 1 && day <= (month === 2 && isLeapYear2(year) ? 29 : DAYS[month]);
52884
52983
  }
52885
52984
  function compareDate(d1, d2) {
52886
52985
  if (!(d1 && d2))
@@ -55696,7 +55795,7 @@ var init_assignee_context = __esm(() => {
55696
55795
  init_assignee_validation();
55697
55796
  });
55698
55797
 
55699
- // node_modules/.bun/@hasna+contracts@0.13.3+86f0f4a9d69523e0/node_modules/@hasna/contracts/dist/client/storage.js
55798
+ // node_modules/.bun/@hasna+contracts@0.13.4+ad7b1171e6eea7eb/node_modules/@hasna/contracts/dist/client/storage.js
55700
55799
  import { isIP } from "net";
55701
55800
  import { readFileSync as readFileSync5, statSync as statSync4 } from "fs";
55702
55801
  import { join as join10 } from "path";
@@ -58156,6 +58255,7 @@ var init_cloud_router = __esm(() => {
58156
58255
  init_storage();
58157
58256
  init_types();
58158
58257
  init_redaction();
58258
+ init_instant_compare();
58159
58259
  init_plan_project_link_contract();
58160
58260
  init_http_client();
58161
58261
  init_adoption_validation();
@@ -93943,7 +94043,7 @@ function scoreHealth(scope, scopeId, db) {
93943
94043
  const blockers = d.query(`SELECT dep.id, dep.short_id, dep.title, dep.status
93944
94044
  FROM task_dependencies td
93945
94045
  JOIN tasks dep ON dep.id = td.depends_on
93946
- WHERE td.task_id = ? AND dep.status != 'completed'`).all(task2.id);
94046
+ WHERE td.task_id = ? AND dep.status NOT IN ('completed', 'cancelled')`).all(task2.id);
93947
94047
  return { id: task2.id, short_id: task2.short_id, title: redactEvidenceText(task2.title), blockers };
93948
94048
  }).filter((entry2) => entry2.blockers.length > 0);
93949
94049
  const overdue = tasks.filter((task2) => activeTaskIds.has(task2.id) && Boolean(task2.due_at && task2.due_at < generatedAt)).map((task2) => ({ id: task2.id, short_id: task2.short_id, title: redactEvidenceText(task2.title), due_at: task2.due_at }));
@@ -1 +1 @@
1
- {"version":3,"file":"local-sqlite.d.ts","sourceRoot":"","sources":["../../src/storage/local-sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAgF3C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAM3D,MAAM,WAAW,2CAA2C;IAC1D,EAAE,CAAC,EAAE,QAAQ,CAAC;CACf;AAiGD,wBAAgB,oCAAoC,CAClD,OAAO,GAAE,2CAAgD,GACxD,mBAAmB,CA2JrB"}
1
+ {"version":3,"file":"local-sqlite.d.ts","sourceRoot":"","sources":["../../src/storage/local-sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAgF3C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAM3D,MAAM,WAAW,2CAA2C;IAC1D,EAAE,CAAC,EAAE,QAAQ,CAAC;CACf;AAqGD,wBAAgB,oCAAoC,CAClD,OAAO,GAAE,2CAAgD,GACxD,mBAAmB,CA2JrB"}
@@ -1 +1 @@
1
- {"version":3,"file":"postgres-adapter.d.ts","sourceRoot":"","sources":["../../src/storage/postgres-adapter.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAcV,mBAAmB,EAYpB,MAAM,iBAAiB,CAAC;AAczB,OAAO,EAIL,KAAK,wBAAwB,EAE9B,MAAM,oBAAoB,CAAC;AA4B5B,MAAM,WAAW,wCAAwC;IACvD,MAAM,EAAE,wBAAwB,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAuBD,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,wCAAwC,GAChD,mBAAmB,CAiLrB"}
1
+ {"version":3,"file":"postgres-adapter.d.ts","sourceRoot":"","sources":["../../src/storage/postgres-adapter.ts"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAcV,mBAAmB,EAYpB,MAAM,iBAAiB,CAAC;AAczB,OAAO,EAIL,KAAK,wBAAwB,EAE9B,MAAM,oBAAoB,CAAC;AA4B5B,MAAM,WAAW,wCAAwC;IACvD,MAAM,EAAE,wBAAwB,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAuBD,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,wCAAwC,GAChD,mBAAmB,CAiLrB"}