openfox 2.0.141 → 2.0.142

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 (83) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/CHANGELOG.md +16 -0
  3. package/dist/{chat-handler-F7QRNO2E.js → chat-handler-OFB4426F.js} +26 -26
  4. package/dist/{chunk-CRJ5J7U5.js → chunk-2CEZQAGQ.js} +3 -3
  5. package/dist/{chunk-BB67GW5P.js → chunk-5CZAKVGZ.js} +12 -12
  6. package/dist/{chunk-SSHXYKW3.js → chunk-5HDPFHHJ.js} +2 -2
  7. package/dist/{chunk-CEEBKCGX.js → chunk-ACILWRPU.js} +7 -7
  8. package/dist/{chunk-TJOKDC34.js → chunk-CSUMZAYY.js} +399 -86
  9. package/dist/{chunk-EE6VFEE6.js → chunk-DNL2NUDB.js} +2 -2
  10. package/dist/{chunk-UA2ATO3B.js → chunk-DQEANXXF.js} +3 -3
  11. package/dist/{chunk-4F4ARGAM.js → chunk-DRIR2IZR.js} +3 -3
  12. package/dist/{chunk-AE2IBEUH.js → chunk-EFEHIJXF.js} +2 -2
  13. package/dist/chunk-EPZO3MGM.js +7 -0
  14. package/dist/{chunk-DBF5GKZF.js → chunk-F3R4YS3F.js} +3 -3
  15. package/dist/{chunk-RHUZLWZV.js → chunk-FFVIO6GL.js} +4 -4
  16. package/dist/{chunk-RCEGPT57.js → chunk-HQDKHNWS.js} +7 -7
  17. package/dist/{chunk-H7YL7PH3.js → chunk-J556QIRD.js} +2 -2
  18. package/dist/{chunk-DPFET73G.js → chunk-JYYBSODN.js} +2 -2
  19. package/dist/{chunk-46Q2SHGJ.js → chunk-MEPTCUYN.js} +2 -2
  20. package/dist/{chunk-YFTCFJJM.js → chunk-N5AJHJG5.js} +3 -3
  21. package/dist/{chunk-2OTUYNXD.js → chunk-NJFXYSXP.js} +5 -5
  22. package/dist/{chunk-XE6UC5Z3.js → chunk-PHR64A6U.js} +2 -2
  23. package/dist/{chunk-G7BQWMV7.js → chunk-Q5JCBAQF.js} +3 -3
  24. package/dist/{chunk-NDAXMOD2.js → chunk-RM4ZBI2B.js} +20 -20
  25. package/dist/{chunk-EMXMTCSY.js → chunk-SKNPOZYP.js} +4 -2
  26. package/dist/{chunk-PS5CHS7S.js → chunk-TEUVZCPS.js} +17 -17
  27. package/dist/{chunk-JLHYB445.js → chunk-TIIWXMGE.js} +2 -2
  28. package/dist/{chunk-UJO67GFD.js → chunk-TRZZCMJ3.js} +9 -9
  29. package/dist/{chunk-LW6MMSTD.js → chunk-VGE6UYND.js} +2 -2
  30. package/dist/{chunk-SKHU4M6L.js → chunk-VK2BSIJP.js} +121 -117
  31. package/dist/{chunk-JU757HVI.js → chunk-WOVWVB5J.js} +11 -1
  32. package/dist/{chunk-FBOIDBPA.js → chunk-WSDR27YV.js} +4 -4
  33. package/dist/{chunk-IZVIN7XC.js → chunk-XADIBENX.js} +6 -6
  34. package/dist/{chunk-GKRYQ56N.js → chunk-Z7IT3V22.js} +3 -3
  35. package/dist/cli/dev.js +1 -1
  36. package/dist/cli/index.js +1 -1
  37. package/dist/{client-KJS7QIIV.js → client-2A766XZK.js} +5 -5
  38. package/dist/{compactor-QDTK3SZI.js → compactor-3BVVHO4M.js} +11 -11
  39. package/dist/{describe-image-UFOHQIPB.js → describe-image-F22VNBWY.js} +12 -12
  40. package/dist/{dynamic-context-UEC6MMEV.js → dynamic-context-5SFXTEAP.js} +12 -12
  41. package/dist/{events-73MLUDTX.js → events-UAUOGRSV.js} +8 -8
  42. package/dist/{export-import-7I7EJKTE.js → export-import-PBEX3KWU.js} +9 -9
  43. package/dist/{folding-BXMVJIFF.js → folding-IHD244OU.js} +6 -6
  44. package/dist/http-client-RA7XFYPO.js +11 -0
  45. package/dist/{inspect-proxy-A3OROWPG.js → inspect-proxy-24Q2CNCZ.js} +4 -4
  46. package/dist/{launch-77CO4C7W.js → launch-L5NL75KK.js} +26 -26
  47. package/dist/{manager-JZNNVDYR.js → manager-4SMHCFXY.js} +5 -5
  48. package/dist/{orchestrator-IRUEM4R4.js → orchestrator-53SS74LD.js} +25 -25
  49. package/dist/package.json +1 -1
  50. package/dist/{path-security-MQXMTFRU.js → path-security-WOA6MTWQ.js} +10 -10
  51. package/dist/{platform-DBPLSLE4.js → platform-EAZNZDAE.js} +4 -4
  52. package/dist/{processor-VMUKAMG6.js → processor-T4VWZMXT.js} +25 -25
  53. package/dist/{project-creator-T3A4H3YV.js → project-creator-K5EGKY2P.js} +3 -3
  54. package/dist/{projects-5U37RUTL.js → projects-VDWCFPVE.js} +3 -3
  55. package/dist/{protocol-D6DZzkSH.d.ts → protocol-Dpf4FBF2.d.ts} +1 -1
  56. package/dist/provider/index.d.ts +4 -4
  57. package/dist/{provider-7LNTLWRB.js → provider-5XNJEDKW.js} +6 -6
  58. package/dist/{provider-manager-TV5I5YJH.js → provider-manager-RHJYUU5W.js} +6 -6
  59. package/dist/{registry-PVJAAL2Y.js → registry-C7RKKHZB.js} +5 -5
  60. package/dist/scheduler-VLTPMGGJ.js +25 -0
  61. package/dist/{serve-HTJREJEC.js → serve-LP3ZX6CW.js} +31 -31
  62. package/dist/server/index.d.ts +3 -3
  63. package/dist/server/index.js +30 -30
  64. package/dist/server-IGZLCNZG.js +61 -0
  65. package/dist/{service-7QKWUNU4.js → service-AZU4YTS2.js} +9 -9
  66. package/dist/{session-overrides-WYOY7PVC.js → session-overrides-CKS2AVE5.js} +7 -7
  67. package/dist/{sessions-U6D2T5QP.js → sessions-DAHLRQE2.js} +6 -6
  68. package/dist/{settings-HCSHADP5.js → settings-TR4BF2C4.js} +3 -3
  69. package/dist/shared/index.d.ts +3 -3
  70. package/dist/{tasks-E7EG7344.js → tasks-T5KZXKMD.js} +18 -12
  71. package/dist/{tools-F4COIAZR.js → tools-BL6XCTUV.js} +23 -23
  72. package/dist/{types-ofYNpiqi.d.ts → types-CZJf6Fnb.d.ts} +1 -1
  73. package/dist/{types-CLcL33hJ.d.ts → types-LO8296aY.d.ts} +34 -1
  74. package/dist/{update-5TGZTHLD.js → update-M5DMX3FC.js} +2 -2
  75. package/dist/web/assets/index-BPhet33M.js +313 -0
  76. package/dist/web/index.html +1 -1
  77. package/dist/web/sw.js +1 -1
  78. package/dist/{workspace-LM646OHC.js → workspace-ENN27P56.js} +4 -4
  79. package/package.json +1 -1
  80. package/dist/chunk-LDWWVUW7.js +0 -7
  81. package/dist/http-client-JVJ43TBY.js +0 -11
  82. package/dist/server-5FECIKQK.js +0 -61
  83. package/dist/web/assets/index-A0-uewf7.js +0 -313
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  serverT
3
- } from "./chunk-JLHYB445.js";
3
+ } from "./chunk-TIIWXMGE.js";
4
4
  import {
5
5
  deleteItemFromDir,
6
6
  getDefaultIds,
@@ -11,13 +11,13 @@ import {
11
11
  } from "./chunk-OSXPQSLW.js";
12
12
  import {
13
13
  parseDefaultModelSelection
14
- } from "./chunk-H7YL7PH3.js";
14
+ } from "./chunk-J556QIRD.js";
15
15
  import {
16
16
  getProject
17
- } from "./chunk-DPFET73G.js";
17
+ } from "./chunk-JYYBSODN.js";
18
18
  import {
19
19
  getDatabase
20
- } from "./chunk-JU757HVI.js";
20
+ } from "./chunk-WOVWVB5J.js";
21
21
  import {
22
22
  logger
23
23
  } from "./chunk-K44MW7JJ.js";
