@oxgeneral/orch 0.2.3 → 0.3.0

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 (145) hide show
  1. package/dist/App-TW35IULR.js +18 -0
  2. package/dist/agent-FRQKL7YI.js +9 -0
  3. package/dist/{orchestrator-VGYKSOZJ.js → chunk-2UC4SVJB.js} +236 -71
  4. package/dist/chunk-2UC4SVJB.js.map +1 -0
  5. package/dist/chunk-5AJ4LYO5.js +8 -0
  6. package/dist/{chunk-45K2XID7.js → chunk-6DWHQPTE.js} +2 -1
  7. package/dist/chunk-6DWHQPTE.js.map +1 -0
  8. package/dist/{chunk-POUC4CPC.js → chunk-6MJ7V6VY.js} +2 -2
  9. package/dist/{chunk-HNKJ4IF7.js → chunk-B4JQM4NU.js} +34 -10
  10. package/dist/chunk-B4JQM4NU.js.map +1 -0
  11. package/dist/{chunk-6HENRUYZ.js → chunk-CDFA4IIQ.js} +2 -2
  12. package/dist/chunk-CHRW4CLD.js +2 -0
  13. package/dist/{chunk-ZU6AY2VU.js → chunk-GZ2Q56YZ.js} +2 -2
  14. package/dist/chunk-HMMPM7MF.js +3 -0
  15. package/dist/{chunk-AELEEEV3.js → chunk-HSBYJ5C5.js} +27 -7
  16. package/dist/chunk-HXOMNULD.js +2 -0
  17. package/dist/{chunk-O5AO5QIR.js → chunk-IQXRQBUK.js} +9 -2
  18. package/dist/chunk-IQXRQBUK.js.map +1 -0
  19. package/dist/chunk-L26TK7Y5.js +2 -0
  20. package/dist/chunk-L3FYR45M.js +2 -0
  21. package/dist/chunk-LXNRCJ22.js +2 -0
  22. package/dist/{chunk-TX7WOFCW.js → chunk-MGFMVPRD.js} +4 -7
  23. package/dist/chunk-MGFMVPRD.js.map +1 -0
  24. package/dist/chunk-MNXU3KCD.js +2 -0
  25. package/dist/{chunk-CHIP7O6V.js → chunk-O2MSGW3V.js} +3 -1
  26. package/dist/chunk-O2MSGW3V.js.map +1 -0
  27. package/dist/chunk-PJ5DKXGR.js +2 -0
  28. package/dist/{chunk-VTA74YWX.js → chunk-QEEM67OA.js} +11 -17
  29. package/dist/chunk-QEEM67OA.js.map +1 -0
  30. package/dist/chunk-UMZEA3JT.js +5 -0
  31. package/dist/{shell-OGTSH4RJ.js → chunk-UW6GUUE6.js} +3 -3
  32. package/dist/chunk-XDVMX2FO.js +8 -0
  33. package/dist/chunk-XDVMX2FO.js.map +1 -0
  34. package/dist/chunk-ZA5Z33GO.js +11 -0
  35. package/dist/claude-E36EGXUV.js +2 -0
  36. package/dist/{chunk-IRN2U2NE.js → claude-RIB3RQS5.js} +5 -2
  37. package/dist/claude-RIB3RQS5.js.map +1 -0
  38. package/dist/cli.js +1 -199
  39. package/dist/clipboard-service-PDTSZIR5.js +25 -0
  40. package/dist/codex-OTZKVESD.js +2 -0
  41. package/dist/{codex-U7LTJTX6.js → codex-VBUSA2GJ.js} +5 -3
  42. package/dist/codex-VBUSA2GJ.js.map +1 -0
  43. package/dist/config-CCSS2P7R.js +2 -0
  44. package/dist/container-OIXLFSX2.js +6 -0
  45. package/dist/context-GSMQHQES.js +7 -0
  46. package/dist/cursor-3DJA6LWS.js +2 -0
  47. package/dist/{cursor-3DI5GKRF.js → cursor-4QIOTDBW.js} +5 -3
  48. package/dist/cursor-4QIOTDBW.js.map +1 -0
  49. package/dist/doctor-KBK5JZBZ.js +2 -0
  50. package/dist/doctor-service-F2SXDWHS.js +91 -0
  51. package/dist/doctor-service-F2SXDWHS.js.map +1 -0
  52. package/dist/doctor-service-PB7YBH3F.js +2 -0
  53. package/dist/goal-RFKFPR7M.js +8 -0
  54. package/dist/index.d.ts +124 -46
  55. package/dist/index.js +1817 -5
  56. package/dist/index.js.map +1 -1
  57. package/dist/init-WRDFAFS2.js +53 -0
  58. package/dist/logs-5QHJWMEG.js +12 -0
  59. package/dist/msg-4SCLBO4K.js +9 -0
  60. package/dist/orchestrator-FGGXK3N3.js +5 -0
  61. package/dist/{orchestrator-TAFBYQQ5.js.map → orchestrator-FGGXK3N3.js.map} +1 -1
  62. package/dist/orchestrator-R7IWZUT6.js +13 -0
  63. package/dist/process-manager-33H27MQF.js +2 -0
  64. package/dist/process-manager-A36Y7LHP.js +3 -0
  65. package/dist/{process-manager-TLZOTO4Y.js.map → process-manager-A36Y7LHP.js.map} +1 -1
  66. package/dist/registry-BO2PPRNG.js +2 -0
  67. package/dist/registry-JXXRLJ5J.js +3 -0
  68. package/dist/{registry-UQAHK77P.js.map → registry-JXXRLJ5J.js.map} +1 -1
  69. package/dist/run-HSHRELOP.js +3 -0
  70. package/dist/shell-EOJBDWTH.js +2 -0
  71. package/dist/{chunk-CIIE6LNG.js → shell-IH2MMTVP.js} +3 -2
  72. package/dist/shell-IH2MMTVP.js.map +1 -0
  73. package/dist/status-DLBNWSWM.js +2 -0
  74. package/dist/task-J6ZN7ALI.js +20 -0
  75. package/dist/team-MSIBKOQC.js +4 -0
  76. package/dist/template-engine-MFL5B677.js +3 -0
  77. package/dist/{template-engine-322SCRR6.js.map → template-engine-MFL5B677.js.map} +1 -1
  78. package/dist/template-engine-ONIDVD4F.js +2 -0
  79. package/dist/tui-G4XUFAIP.js +2 -0
  80. package/dist/update-PC2ENCKU.js +2 -0
  81. package/dist/update-check-HGMBDYHL.js +2 -0
  82. package/dist/workspace-manager-KOOYTO7E.js +3 -0
  83. package/dist/{workspace-manager-47KI7B27.js → workspace-manager-T6AXG7XL.js} +40 -3
  84. package/dist/workspace-manager-T6AXG7XL.js.map +1 -0
  85. package/package.json +2 -1
  86. package/readme.md +5 -4
  87. package/scripts/benchmark.ts +304 -0
  88. package/dist/App-KDZSTAMR.js +0 -4864
  89. package/dist/agent-V5M2C3OC.js +0 -157
  90. package/dist/chunk-2B32FPEB.js +0 -11
  91. package/dist/chunk-2B32FPEB.js.map +0 -1
  92. package/dist/chunk-33QNTNR6.js +0 -46
  93. package/dist/chunk-6GFVB6EK.js +0 -101
  94. package/dist/chunk-6HENRUYZ.js.map +0 -1
  95. package/dist/chunk-AELEEEV3.js.map +0 -1
  96. package/dist/chunk-E3TCKHU6.js +0 -13
  97. package/dist/chunk-E3TCKHU6.js.map +0 -1
  98. package/dist/chunk-ED47GL3F.js +0 -29
  99. package/dist/chunk-HXYAZGLP.js +0 -15
  100. package/dist/chunk-I5WEMARW.js +0 -166
  101. package/dist/chunk-IZYSGYXG.js +0 -2
  102. package/dist/chunk-IZYSGYXG.js.map +0 -1
  103. package/dist/chunk-P6ATSXGL.js +0 -107
  104. package/dist/chunk-PBFE5V3G.js +0 -2
  105. package/dist/chunk-PBFE5V3G.js.map +0 -1
  106. package/dist/chunk-PNE6LQRF.js +0 -5
  107. package/dist/chunk-POUC4CPC.js.map +0 -1
  108. package/dist/chunk-XI4TU6VU.js +0 -50
  109. package/dist/chunk-ZU6AY2VU.js.map +0 -1
  110. package/dist/claude-GH6P2DC5.js +0 -4
  111. package/dist/claude-S47YTIHU.js +0 -2
  112. package/dist/claude-S47YTIHU.js.map +0 -1
  113. package/dist/codex-2CH57B7G.js +0 -2
  114. package/dist/codex-2CH57B7G.js.map +0 -1
  115. package/dist/config-LJFM55LN.js +0 -75
  116. package/dist/container-JV7TAUP5.js +0 -1532
  117. package/dist/context-EPSDCJTU.js +0 -83
  118. package/dist/cursor-QFUNKPCQ.js +0 -2
  119. package/dist/cursor-QFUNKPCQ.js.map +0 -1
  120. package/dist/doctor-IO4PV4D6.js +0 -67
  121. package/dist/doctor-service-A34DHPKI.js +0 -2
  122. package/dist/doctor-service-NTWBWOM2.js +0 -2
  123. package/dist/doctor-service-NTWBWOM2.js.map +0 -1
  124. package/dist/goal-I56QP7HS.js +0 -110
  125. package/dist/init-BE5VKWOM.js +0 -149
  126. package/dist/logs-IAUAS5TX.js +0 -207
  127. package/dist/msg-SQWQLJP6.js +0 -95
  128. package/dist/orchestrator-TAFBYQQ5.js +0 -2
  129. package/dist/process-manager-HUVNAPQV.js +0 -2
  130. package/dist/process-manager-TLZOTO4Y.js +0 -2
  131. package/dist/registry-PQWRVNF2.js +0 -2
  132. package/dist/registry-UQAHK77P.js +0 -2
  133. package/dist/run-PSZURVVL.js +0 -95
  134. package/dist/shell-5ZNXFGXV.js +0 -3
  135. package/dist/shell-OGTSH4RJ.js.map +0 -1
  136. package/dist/status-DTF7D3DV.js +0 -56
  137. package/dist/task-5OJTXW27.js +0 -209
  138. package/dist/team-AISPLEJB.js +0 -97
  139. package/dist/template-engine-322SCRR6.js +0 -2
  140. package/dist/template-engine-3CDRZNMJ.js +0 -3
  141. package/dist/tui-XDJE3IUA.js +0 -225
  142. package/dist/update-72GZMF65.js +0 -64
  143. package/dist/update-check-4RV7Z6WT.js +0 -2
  144. package/dist/workspace-manager-7M46ESUL.js +0 -2
  145. package/dist/workspace-manager-7M46ESUL.js.map +0 -1