@@ -365,14 +365,17 @@ async function resolveSlashLaunch(configDir, projectDir, prompt) {
365
365
 
366
366
  // src/server/db/tasks.ts
367
367
  import { randomBytes } from "crypto";
368
+ function scheduleNextRun(schedule) {
369
+ return schedule.type === "once" ? schedule.runAt : schedule.nextRunAt;
370
+ }
368
371
  function createTask(projectId, input) {
369
372
  const db = getDatabase();
370
373
  const now = (/* @__PURE__ */ new Date()).toISOString();
371
374
  const id = randomBytes(8).toString("hex");
372
375
  const position = db.prepare(`SELECT COALESCE(MAX(position), -1) + 1 AS pos FROM tasks WHERE project_id = ? AND status = 'todo'`).get(projectId);
373
376
  db.prepare(
374
- `INSERT INTO tasks (id, project_id, prompt, attachments, status, run_state, position, version, agent_id, provider_id, model, created_at, updated_at)
375
- VALUES (?, ?, ?, ?, 'todo', NULL, ?, 1, ?, ?, ?, ?, ?)`
377
+ `INSERT INTO tasks (id, project_id, prompt, attachments, status, run_state, position, version, agent_id, provider_id, model, schedule, next_run_at, created_at, updated_at)
378
+ VALUES (?, ?, ?, ?, 'todo', NULL, ?, 1, ?, ?, ?, ?, ?, ?, ?)`
376
379
  ).run(
377
380
  id,
378
381
  projectId,
@@ -382,6 +385,8 @@ function createTask(projectId, input) {
382
385
  input.agentId ?? null,
383
386
  input.providerId ?? null,
384
387
  input.model ?? null,
388
+ input.schedule ? JSON.stringify(input.schedule) : null,
389
+ input.schedule ? scheduleNextRun(input.schedule) : null,
385
390
  now,
386
391
  now
387
392
  );
@@ -423,6 +428,12 @@ function updateTask(id, patch) {
423
428
  sets.push("model = ?");
424
429
  values.push(patch.model);
425
430
  }
431
+ if (patch.schedule !== void 0) {
432
+ sets.push("schedule = ?");
433
+ values.push(patch.schedule ? JSON.stringify(patch.schedule) : null);
434
+ sets.push("next_run_at = ?");
435
+ values.push(patch.schedule ? scheduleNextRun(patch.schedule) : null);
436
+ }
426
437
  values.push(id);
427
438
  db.prepare(`UPDATE tasks SET ${sets.join(", ")} WHERE id = ?`).run(...values);
428
439
  return getTask(id);
@@ -449,6 +460,28 @@ function deleteTask(id) {
449
460
  const db = getDatabase();
450
461
  db.prepare(`DELETE FROM tasks WHERE id = ?`).run(id);
451
462
  }
463
+ function listDueScheduledTasks(nowIso) {
464
+ const db = getDatabase();
465
+ const rows = db.prepare(
466
+ `SELECT * FROM tasks WHERE status = 'todo' AND next_run_at IS NOT NULL AND next_run_at <= ? ORDER BY next_run_at ASC`
467
+ ).all(nowIso);
468
+ return rows.map(hydrateTask);
469
+ }
470
+ function setTaskSchedule(id, schedule, nextRunAt) {
471
+ const db = getDatabase();
472
+ db.prepare(`UPDATE tasks SET schedule = ?, next_run_at = ?, version = version + 1, updated_at = ? WHERE id = ?`).run(
473
+ JSON.stringify(schedule),
474
+ nextRunAt,
475
+ (/* @__PURE__ */ new Date()).toISOString(),
476
+ id
477
+ );
478
+ }
479
+ function clearTaskSchedule(id) {
480
+ const db = getDatabase();
481
+ db.prepare(
482
+ `UPDATE tasks SET schedule = NULL, next_run_at = NULL, version = version + 1, updated_at = ? WHERE id = ?`
483
+ ).run((/* @__PURE__ */ new Date()).toISOString(), id);
484
+ }
452
485
  function appendToBottom(id, status) {
453
486
  const db = getDatabase();
454
487
  const task = getTask(id);
@@ -460,7 +493,7 @@ function appendToBottom(id, status) {
460
493
  id
461
494
  );
462
495
  }
463
- function cloneTask(sourceId) {
496
+ function cloneTask(sourceId, copySchedule = true) {
464
497
  const source = getTask(sourceId);
465
498
  if (!source) return null;
466
499
  return createTask(source.projectId, {
@@ -468,7 +501,8 @@ function cloneTask(sourceId) {
468
501
  attachments: source.attachments,
469
502
  ...source.agentId ? { agentId: source.agentId } : {},
470
503
  ...source.providerId ? { providerId: source.providerId } : {},
471
- ...source.model ? { model: source.model } : {}
504
+ ...source.model ? { model: source.model } : {},
505
+ ...copySchedule && source.schedule ? { schedule: source.schedule } : {}
472
506
  });
473
507
  }
474
508
  function reorderTask(id, status, toIndex) {
@@ -612,6 +646,7 @@ function hydrateTask(row) {
612
646
  const sessionIds = links.map((l) => l.session_id);
613
647
  const activeLink = links.find((l) => Boolean(l.active));
614
648
  const runState = row.status === "in_progress" ? row.run_state : void 0;
649
+ const schedule = row.schedule ? safeParseSchedule(row.schedule) : void 0;
615
650
  const task = {
616
651
  id: row.id,
617
652
  projectId: row.project_id,
@@ -624,6 +659,7 @@ function hydrateTask(row) {
624
659
  ...row.agent_id ? { agentId: row.agent_id } : {},
625
660
  ...row.provider_id ? { providerId: row.provider_id } : {},
626
661
  ...row.model ? { model: row.model } : {},
662
+ ...schedule ? { schedule } : {},
627
663
  sessionIds,
628
664
  ...activeLink ? { activeSessionId: activeLink.session_id } : {},
629
665
  gateValues: getGateValues(row.id),
@@ -656,6 +692,104 @@ function safeParseAttachments(raw) {
656
692
  return [];
657
693
  }
658
694
  }
695
+ function safeParseSchedule(raw) {
696
+ try {
697
+ const parsed = JSON.parse(raw);
698
+ if (!parsed || typeof parsed !== "object") return void 0;
699
+ if (parsed.type === "once" && typeof parsed.runAt === "string") return parsed;
700
+ if (parsed.type === "recurring" && typeof parsed.interval === "number" && typeof parsed.startAt === "string" && typeof parsed.nextRunAt === "string" && parsed.end && typeof parsed.end.kind === "string" && typeof parsed.occurrencesDone === "number") {
701
+ return parsed;
702
+ }
703
+ return void 0;
704
+ } catch {
705
+ return void 0;
706
+ }
707
+ }
708
+
709
+ // src/server/tasks/recurrence.ts
710
+ var MS_DAY = 864e5;
711
+ function dayDiff(a, b) {
712
+ const ua = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
713
+ const ub = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());
714
+ return Math.round((ub - ua) / MS_DAY);
715
+ }
716
+ function addDays(date, days) {
717
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate() + days);
718
+ }
719
+ function mondayOf(date) {
720
+ return addDays(date, -((date.getDay() + 6) % 7));
721
+ }
722
+ function lastDayOfMonth(y, m0) {
723
+ return new Date(y, m0 + 1, 0).getDate();
724
+ }
725
+ function nextOccurrence(after, rule) {
726
+ const start = new Date(rule.startAt);
727
+ if (Number.isNaN(start.getTime()) || !Number.isFinite(after.getTime())) return null;
728
+ if (rule.end.kind === "count" && rule.occurrencesDone >= rule.end.count) return null;
729
+ const hour = start.getHours();
730
+ const minute = start.getMinutes();
731
+ const second = start.getSeconds();
732
+ const ms = start.getMilliseconds();
733
+ const atTime = (y, m0, d) => new Date(y, m0, d, hour, minute, second, ms);
734
+ let candidate = null;
735
+ if (after.getTime() < start.getTime()) {
736
+ candidate = new Date(start);
737
+ } else if (rule.freq === "day") {
738
+ const k = Math.floor(dayDiff(start, after) / rule.interval);
739
+ let c = addDays(start, k * rule.interval);
740
+ if (c.getTime() <= after.getTime()) c = addDays(c, rule.interval);
741
+ candidate = atTime(c.getFullYear(), c.getMonth(), c.getDate());
742
+ } else if (rule.freq === "week") {
743
+ const weekdays = rule.weekdays && rule.weekdays.length > 0 ? rule.weekdays : [start.getDay()];
744
+ const baseMonday = mondayOf(start);
745
+ const maxDays = (rule.interval + 1) * 7 + 7;
746
+ for (let i = 1; i <= maxDays; i++) {
747
+ const c = addDays(after, i);
748
+ if (!weekdays.includes(c.getDay())) continue;
749
+ const weekIndex = Math.floor(dayDiff(baseMonday, mondayOf(c)) / 7);
750
+ if (weekIndex >= 0 && weekIndex % rule.interval === 0) {
751
+ candidate = atTime(c.getFullYear(), c.getMonth(), c.getDate());
752
+ break;
753
+ }
754
+ }
755
+ } else if (rule.freq === "month") {
756
+ const baseAbs = start.getFullYear() * 12 + start.getMonth();
757
+ const day = rule.monthDay ?? start.getDate();
758
+ const startAbs = after.getFullYear() * 12 + after.getMonth();
759
+ const rem = ((startAbs - baseAbs) % rule.interval + rule.interval) % rule.interval;
760
+ let mAbs = startAbs + (rem === 0 ? 0 : rule.interval - rem);
761
+ for (let i = 0; i <= rule.interval + 1; i++) {
762
+ const y = Math.floor(mAbs / 12);
763
+ const m0 = mAbs % 12;
764
+ const c = atTime(y, m0, Math.min(day, lastDayOfMonth(y, m0)));
765
+ if (c.getTime() > after.getTime()) {
766
+ candidate = c;
767
+ break;
768
+ }
769
+ mAbs += rule.interval;
770
+ }
771
+ } else {
772
+ const baseY = start.getFullYear();
773
+ const m = (rule.yearMonth ?? start.getMonth() + 1) - 1;
774
+ const day = rule.monthDay ?? start.getDate();
775
+ const rem = ((after.getFullYear() - baseY) % rule.interval + rule.interval) % rule.interval;
776
+ let y = after.getFullYear() + (rem === 0 ? 0 : rule.interval - rem);
777
+ for (let i = 0; i <= rule.interval + 1; i++) {
778
+ const c = atTime(y, m, Math.min(day, lastDayOfMonth(y, m)));
779
+ if (c.getTime() > after.getTime()) {
780
+ candidate = c;
781
+ break;
782
+ }
783
+ y += rule.interval;
784
+ }
785
+ }
786
+ if (!candidate) return null;
787
+ if (rule.end.kind === "until") {
788
+ const until = new Date(rule.end.until);
789
+ if (Number.isNaN(until.getTime()) || candidate.getTime() > until.getTime()) return null;
790
+ }
791
+ return candidate;
792
+ }
659
793
 
660
794
  // src/server/tasks/service.ts
661
795
  function isTaskConflictError(err) {
@@ -745,7 +879,8 @@ function createTasksService(deps) {
745
879
  attachments: input.attachments ?? [],
746
880
  ...input.agentId ? { agentId: input.agentId } : {},
747
881
  ...input.providerId ? { providerId: input.providerId } : {},
748
- ...input.model ? { model: input.model } : {}
882
+ ...input.model ? { model: input.model } : {},
883
+ ...input.schedule ? { schedule: normalizeSchedule(input.schedule) } : {}
749
884
  });
750
885
  addAuditEntry(task.id, actor.actor, "create", `Task created in To Do`, actor.actorName);
751
886
  const fresh = getTask(task.id);
@@ -756,7 +891,10 @@ function createTasksService(deps) {
756
891
  return withLock(projectId, () => {
757
892
  const task = assertOwned(projectId, taskId);
758
893
  assertNotStale(task, expectedVersion);
759
- const next = updateTask(taskId, patch) ?? getTask(taskId);
894
+ const next = updateTask(taskId, {
895
+ ...patch,
896
+ ...patch.schedule !== void 0 ? { schedule: patch.schedule === null ? null : normalizeSchedule(patch.schedule) } : {}
897
+ }) ?? getTask(taskId);
760
898
  const changed = summarizeChanges(task, next);
761
899
  addAuditEntry(taskId, actor.actor, "edit", changed || "Task updated", actor.actorName);
762
900
  const fresh = getTask(taskId);
@@ -792,87 +930,164 @@ function createTasksService(deps) {
792
930
  });
793
931
  }
794
932
  function move(projectId, taskId, to, opts) {
795
- return withLock(projectId, async () => {
796
- const task = assertOwned(projectId, taskId);
797
- assertNotStale(task, opts.expectedVersion);
798
- const from = task.status;
799
- if (from === to) {
800
- return { task };
933
+ return withLock(projectId, () => moveLocked(projectId, taskId, to, opts));
934
+ }
935
+ async function moveLocked(projectId, taskId, to, opts) {
936
+ const task = assertOwned(projectId, taskId);
937
+ assertNotStale(task, opts.expectedVersion);
938
+ const from = task.status;
939
+ if (from === to) {
940
+ return { task };
941
+ }
942
+ let sessionId;
943
+ const wasRunning = task.status === "in_progress" && task.runState === "running";
944
+ if (to === "in_progress") {
945
+ const readyGates = requiredGates(projectId, "ready");
946
+ const missing = missingGateFields(task, readyGates);
947
+ if (missing.length > 0) {
948
+ throw gateBlockedError(task, "In Progress", missing);
801
949
  }
802
- let sessionId;
803
- const wasRunning = task.status === "in_progress" && task.runState === "running";
804
- if (to === "in_progress") {
805
- const readyGates = requiredGates(projectId, "ready");
806
- const missing = missingGateFields(task, readyGates);
807
- if (missing.length > 0) {
808
- throw gateBlockedError(task, "In Progress", missing);
950
+ setTaskStatus(taskId, "in_progress");
951
+ const settings = getTaskSettings(projectId);
952
+ if (opts.actor === "agent") {
953
+ if (!opts.sessionId) {
954
+ throw new Error(
955
+ serverT({
956
+ en: "Agent moves must bind to the current session (sessionId is required)",
957
+ fr: "Les d\xE9placements d\u2019agent doivent \xEAtre li\xE9s \xE0 la session courante (sessionId requis)"
958
+ })
959
+ );
809
960
  }
810
- setTaskStatus(taskId, "in_progress");
811
- const settings = getTaskSettings(projectId);
812
- if (opts.actor === "agent") {
813
- if (!opts.sessionId) {
814
- throw new Error(
815
- serverT({
816
- en: "Agent moves must bind to the current session (sessionId is required)",
817
- fr: "Les d\xE9placements d\u2019agent doivent \xEAtre li\xE9s \xE0 la session courante (sessionId requis)"
818
- })
961
+ setTaskRunState(taskId, "running");
962
+ addTaskLink(taskId, opts.sessionId, true);
963
+ addAuditEntry(taskId, "agent", "move", `Moved to In Progress (running)`, opts.actorName);
964
+ emitReminder(opts.sessionId, reminderForInProgress(task, opts.sessionId, from));
965
+ sessionId = opts.sessionId;
966
+ } else {
967
+ const runningCount = activeCount(projectId);
968
+ const launched = runningCount < settings.slotLimit && !settings.queuePaused;
969
+ setTaskRunState(taskId, launched ? "running" : "queued");
970
+ if (task.schedule) {
971
+ clearTaskSchedule(taskId);
972
+ }
973
+ if (launched) {
974
+ const seeded = await seedSession(projectId, task, from, reusableTarget(projectId, opts.sessionId));
975
+ addTaskLink(taskId, seeded.session.id, true);
976
+ addAuditEntry(taskId, opts.actor, "move", `Moved to In Progress (running)`, opts.actorName);
977
+ sessionId = seeded.session.id;
978
+ } else {
979
+ appendToBottom(taskId, "in_progress");
980
+ const earmarked = reusableTarget(projectId, opts.sessionId);
981
+ if (earmarked) addTaskLink(taskId, earmarked, false);
982
+ addAuditEntry(taskId, opts.actor, "move", `Moved to In Progress (queued)`, opts.actorName);
983
+ }
984
+ }
985
+ } else if (to === "done") {
986
+ const doneGates = requiredGates(projectId, "done");
987
+ const missing = missingGateFields(task, doneGates);
988
+ if (missing.length > 0) {
989
+ throw gateBlockedError(task, "Done", missing);
990
+ }
991
+ setTaskStatus(taskId, "done");
992
+ clearActiveTaskLink(taskId);
993
+ if (task.schedule) clearTaskSchedule(taskId);
994
+ addAuditEntry(taskId, opts.actor, "move", `Moved to Done`, opts.actorName);
995
+ if (task.activeSessionId) {
996
+ emitReminder(task.activeSessionId, reminderForDone(task));
997
+ }
998
+ } else {
999
+ setTaskStatus(taskId, "todo");
1000
+ removeTaskLinks(taskId);
1001
+ const detailParts = [`Moved back to To Do`];
1002
+ if (opts.reason) detailParts.push(`Reason: ${opts.reason}`);
1003
+ addAuditEntry(taskId, opts.actor, "move", detailParts.join(". "), opts.actorName);
1004
+ if (task.activeSessionId) {
1005
+ emitReminder(task.activeSessionId, reminderForTodo(task));
1006
+ }
1007
+ }
1008
+ const fresh = getTask(taskId);
1009
+ let autoLaunched;
1010
+ if (wasRunning || to === "todo" && from === "in_progress") {
1011
+ autoLaunched = await maybeAutoLaunch(projectId);
1012
+ }
1013
+ publish(projectId, fresh.id, autoLaunched);
1014
+ return {
1015
+ task: fresh,
1016
+ ...sessionId ? { sessionId } : {},
1017
+ ...autoLaunched ? { autoLaunched } : {}
1018
+ };
1019
+ }
1020
+ async function runScheduled(now = /* @__PURE__ */ new Date()) {
1021
+ const due = listDueScheduledTasks(now.toISOString());
1022
+ for (const task of due) {
1023
+ try {
1024
+ await withLock(task.projectId, async () => {
1025
+ const fresh = getTask(task.id);
1026
+ if (!fresh || fresh.status !== "todo" || !fresh.schedule) return;
1027
+ const schedule = fresh.schedule;
1028
+ if (schedule.type === "once") {
1029
+ await moveLocked(fresh.projectId, fresh.id, "in_progress", { actor: "system" });
1030
+ return;
1031
+ }
1032
+ const readyGates = requiredGates(fresh.projectId, "ready");
1033
+ if (missingGateFields(fresh, readyGates).length > 0) {
1034
+ logger.warn("Scheduled recurring spawn skipped: missing ready gates", {
1035
+ taskId: fresh.id,
1036
+ gates: readyGates.map((g) => g.name).join(", ")
1037
+ });
1038
+ return;
1039
+ }
1040
+ const run = cloneTask(fresh.id, false);
1041
+ if (run) {
1042
+ try {
1043
+ await moveLocked(run.projectId, run.id, "in_progress", { actor: "system" });
1044
+ } catch (error) {
1045
+ deleteTask(run.id);
1046
+ throw error;
1047
+ }
1048
+ addAuditEntry(
1049
+ run.id,
1050
+ "system",
1051
+ "scheduled_run",
1052
+ `Spawned from recurring task "${promptLabel(fresh.prompt)}"`,
1053
+ "system"
819
1054
  );
820
1055
  }
821
- setTaskRunState(taskId, "running");
822
- addTaskLink(taskId, opts.sessionId, true);
823
- addAuditEntry(taskId, "agent", "move", `Moved to In Progress (running)`, opts.actorName);
824
- emitReminder(opts.sessionId, reminderForInProgress(task, opts.sessionId, from));
825
- sessionId = opts.sessionId;
826
- } else {
827
- const runningCount = activeCount(projectId);
828
- const launched = runningCount < settings.slotLimit && !settings.queuePaused;
829
- setTaskRunState(taskId, launched ? "running" : "queued");
830
- if (launched) {
831
- const seeded = await seedSession(projectId, task, from, reusableTarget(projectId, opts.sessionId));
832
- addTaskLink(taskId, seeded.session.id, true);
833
- addAuditEntry(taskId, "human", "move", `Moved to In Progress (running)`, opts.actorName);
834
- sessionId = seeded.session.id;
1056
+ const rule = {
1057
+ freq: schedule.freq,
1058
+ interval: schedule.interval,
1059
+ ...schedule.weekdays ? { weekdays: schedule.weekdays } : {},
1060
+ ...schedule.monthDay ? { monthDay: schedule.monthDay } : {},
1061
+ ...schedule.yearMonth ? { yearMonth: schedule.yearMonth } : {},
1062
+ startAt: schedule.startAt,
1063
+ end: schedule.end,
1064
+ occurrencesDone: schedule.occurrencesDone + 1
1065
+ };
1066
+ const next = nextOccurrence(now, rule);
1067
+ if (next) {
1068
+ const advanced = {
1069
+ ...schedule,
1070
+ occurrencesDone: schedule.occurrencesDone + 1,
1071
+ nextRunAt: next.toISOString()
1072
+ };
1073
+ setTaskSchedule(fresh.id, advanced, next.toISOString());
1074
+ addAuditEntry(
1075
+ fresh.id,
1076
+ "system",
1077
+ "scheduled_run",
1078
+ `Triggered occurrence ${schedule.occurrencesDone + 1}, next run ${next.toISOString()}`,
1079
+ "system"
1080
+ );
835
1081
  } else {
836
- appendToBottom(taskId, "in_progress");
837
- const earmarked = reusableTarget(projectId, opts.sessionId);
838
- if (earmarked) addTaskLink(taskId, earmarked, false);
839
- addAuditEntry(taskId, "human", "move", `Moved to In Progress (queued)`, opts.actorName);
1082
+ clearTaskSchedule(fresh.id);
1083
+ addAuditEntry(fresh.id, "system", "schedule_ended", "Recurrence ended \u2014 schedule cleared", "system");
840
1084
  }
841
- }
842
- } else if (to === "done") {
843
- const doneGates = requiredGates(projectId, "done");
844
- const missing = missingGateFields(task, doneGates);
845
- if (missing.length > 0) {
846
- throw gateBlockedError(task, "Done", missing);
847
- }
848
- setTaskStatus(taskId, "done");
849
- clearActiveTaskLink(taskId);
850
- addAuditEntry(taskId, opts.actor, "move", `Moved to Done`, opts.actorName);
851
- if (task.activeSessionId) {
852
- emitReminder(task.activeSessionId, reminderForDone(task));
853
- }
854
- } else {
855
- setTaskStatus(taskId, "todo");
856
- removeTaskLinks(taskId);
857
- const detailParts = [`Moved back to To Do`];
858
- if (opts.reason) detailParts.push(`Reason: ${opts.reason}`);
859
- addAuditEntry(taskId, opts.actor, "move", detailParts.join(". "), opts.actorName);
860
- if (task.activeSessionId) {
861
- emitReminder(task.activeSessionId, reminderForTodo(task));
862
- }
863
- }
864
- const fresh = getTask(taskId);
865
- let autoLaunched;
866
- if (wasRunning || to === "todo" && from === "in_progress") {
867
- autoLaunched = await maybeAutoLaunch(projectId);
1085
+ publish(fresh.projectId, fresh.id);
1086
+ });
1087
+ } catch (error) {
1088
+ logger.warn("Scheduled task trigger failed", { taskId: task.id, error: String(error) });
868
1089
  }
869
- publish(projectId, fresh.id, autoLaunched);
870
- return {
871
- task: fresh,
872
- ...sessionId ? { sessionId } : {},
873
- ...autoLaunched ? { autoLaunched } : {}
874
- };
875
- });
1090
+ }
876
1091
  }
877
1092
  function setGateValue2(projectId, taskId, gateId, value, actor, sessionId, expectedVersion) {
878
1093
  return withLock(projectId, () => {
@@ -1106,7 +1321,105 @@ ${evidenceLine}`,
1106
1321
  setGateConfig,
1107
1322
  setSettings,
1108
1323
  reorder,
1109
- counts
1324
+ counts,
1325
+ runScheduled
1326
+ };
1327
+ }
1328
+ function normalizeSchedule(input) {
1329
+ if (input.type === "once") {
1330
+ if (!Number.isFinite(new Date(input.runAt).getTime())) {
1331
+ throw new Error(
1332
+ serverT(
1333
+ { en: "Invalid schedule time: {{time}}", fr: "Heure de planification invalide : {{time}}" },
1334
+ {
1335
+ time: input.runAt
1336
+ }
1337
+ )
1338
+ );
1339
+ }
1340
+ return { type: "once", runAt: input.runAt };
1341
+ }
1342
+ const { freq, interval, startAt, end } = input;
1343
+ if (!Number.isInteger(interval) || interval < 1) {
1344
+ throw new Error(
1345
+ serverT({
1346
+ en: "Recurring interval must be a positive integer",
1347
+ fr: "L\u2019intervalle de r\xE9currence doit \xEAtre un entier positif"
1348
+ })
1349
+ );
1350
+ }
1351
+ if (freq === "week") {
1352
+ const days = input.weekdays ?? [];
1353
+ if (days.length === 0 || !days.every((d) => Number.isInteger(d) && d >= 0 && d <= 6)) {
1354
+ throw new Error(
1355
+ serverT({
1356
+ en: "Weekly recurrence requires at least one selected weekday",
1357
+ fr: "La r\xE9currence hebdomadaire requiert au moins un jour de la semaine s\xE9lectionn\xE9"
1358
+ })
1359
+ );
1360
+ }
1361
+ }
1362
+ if ((freq === "month" || freq === "year") && input.monthDay !== void 0) {
1363
+ if (!Number.isInteger(input.monthDay) || input.monthDay < 1 || input.monthDay > 31) {
1364
+ throw new Error(
1365
+ serverT({
1366
+ en: "Day of month must be between 1 and 31",
1367
+ fr: "Le jour du mois doit \xEAtre compris entre 1 et 31"
1368
+ })
1369
+ );
1370
+ }
1371
+ }
1372
+ if (freq === "year" && input.yearMonth !== void 0) {
1373
+ if (!Number.isInteger(input.yearMonth) || input.yearMonth < 1 || input.yearMonth > 12) {
1374
+ throw new Error(
1375
+ serverT({
1376
+ en: "Month must be between 1 and 12",
1377
+ fr: "Le mois doit \xEAtre compris entre 1 et 12"
1378
+ })
1379
+ );
1380
+ }
1381
+ }
1382
+ if (!Number.isFinite(new Date(startAt).getTime())) {
1383
+ throw new Error(
1384
+ serverT(
1385
+ {
1386
+ en: "Invalid schedule start time: {{time}}",
1387
+ fr: "Heure de d\xE9part de planification invalide : {{time}}"
1388
+ },
1389
+ { time: startAt }
1390
+ )
1391
+ );
1392
+ }
1393
+ if (end.kind === "until" && !Number.isFinite(new Date(end.until).getTime())) {
1394
+ throw new Error(
1395
+ serverT(
1396
+ {
1397
+ en: "Invalid recurrence end date: {{time}}",
1398
+ fr: "Date de fin de r\xE9currence invalide : {{time}}"
1399
+ },
1400
+ { time: end.until }
1401
+ )
1402
+ );
1403
+ }
1404
+ if (end.kind === "count" && (!Number.isInteger(end.count) || end.count < 1)) {
1405
+ throw new Error(
1406
+ serverT({
1407
+ en: "Occurrence count must be a positive integer",
1408
+ fr: "Le nombre d\u2019occurrences doit \xEAtre un entier positif"
1409
+ })
1410
+ );
1411
+ }
1412
+ return {
1413
+ type: "recurring",
1414
+ freq,
1415
+ interval,
1416
+ ...input.weekdays ? { weekdays: input.weekdays } : {},
1417
+ ...input.monthDay !== void 0 ? { monthDay: input.monthDay } : {},
1418
+ ...input.yearMonth !== void 0 ? { yearMonth: input.yearMonth } : {},
1419
+ startAt,
1420
+ end,
1421
+ occurrencesDone: 0,
1422
+ nextRunAt: startAt
1110
1423
  };
1111
1424
  }
1112
1425
  function promptLabel(prompt) {
@@ -1149,4 +1462,4 @@ export {
1149
1462
  isTaskGateError,
1150
1463
  createTasksService
1151
1464
  };
1152
- //# sourceMappingURL=chunk-TJOKDC34.js.map
1465
+ //# sourceMappingURL=chunk-CSUMZAYY.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  SETTINGS_KEYS,
3
3
  getSetting
4
- } from "./chunk-EMXMTCSY.js";
4
+ } from "./chunk-SKNPOZYP.js";
5
5
  import {
6
6
  logger
7
7
  } from "./chunk-K44MW7JJ.js";
@@ -194,4 +194,4 @@ export {
194
194
  ChatHttpClient,
195
195
  OpenAIHttpClient
196
196
  };
197
- //# sourceMappingURL=chunk-EE6VFEE6.js.map
197
+ //# sourceMappingURL=chunk-DNL2NUDB.js.map
@@ -11,11 +11,11 @@ import {
11
11
  } from "./chunk-I72ACQ6R.js";
12
12
  import {
13
13
  getPlatformShell
14
- } from "./chunk-46Q2SHGJ.js";
14
+ } from "./chunk-MEPTCUYN.js";
15
15
  import {
16
16
  SETTINGS_KEYS,
17
17
  getSetting
18
- } from "./chunk-EMXMTCSY.js";
18
+ } from "./chunk-SKNPOZYP.js";
19
19
 
20
20
  // src/server/utils/shell.ts
21
21
  import { spawn } from "child_process";
@@ -214,4 +214,4 @@ export {
214
214
  getSessionProcesses2 as getSessionProcesses,
215
215
  getProcessLogs
216
216
  };
217
- //# sourceMappingURL=chunk-UA2ATO3B.js.map
217
+ //# sourceMappingURL=chunk-DQEANXXF.js.map
@@ -3,11 +3,11 @@ import {
3
3
  } from "./chunk-OSXPQSLW.js";
4
4
  import {
5
5
  getProjectDefaultAgent
6
- } from "./chunk-DPFET73G.js";
6
+ } from "./chunk-JYYBSODN.js";
7
7
  import {
8
8
  SETTINGS_KEYS,
9
9
  getSetting
10
- } from "./chunk-EMXMTCSY.js";
10
+ } from "./chunk-SKNPOZYP.js";
11
11
  import {
12
12
  logger
13
13
  } from "./chunk-K44MW7JJ.js";
@@ -352,4 +352,4 @@ export {
352
352
  getOverrideAgentIds,
353
353
  resolveDefaultAgentId
354
354
  };
355
- //# sourceMappingURL=chunk-4F4ARGAM.js.map
355
+ //# sourceMappingURL=chunk-DRIR2IZR.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resolveDefaultAgentId
3
- } from "./chunk-4F4ARGAM.js";
3
+ } from "./chunk-DRIR2IZR.js";
4
4
  import {
5
5
  normalizeAskOptions
6
6
  } from "./chunk-LCLH6ZUL.js";
@@ -983,4 +983,4 @@ export {
983
983
  buildSnapshot,
984
984
  buildSnapshotFromSessionState
985
985
  };
986
- //# sourceMappingURL=chunk-AE2IBEUH.js.map
986
+ //# sourceMappingURL=chunk-EFEHIJXF.js.map
@@ -0,0 +1,7 @@
1
+ // src/constants.ts
2
+ var VERSION = "2.0.142";
3
+
4
+ export {
5
+ VERSION
6
+ };
7
+ //# sourceMappingURL=chunk-EPZO3MGM.js.map