@@ -1,12 +1,57 @@
1
- #!/usr/bin/env node
2
- import { DEFAULT_PROMPT_TEMPLATE, buildPromptContext } from './chunk-HNKJ4IF7.js';
3
- import { resolveFailureStatus, isDispatchable, isTerminal, isBlocked, resolveCompletionStatus, calculateRetryDelay } from './chunk-33QNTNR6.js';
4
- import { AUTONOMOUS_LABEL } from './chunk-PNE6LQRF.js';
5
- import { LockConflictError, TaskAlreadyRunningError, NoAgentsError } from './chunk-O5AO5QIR.js';
1
+ import { LockConflictError, WorkspaceError, TaskAlreadyRunningError, NoAgentsError } from './chunk-IQXRQBUK.js';
2
+ import { AUTONOMOUS_LABEL, DEFAULT_PROMPT_TEMPLATE, buildPromptContext } from './chunk-B4JQM4NU.js';
6
3
  import { dirname } from 'path';
7
4
  import fs from 'fs/promises';
8
5
  import { execFile } from 'child_process';
9
6
 
7
+ // src/domain/transitions.ts
8
+ var VALID_TRANSITIONS = {
9
+ todo: ["in_progress", "cancelled"],
10
+ in_progress: ["review", "retrying", "failed", "cancelled"],
11
+ retrying: ["in_progress", "failed", "cancelled"],
12
+ review: ["done", "todo", "cancelled"],
13
+ done: [],
14
+ failed: ["todo", "retrying"],
15
+ cancelled: ["todo"]
16
+ };
17
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["done", "failed", "cancelled"]);
18
+ function canTransition(from, to) {
19
+ return VALID_TRANSITIONS[from].includes(to);
20
+ }
21
+ function isTerminal(status) {
22
+ return TERMINAL_STATUSES.has(status);
23
+ }
24
+ function isDispatchable(status) {
25
+ return status === "todo" || status === "retrying";
26
+ }
27
+ function isBlocked(task, allTasks) {
28
+ if (task.depends_on.length === 0) return false;
29
+ if (allTasks instanceof Map) {
30
+ return task.depends_on.some((depId) => {
31
+ const dep = allTasks.get(depId);
32
+ return !dep || dep.status !== "done";
33
+ });
34
+ }
35
+ return task.depends_on.some((depId) => {
36
+ const dep = allTasks.find((t) => t.id === depId);
37
+ return !dep || dep.status !== "done";
38
+ });
39
+ }
40
+ function resolveFailureStatus(task) {
41
+ if (task.attempts < task.max_attempts) {
42
+ return "retrying";
43
+ }
44
+ return "failed";
45
+ }
46
+ function resolveCompletionStatus(task, success, _autoApprove) {
47
+ {
48
+ return "review";
49
+ }
50
+ }
51
+ function calculateRetryDelay(attempt, baseDelayMs, maxDelayMs) {
52
+ const delay = baseDelayMs * Math.pow(2, attempt);
53
+ return Math.min(delay, maxDelayMs);
54
+ }
10
55
  function scopesOverlap(a, b) {
11
56
  if (!a?.length || !b?.length) return false;
12
57
  for (const pa of a) {
@@ -16,6 +61,54 @@ function scopesOverlap(a, b) {
16
61
  }
17
62
  return false;
18
63
  }
64
+ function computePatternInfo(pattern) {
65
+ const base = pattern.split("*")[0];
66
+ const isFile = !base.endsWith("/");
67
+ const dir = isFile ? dirname(base) : "";
68
+ return { raw: pattern, base, isFile, dir };
69
+ }
70
+ var ScopeIndex = class {
71
+ entries;
72
+ constructor(scopes) {
73
+ this.entries = [];
74
+ for (const scope of scopes) {
75
+ if (scope?.length) {
76
+ for (const p of scope) {
77
+ this.entries.push(computePatternInfo(p));
78
+ }
79
+ }
80
+ }
81
+ }
82
+ /** Returns true if the given scope overlaps with any pattern in the index. */
83
+ overlapsAny(scope) {
84
+ if (!scope?.length || this.entries.length === 0) return false;
85
+ for (const raw of scope) {
86
+ const info = computePatternInfo(raw);
87
+ for (const entry of this.entries) {
88
+ if (patternsOverlapInfo(info, entry)) return true;
89
+ }
90
+ }
91
+ return false;
92
+ }
93
+ /** Add patterns to the index (e.g. from an approved candidate). */
94
+ add(scope) {
95
+ if (!scope?.length) return;
96
+ for (const p of scope) {
97
+ this.entries.push(computePatternInfo(p));
98
+ }
99
+ }
100
+ get size() {
101
+ return this.entries.length;
102
+ }
103
+ };
104
+ function patternsOverlapInfo(a, b) {
105
+ if (a.raw === b.raw) return true;
106
+ if (a.base.startsWith(b.base) || b.base.startsWith(a.base)) return true;
107
+ if (a.isFile && b.isFile) {
108
+ return a.dir === b.dir && a.dir !== ".";
109
+ }
110
+ return false;
111
+ }
19
112
  function patternsOverlap(a, b) {
20
113
  if (a === b) return true;
21
114
  const aBase = a.split("*")[0];
@@ -28,29 +121,37 @@ function patternsOverlap(a, b) {
28
121
  }
29
122
  return false;
30
123
  }
124
+ var acquireMutex = Promise.resolve();
31
125
  async function acquireLock(lockPath) {
32
- const bakPath = lockPath + ".bak";
126
+ let release;
127
+ const gate = new Promise((r) => {
128
+ release = r;
129
+ });
130
+ const prev = acquireMutex;
131
+ acquireMutex = gate;
132
+ await prev;
133
+ try {
134
+ return await doAcquire(lockPath);
135
+ } finally {
136
+ release();
137
+ }
138
+ }
139
+ async function doAcquire(lockPath) {
33
140
  const existing = await readLockPid(lockPath);
34
141
  if (existing !== null) {
35
142
  if (isProcessAlive(existing)) {
36
143
  return { acquired: false, pid: existing };
37
144
  }
38
- try {
39
- await fs.rename(lockPath, bakPath);
40
- } catch {
41
- }
145
+ await fs.unlink(lockPath).catch(() => {
146
+ });
42
147
  }
43
148
  try {
44
149
  const fd = await fs.open(lockPath, "wx");
45
150
  await fd.writeFile(String(process.pid), "utf-8");
46
151
  await fd.close();
47
- await fs.unlink(bakPath).catch(() => {
48
- });
49
152
  return { acquired: true, pid: process.pid };
50
153
  } catch (err) {
51
154
  if (err.code === "EEXIST") {
52
- await fs.rename(bakPath, lockPath).catch(() => {
53
- });
54
155
  const pid = await readLockPid(lockPath);
55
156
  return { acquired: false, pid: pid ?? void 0 };
56
157
  }
@@ -87,7 +188,7 @@ var CachedTaskStore = class {
87
188
  }
88
189
  cache = /* @__PURE__ */ new Map();
89
190
  async list(filter) {
90
- const key = filter?.status ?? "__all__";
191
+ const key = filter ? `${filter.status ?? ""}:${filter.goalId ?? ""}` : "__all__";
91
192
  if (this.cache.has(key)) {
92
193
  return this.cache.get(key);
93
194
  }
@@ -115,6 +216,7 @@ var CachedAgentStore = class {
115
216
  this.inner = inner;
116
217
  }
117
218
  listCache = null;
219
+ nameCache = /* @__PURE__ */ new Map();
118
220
  async list() {
119
221
  if (this.listCache) {
120
222
  return this.listCache;
@@ -127,18 +229,26 @@ var CachedAgentStore = class {
127
229
  return this.inner.get(id);
128
230
  }
129
231
  async getByName(name) {
130
- return this.inner.getByName(name);
232
+ if (this.nameCache.has(name)) {
233
+ return this.nameCache.get(name) ?? null;
234
+ }
235
+ const result = await this.inner.getByName(name);
236
+ this.nameCache.set(name, result);
237
+ return result;
131
238
  }
132
239
  async save(agent) {
133
240
  await this.inner.save(agent);
134
241
  this.listCache = null;
242
+ this.nameCache.clear();
135
243
  }
136
244
  async delete(id) {
137
245
  await this.inner.delete(id);
138
246
  this.listCache = null;
247
+ this.nameCache.clear();
139
248
  }
140
249
  invalidate() {
141
250
  this.listCache = null;
251
+ this.nameCache.clear();
142
252
  }
143
253
  };
144
254
  var CachedGoalStore = class {
@@ -285,21 +395,40 @@ var Orchestrator = class {
285
395
  });
286
396
  }
287
397
  /**
288
- * Run a single task by ID. Acquires lock for the duration of the run.
398
+ * Run a single task by ID.
399
+ * If watch mode is active (lock already held), dispatches inline via stateMutex.
400
+ * Otherwise acquires a temporary lock for the duration of the run.
289
401
  */
290
402
  async runTask(taskId) {
291
- await this.withTemporaryLock(async () => {
292
- await this.loadState();
293
- await this.dispatchTask(taskId);
294
- });
403
+ if (this.lockAcquired) {
404
+ await this.freshDispatch(() => this.dispatchTask(taskId));
405
+ return;
406
+ }
407
+ await this.withTemporaryLock(() => this.freshDispatch(() => this.dispatchTask(taskId)));
295
408
  }
296
409
  /**
297
- * Run all dispatchable tasks. Acquires lock for the duration of the run.
410
+ * Run all dispatchable tasks.
411
+ * If watch mode is active (lock already held), dispatches inline via stateMutex.
412
+ * Otherwise acquires a temporary lock for the duration of the run.
298
413
  */
299
414
  async runAll() {
300
- await this.withTemporaryLock(async () => {
415
+ if (this.lockAcquired) {
416
+ await this.freshDispatch(() => this.dispatchAll());
417
+ return;
418
+ }
419
+ await this.withTemporaryLock(() => this.freshDispatch(() => this.dispatchAll()));
420
+ }
421
+ /**
422
+ * Invalidate caches → loadState → run dispatch fn → saveState.
423
+ * Shared by runTask, runAll, and immediateDispatch.
424
+ */
425
+ async freshDispatch(fn) {
426
+ await this.withStateLock(async () => {
427
+ this.cachedTaskStore.invalidate();
428
+ this.cachedAgentStore.invalidate();
301
429
  await this.loadState();
302
- await this.dispatchAll();
430
+ await fn();
431
+ await this.saveState();
303
432
  });
304
433
  }
305
434
  /**
@@ -427,7 +556,7 @@ var Orchestrator = class {
427
556
  await this.deps.agentService.setStatus(entry.agent_id, "idle");
428
557
  }
429
558
  this.state.running = {};
430
- this.state.claimed = [];
559
+ this.state.claimed = /* @__PURE__ */ new Set();
431
560
  this.state.pid = void 0;
432
561
  this.state.started_at = void 0;
433
562
  await this.saveState();
@@ -539,13 +668,18 @@ var Orchestrator = class {
539
668
  /**
540
669
  * Schedule an immediate dispatch with 500ms debounce.
541
670
  * Called on task:created to avoid waiting for the next 30s tick.
671
+ * Retries up to 10 times (5s) if a tick is in progress.
542
672
  */
543
- scheduleImmediateDispatch() {
673
+ scheduleImmediateDispatch(retries = 0) {
544
674
  if (this.shuttingDown) return;
545
675
  if (this.immediateDispatchTimer) return;
546
676
  this.immediateDispatchTimer = setTimeout(() => {
547
677
  this.immediateDispatchTimer = null;
548
- if (this.shuttingDown || this.tickInProgress) return;
678
+ if (this.shuttingDown) return;
679
+ if (this.tickInProgress) {
680
+ if (retries < 10) this.scheduleImmediateDispatch(retries + 1);
681
+ return;
682
+ }
549
683
  this.immediateDispatch().catch((err) => {
550
684
  this.deps.eventBus.emit({
551
685
  type: "orchestrator:error",
@@ -562,14 +696,7 @@ var Orchestrator = class {
562
696
  */
563
697
  async immediateDispatch() {
564
698
  if (this.shuttingDown) return;
565
- await this.withStateLock(async () => {
566
- if (this.shuttingDown) return;
567
- this.cachedTaskStore.invalidate();
568
- this.cachedAgentStore.invalidate();
569
- await this.loadState();
570
- await this.dispatchAll();
571
- await this.saveState();
572
- });
699
+ await this.freshDispatch(() => this.shuttingDown ? Promise.resolve() : this.dispatchAll());
573
700
  }
574
701
  /**
575
702
  * Reconcile: check PID liveness, detect stalls, process retry queue.
@@ -577,8 +704,13 @@ var Orchestrator = class {
577
704
  async reconcile() {
578
705
  const state = this.state;
579
706
  const now = Date.now();
580
- for (const [taskId, entry] of Object.entries(state.running)) {
581
- const taskData = await this.deps.taskStore.get(taskId);
707
+ const runningEntries = Object.entries(state.running);
708
+ const runningTaskData = await Promise.all(
709
+ runningEntries.map(([taskId]) => this.deps.taskStore.get(taskId))
710
+ );
711
+ for (let i = 0; i < runningEntries.length; i++) {
712
+ const [taskId, entry] = runningEntries[i];
713
+ const taskData = runningTaskData[i];
582
714
  if (!taskData || isTerminal(taskData.status)) {
583
715
  this.abortControllers.delete(taskId);
584
716
  delete state.running[taskId];
@@ -647,12 +779,16 @@ var Orchestrator = class {
647
779
  });
648
780
  }
649
781
  }
650
- for (let i = state.retry_queue.length - 1; i >= 0; i--) {
651
- const retry = state.retry_queue[i];
782
+ const dueRetries = [];
783
+ state.retry_queue = state.retry_queue.filter((retry) => {
652
784
  if (now >= new Date(retry.due_at).getTime()) {
653
- state.retry_queue.splice(i, 1);
654
- await this.dispatchTask(retry.task_id);
785
+ dueRetries.push(retry.task_id);
786
+ return false;
655
787
  }
788
+ return true;
789
+ });
790
+ for (const taskId of dueRetries) {
791
+ await this.dispatchTask(taskId);
656
792
  }
657
793
  await this.saveState();
658
794
  }
@@ -697,7 +833,8 @@ Agent role: ${role}` : `Autonomous work cycle. Agent role: ${role}`;
697
833
  description,
698
834
  assignee: agent.id,
699
835
  labels: [AUTONOMOUS_LABEL],
700
- priority: 3
836
+ priority: 3,
837
+ goalId: goal?.id
701
838
  });
702
839
  anyCreated = true;
703
840
  } catch (err) {
@@ -721,8 +858,9 @@ Agent role: ${role}` : `Autonomous work cycle. Agent role: ${role}`;
721
858
  const availableSlots = maxConcurrent - currentRunning;
722
859
  if (availableSlots <= 0) return;
723
860
  const allTasks = await this.cachedTaskStore.list();
861
+ const taskMap = new Map(allTasks.map((t) => [t.id, t]));
724
862
  const candidates = allTasks.filter(
725
- (t) => isDispatchable(t.status) && !isBlocked(t, allTasks) && !state.running[t.id] && !state.claimed.includes(t.id)
863
+ (t) => isDispatchable(t.status) && !isBlocked(t, taskMap) && !state.running[t.id] && !state.claimed.has(t.id)
726
864
  ).sort((a, b) => {
727
865
  const bTime = b.updated_at ?? "";
728
866
  const aTime = a.updated_at ?? "";
@@ -730,31 +868,38 @@ Agent role: ${role}` : `Autonomous work cycle. Agent role: ${role}`;
730
868
  }).slice(0, availableSlots);
731
869
  const blockedIds = /* @__PURE__ */ new Set();
732
870
  const inProgressScoped = allTasks.filter((t) => t.status === "in_progress" && t.scope?.length);
733
- for (let i = 0; i < candidates.length; i++) {
734
- const candidate = candidates[i];
871
+ const scopeIndex = new ScopeIndex(inProgressScoped.map((t) => t.scope));
872
+ for (const candidate of candidates) {
735
873
  if (!candidate.scope?.length) continue;
736
- const approvedPeers = candidates.slice(0, i).filter((c) => !blockedIds.has(c.id));
737
- const compareTo = [...inProgressScoped, ...approvedPeers];
738
- let overlapping = false;
739
- for (const other of compareTo) {
740
- if (scopesOverlap(candidate.scope, other.scope)) {
741
- this.deps.eventBus.emit({
742
- type: "task:scope_overlap",
743
- taskId: candidate.id,
744
- overlappingTaskId: other.id,
745
- patterns: candidate.scope
746
- });
747
- overlapping = true;
748
- break;
749
- }
874
+ if (scopeIndex.overlapsAny(candidate.scope)) {
875
+ const overlapper = inProgressScoped.find((t) => scopesOverlap(candidate.scope, t.scope));
876
+ this.deps.eventBus.emit({
877
+ type: "task:scope_overlap",
878
+ taskId: candidate.id,
879
+ overlappingTaskId: overlapper?.id ?? candidate.id,
880
+ patterns: candidate.scope
881
+ });
882
+ blockedIds.add(candidate.id);
883
+ } else {
884
+ scopeIndex.add(candidate.scope);
750
885
  }
751
- if (overlapping) blockedIds.add(candidate.id);
752
886
  }
753
887
  for (const task of candidates) {
754
888
  if (blockedIds.has(task.id)) continue;
755
889
  try {
756
890
  await this.dispatchTask(task.id);
757
891
  } catch (err) {
892
+ if (err instanceof WorkspaceError) {
893
+ try {
894
+ const t = await this.deps.taskStore.get(task.id);
895
+ if (t && !isTerminal(t.status)) {
896
+ t.status = "failed";
897
+ t.updated_at = (/* @__PURE__ */ new Date()).toISOString();
898
+ await this.deps.taskStore.save(t);
899
+ }
900
+ } catch {
901
+ }
902
+ }
758
903
  this.deps.eventBus.emit({
759
904
  type: "orchestrator:error",
760
905
  error: err instanceof Error ? err.message : String(err),
@@ -774,13 +919,13 @@ Agent role: ${role}` : `Autonomous work cycle. Agent role: ${role}`;
774
919
  throw new TaskAlreadyRunningError(taskId, entry.run_id, entry.agent_id);
775
920
  }
776
921
  const task = await this.deps.taskService.get(taskId);
777
- state.claimed.push(taskId);
922
+ state.claimed.add(taskId);
778
923
  await this.saveState();
779
924
  try {
925
+ const allAgents = await this.cachedAgentStore.list();
780
926
  const agent = await this.deps.agentService.findBestAgent(task);
781
927
  if (!agent) {
782
- const allAgents2 = await this.cachedAgentStore.list();
783
- if (allAgents2.length === 0) {
928
+ if (allAgents.length === 0) {
784
929
  throw new NoAgentsError();
785
930
  }
786
931
  this.unclaim(taskId);
@@ -793,7 +938,6 @@ Agent role: ${role}` : `Autonomous work cycle. Agent role: ${role}`;
793
938
  this.deps.config
794
939
  );
795
940
  const template = this.deps.config.prompt?.template ?? DEFAULT_PROMPT_TEMPLATE;
796
- const allAgents = await this.cachedAgentStore.list();
797
941
  const attempt = task.attempts + 1;
798
942
  let retryContext;
799
943
  if (attempt > 1) {
@@ -805,15 +949,35 @@ Agent role: ${role}` : `Autonomous work cycle. Agent role: ${role}`;
805
949
  };
806
950
  }
807
951
  }
808
- const sharedContext = this.deps.contextStore ? await this.deps.contextStore.getAll() : void 0;
809
- const pendingMessages = this.deps.messageService ? await this.deps.messageService.drainMailbox(agent.id, task.id) : [];
952
+ const goalId = task.goalId;
953
+ const [sharedContext, pendingMessages, goalRaw] = await Promise.all([
954
+ this.deps.contextStore?.getAll(),
955
+ this.deps.messageService ? this.deps.messageService.drainMailbox(agent.id, task.id) : [],
956
+ goalId && this.cachedGoalStore ? this.cachedGoalStore.get(goalId).catch(() => null) : null
957
+ ]);
958
+ let goalContext;
959
+ if (goalRaw) {
960
+ const allTasks = await this.cachedTaskStore.list();
961
+ const goalTasks = allTasks.filter((t) => t.goalId === goalId);
962
+ const progressEntry = await this.deps.contextStore?.get(`${goalId}-progress`);
963
+ const MAX_GOAL_TASK_NAMES = 30;
964
+ const taskNames = goalTasks.map((t) => `[${t.status}] ${t.title}`);
965
+ goalContext = {
966
+ id: goalRaw.id,
967
+ title: goalRaw.title,
968
+ description: goalRaw.description,
969
+ status: goalRaw.status,
970
+ task_names: taskNames.length > MAX_GOAL_TASK_NAMES ? [...taskNames.slice(0, MAX_GOAL_TASK_NAMES), `... and ${taskNames.length - MAX_GOAL_TASK_NAMES} more`] : taskNames,
971
+ progress: progressEntry?.value
972
+ };
973
+ }
810
974
  const context = buildPromptContext(
811
975
  task,
812
976
  agent,
813
977
  attempt,
814
978
  workspacePath,
815
979
  this.deps.config,
816
- { allAgents, retryContext, sharedContext, feedback: task.feedback, messages: pendingMessages.length ? pendingMessages : void 0 }
980
+ { allAgents, retryContext, sharedContext, feedback: task.feedback, messages: pendingMessages.length ? pendingMessages : void 0, goal: goalContext }
817
981
  );
818
982
  const prompt = await this.deps.templateEngine.render(template, context);
819
983
  const run = await this.deps.runService.create({
@@ -990,7 +1154,7 @@ Agent role: ${role}` : `Autonomous work cycle. Agent role: ${role}`;
990
1154
  const agent = await this.deps.agentStore.get(agentId);
991
1155
  const isAutonomousTask = task.labels?.includes(AUTONOMOUS_LABEL);
992
1156
  const autoApprove = isAutonomousTask || agent?.config.approval_policy === "auto";
993
- const newStatus = resolveCompletionStatus(task, true, autoApprove);
1157
+ const newStatus = resolveCompletionStatus();
994
1158
  await this.deps.runService.finish(runId, "succeeded", tokens);
995
1159
  const runningEntry = state.running[taskId];
996
1160
  const successRuntimeMs = runningEntry ? Date.now() - new Date(runningEntry.started_at).getTime() : 0;
@@ -1223,8 +1387,7 @@ ${task.proof?.agent_summary ?? ""}`.slice(0, 2e3),
1223
1387
  await this.saveState();
1224
1388
  }
1225
1389
  unclaim(taskId) {
1226
- const idx = this.state.claimed.indexOf(taskId);
1227
- if (idx !== -1) this.state.claimed.splice(idx, 1);
1390
+ this.state.claimed.delete(taskId);
1228
1391
  }
1229
1392
  /**
1230
1393
  * Throw if this instance doesn't own the lock (read-only session).
@@ -1289,4 +1452,6 @@ function serializeEventData(data, maxLen) {
1289
1452
  return str.length > maxLen ? str.slice(0, maxLen) + "\u2026" : str;
1290
1453
  }
1291
1454
 
1292
- export { Orchestrator };
1455
+ export { Orchestrator, canTransition, isBlocked, isDispatchable, isTerminal, resolveFailureStatus };
1456
+ //# sourceMappingURL=chunk-2UC4SVJB.js.map
1457
+ //# sourceMappingURL=chunk-2UC4SVJB.js.map