oxe-cc 1.0.0 → 1.2.1

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 (207) hide show
  1. package/.cursor/commands/oxe-ask.md +1 -1
  2. package/.cursor/commands/oxe-capabilities.md +1 -1
  3. package/.cursor/commands/oxe-checkpoint.md +1 -1
  4. package/.cursor/commands/oxe-compact.md +1 -1
  5. package/.cursor/commands/oxe-dashboard.md +1 -1
  6. package/.cursor/commands/oxe-debug.md +1 -1
  7. package/.cursor/commands/oxe-discuss.md +1 -1
  8. package/.cursor/commands/oxe-execute.md +2 -2
  9. package/.cursor/commands/oxe-forensics.md +1 -1
  10. package/.cursor/commands/oxe-help.md +1 -1
  11. package/.cursor/commands/oxe-loop.md +1 -1
  12. package/.cursor/commands/oxe-milestone.md +1 -1
  13. package/.cursor/commands/oxe-next.md +1 -1
  14. package/.cursor/commands/oxe-obs.md +1 -1
  15. package/.cursor/commands/oxe-plan-agent.md +1 -1
  16. package/.cursor/commands/oxe-plan.md +1 -1
  17. package/.cursor/commands/oxe-project.md +1 -1
  18. package/.cursor/commands/oxe-quick.md +1 -1
  19. package/.cursor/commands/oxe-research.md +1 -1
  20. package/.cursor/commands/oxe-retro.md +1 -1
  21. package/.cursor/commands/oxe-review-pr.md +1 -1
  22. package/.cursor/commands/oxe-route.md +1 -1
  23. package/.cursor/commands/oxe-scan.md +1 -1
  24. package/.cursor/commands/oxe-security.md +1 -1
  25. package/.cursor/commands/oxe-session.md +2 -2
  26. package/.cursor/commands/oxe-ship.md +45 -0
  27. package/.cursor/commands/oxe-skill.md +1 -1
  28. package/.cursor/commands/oxe-spec.md +1 -1
  29. package/.cursor/commands/oxe-ui-review.md +1 -1
  30. package/.cursor/commands/oxe-ui-spec.md +1 -1
  31. package/.cursor/commands/oxe-update.md +1 -1
  32. package/.cursor/commands/oxe-validate-gaps.md +1 -1
  33. package/.cursor/commands/oxe-verify.md +1 -1
  34. package/.cursor/commands/oxe-workstream.md +1 -1
  35. package/.cursor/commands/oxe.md +4 -4
  36. package/.github/copilot-instructions.md +91 -1
  37. package/.github/prompts/oxe-ask.prompt.md +1 -1
  38. package/.github/prompts/oxe-capabilities.prompt.md +1 -1
  39. package/.github/prompts/oxe-checkpoint.prompt.md +1 -1
  40. package/.github/prompts/oxe-compact.prompt.md +1 -1
  41. package/.github/prompts/oxe-dashboard.prompt.md +1 -1
  42. package/.github/prompts/oxe-debug.prompt.md +1 -1
  43. package/.github/prompts/oxe-discuss.prompt.md +1 -1
  44. package/.github/prompts/oxe-execute.prompt.md +2 -2
  45. package/.github/prompts/oxe-forensics.prompt.md +1 -1
  46. package/.github/prompts/oxe-help.prompt.md +1 -1
  47. package/.github/prompts/oxe-loop.prompt.md +1 -1
  48. package/.github/prompts/oxe-milestone.prompt.md +1 -1
  49. package/.github/prompts/oxe-next.prompt.md +1 -1
  50. package/.github/prompts/oxe-obs.prompt.md +1 -1
  51. package/.github/prompts/oxe-plan-agent.prompt.md +1 -1
  52. package/.github/prompts/oxe-plan.prompt.md +1 -1
  53. package/.github/prompts/oxe-project.prompt.md +1 -1
  54. package/.github/prompts/oxe-quick.prompt.md +1 -1
  55. package/.github/prompts/oxe-research.prompt.md +1 -1
  56. package/.github/prompts/oxe-retro.prompt.md +1 -1
  57. package/.github/prompts/oxe-review-pr.prompt.md +1 -1
  58. package/.github/prompts/oxe-route.prompt.md +1 -1
  59. package/.github/prompts/oxe-scan.prompt.md +1 -1
  60. package/.github/prompts/oxe-security.prompt.md +1 -1
  61. package/.github/prompts/oxe-session.prompt.md +2 -2
  62. package/.github/prompts/oxe-ship.prompt.md +45 -0
  63. package/.github/prompts/oxe-skill.prompt.md +1 -1
  64. package/.github/prompts/oxe-spec.prompt.md +1 -1
  65. package/.github/prompts/oxe-ui-review.prompt.md +1 -1
  66. package/.github/prompts/oxe-ui-spec.prompt.md +1 -1
  67. package/.github/prompts/oxe-update.prompt.md +1 -1
  68. package/.github/prompts/oxe-validate-gaps.prompt.md +1 -1
  69. package/.github/prompts/oxe-verify.prompt.md +1 -1
  70. package/.github/prompts/oxe-workstream.prompt.md +1 -1
  71. package/.github/prompts/oxe.prompt.md +3 -3
  72. package/AGENTS.md +43 -28
  73. package/CHANGELOG.md +158 -0
  74. package/README.md +72 -50
  75. package/bin/banner.txt +1 -1
  76. package/bin/lib/oxe-project-health.cjs +1 -1
  77. package/commands/oxe/ask.md +5 -1
  78. package/commands/oxe/checkpoint.md +1 -1
  79. package/commands/oxe/compact.md +1 -1
  80. package/commands/oxe/debug.md +1 -1
  81. package/commands/oxe/execute.md +2 -2
  82. package/commands/oxe/forensics.md +1 -1
  83. package/commands/oxe/loop.md +1 -1
  84. package/commands/oxe/milestone.md +1 -1
  85. package/commands/oxe/next.md +1 -1
  86. package/commands/oxe/obs.md +1 -1
  87. package/commands/oxe/oxe.md +3 -3
  88. package/commands/oxe/project.md +1 -1
  89. package/commands/oxe/research.md +1 -1
  90. package/commands/oxe/retro.md +1 -1
  91. package/commands/oxe/review-pr.md +1 -1
  92. package/commands/oxe/route.md +1 -1
  93. package/commands/oxe/scan.md +1 -1
  94. package/commands/oxe/security.md +1 -1
  95. package/commands/oxe/session.md +2 -2
  96. package/commands/oxe/ship.md +49 -0
  97. package/commands/oxe/spec.md +2 -2
  98. package/commands/oxe/ui-review.md +1 -1
  99. package/commands/oxe/ui-spec.md +1 -1
  100. package/commands/oxe/validate-gaps.md +1 -1
  101. package/commands/oxe/verify.md +2 -2
  102. package/commands/oxe/workstream.md +1 -1
  103. package/lib/runtime/audit/audit-trail.d.ts +71 -0
  104. package/lib/runtime/audit/audit-trail.js +154 -0
  105. package/lib/runtime/audit/index.d.ts +2 -0
  106. package/lib/runtime/audit/index.js +18 -0
  107. package/lib/runtime/audit/policy-pack.d.ts +15 -0
  108. package/lib/runtime/audit/policy-pack.js +57 -0
  109. package/lib/runtime/context/context-pack-builder.d.ts +15 -0
  110. package/lib/runtime/context/context-pack-builder.js +42 -0
  111. package/lib/runtime/context/context-pack-store.d.ts +38 -0
  112. package/lib/runtime/context/context-pack-store.js +142 -0
  113. package/lib/runtime/context/context-profiles.d.ts +11 -0
  114. package/lib/runtime/context/context-profiles.js +51 -0
  115. package/lib/runtime/context/index.d.ts +2 -0
  116. package/lib/runtime/context/index.js +2 -0
  117. package/lib/runtime/decision/decision-engine.d.ts +43 -0
  118. package/lib/runtime/decision/decision-engine.js +127 -0
  119. package/lib/runtime/decision/decision-memo.d.ts +53 -0
  120. package/lib/runtime/decision/decision-memo.js +173 -0
  121. package/lib/runtime/decision/index.d.ts +2 -0
  122. package/lib/runtime/decision/index.js +18 -0
  123. package/lib/runtime/delivery/index.d.ts +1 -0
  124. package/lib/runtime/delivery/index.js +1 -0
  125. package/lib/runtime/delivery/promotion-pipeline.d.ts +39 -0
  126. package/lib/runtime/delivery/promotion-pipeline.js +127 -0
  127. package/lib/runtime/index.d.ts +3 -0
  128. package/lib/runtime/index.js +4 -0
  129. package/lib/runtime/plugins/capability-matrix.d.ts +20 -0
  130. package/lib/runtime/plugins/capability-matrix.js +59 -0
  131. package/lib/runtime/plugins/index.d.ts +2 -0
  132. package/lib/runtime/plugins/index.js +2 -0
  133. package/lib/runtime/plugins/plugin-manifest.d.ts +22 -0
  134. package/lib/runtime/plugins/plugin-manifest.js +91 -0
  135. package/lib/runtime/plugins/plugin-registry.js +5 -0
  136. package/lib/runtime/policy/policy-engine.d.ts +28 -1
  137. package/lib/runtime/policy/policy-engine.js +96 -5
  138. package/lib/runtime/reducers/run-state-reducer.d.ts +26 -0
  139. package/lib/runtime/reducers/run-state-reducer.js +117 -1
  140. package/lib/runtime/scheduler/agent-registry.d.ts +44 -0
  141. package/lib/runtime/scheduler/agent-registry.js +96 -0
  142. package/lib/runtime/scheduler/agent-roles.d.ts +54 -0
  143. package/lib/runtime/scheduler/agent-roles.js +62 -0
  144. package/lib/runtime/scheduler/index.d.ts +3 -0
  145. package/lib/runtime/scheduler/index.js +3 -0
  146. package/lib/runtime/scheduler/multi-agent-coordinator.d.ts +2 -0
  147. package/lib/runtime/scheduler/multi-agent-coordinator.js +91 -4
  148. package/lib/runtime/scheduler/run-journal.d.ts +18 -0
  149. package/lib/runtime/scheduler/run-journal.js +54 -0
  150. package/lib/runtime/scheduler/scheduler.d.ts +11 -1
  151. package/lib/runtime/scheduler/scheduler.js +135 -7
  152. package/lib/runtime/verification/index.d.ts +1 -0
  153. package/lib/runtime/verification/index.js +1 -0
  154. package/lib/runtime/verification/verification-manifest.d.ts +58 -0
  155. package/lib/runtime/verification/verification-manifest.js +129 -0
  156. package/oxe/workflows/ask.md +4 -0
  157. package/oxe/workflows/checkpoint.md +14 -10
  158. package/oxe/workflows/debug.md +19 -15
  159. package/oxe/workflows/execute.md +30 -2
  160. package/oxe/workflows/forensics.md +13 -9
  161. package/oxe/workflows/help.md +97 -49
  162. package/oxe/workflows/loop.md +17 -13
  163. package/oxe/workflows/obs.md +4 -0
  164. package/oxe/workflows/oxe.md +64 -31
  165. package/oxe/workflows/project.md +6 -1
  166. package/oxe/workflows/references/workflow-runtime-contracts.json +23 -0
  167. package/oxe/workflows/research.md +32 -28
  168. package/oxe/workflows/retro.md +4 -0
  169. package/oxe/workflows/review-pr.md +15 -11
  170. package/oxe/workflows/scan.md +4 -0
  171. package/oxe/workflows/security.md +14 -10
  172. package/oxe/workflows/session.md +17 -1
  173. package/oxe/workflows/ship.md +142 -0
  174. package/oxe/workflows/spec.md +15 -0
  175. package/oxe/workflows/ui-review.md +20 -16
  176. package/oxe/workflows/ui-spec.md +7 -3
  177. package/oxe/workflows/validate-gaps.md +13 -9
  178. package/oxe/workflows/verify.md +42 -3
  179. package/package.json +1 -1
  180. package/packages/runtime/src/audit/audit-trail.ts +243 -0
  181. package/packages/runtime/src/audit/index.ts +2 -0
  182. package/packages/runtime/src/audit/policy-pack.ts +62 -0
  183. package/packages/runtime/src/context/context-pack-builder.ts +66 -0
  184. package/packages/runtime/src/context/context-pack-store.ts +197 -0
  185. package/packages/runtime/src/context/context-profiles.ts +60 -0
  186. package/packages/runtime/src/context/index.ts +2 -0
  187. package/packages/runtime/src/decision/decision-engine.ts +174 -0
  188. package/packages/runtime/src/decision/decision-memo.ts +211 -0
  189. package/packages/runtime/src/decision/index.ts +2 -0
  190. package/packages/runtime/src/delivery/index.ts +1 -0
  191. package/packages/runtime/src/delivery/promotion-pipeline.ts +180 -0
  192. package/packages/runtime/src/index.ts +5 -0
  193. package/packages/runtime/src/plugins/capability-matrix.ts +83 -0
  194. package/packages/runtime/src/plugins/index.ts +2 -0
  195. package/packages/runtime/src/plugins/plugin-manifest.ts +113 -0
  196. package/packages/runtime/src/plugins/plugin-registry.ts +5 -0
  197. package/packages/runtime/src/policy/policy-engine.ts +138 -7
  198. package/packages/runtime/src/reducers/run-state-reducer.ts +143 -1
  199. package/packages/runtime/src/scheduler/agent-registry.ts +132 -0
  200. package/packages/runtime/src/scheduler/agent-roles.ts +109 -0
  201. package/packages/runtime/src/scheduler/index.ts +3 -0
  202. package/packages/runtime/src/scheduler/multi-agent-coordinator.ts +106 -4
  203. package/packages/runtime/src/scheduler/run-journal.ts +62 -0
  204. package/packages/runtime/src/scheduler/scheduler.ts +168 -8
  205. package/packages/runtime/src/verification/index.ts +1 -0
  206. package/packages/runtime/src/verification/verification-manifest.ts +192 -0
  207. package/vscode-extension/oxe-agents-1.0.0.vsix +0 -0
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AgentRegistry = void 0;
4
+ class AgentRegistry {
5
+ constructor(heartbeatTimeoutMs = 30000) {
6
+ this.agents = new Map();
7
+ this.heartbeatTimeoutMs = heartbeatTimeoutMs;
8
+ }
9
+ register(id, executor, workspaceManager, assignedTaskIds = [], role) {
10
+ if (this.agents.has(id))
11
+ throw new Error(`Agent "${id}" is already registered`);
12
+ const agent = {
13
+ id,
14
+ executor,
15
+ workspaceManager,
16
+ assignedTaskIds,
17
+ heartbeat: {
18
+ agent_id: id,
19
+ last_seen: new Date().toISOString(),
20
+ current_task: null,
21
+ status: 'idle',
22
+ },
23
+ role,
24
+ actionLog: [],
25
+ };
26
+ this.agents.set(id, agent);
27
+ return agent;
28
+ }
29
+ unregister(id) {
30
+ this.agents.delete(id);
31
+ }
32
+ beat(id, currentTask = null) {
33
+ const agent = this.agents.get(id);
34
+ if (!agent)
35
+ return;
36
+ agent.heartbeat.last_seen = new Date().toISOString();
37
+ agent.heartbeat.current_task = currentTask;
38
+ agent.heartbeat.status = currentTask ? 'running' : 'idle';
39
+ }
40
+ setStatus(id, status) {
41
+ const agent = this.agents.get(id);
42
+ if (agent)
43
+ agent.heartbeat.status = status;
44
+ }
45
+ isAlive(id) {
46
+ const agent = this.agents.get(id);
47
+ if (!agent)
48
+ return false;
49
+ const elapsed = Date.now() - new Date(agent.heartbeat.last_seen).getTime();
50
+ return elapsed < this.heartbeatTimeoutMs;
51
+ }
52
+ /** Returns agents that haven't sent a heartbeat within the timeout window */
53
+ timedOut() {
54
+ return [...this.agents.values()].filter((a) => !this.isAlive(a.id));
55
+ }
56
+ liveAgents() {
57
+ return [...this.agents.values()].filter((a) => this.isAlive(a.id));
58
+ }
59
+ get(id) {
60
+ return this.agents.get(id) ?? null;
61
+ }
62
+ list() {
63
+ return [...this.agents.values()];
64
+ }
65
+ /**
66
+ * Reassign orphaned tasks from timed-out agents to a fallback agent.
67
+ * Returns the list of task IDs that were reassigned.
68
+ */
69
+ failover(fallbackAgentId) {
70
+ const fallback = this.agents.get(fallbackAgentId);
71
+ if (!fallback)
72
+ throw new Error(`Fallback agent "${fallbackAgentId}" not found`);
73
+ const orphaned = [];
74
+ for (const agent of this.timedOut()) {
75
+ orphaned.push(...agent.assignedTaskIds);
76
+ agent.assignedTaskIds = [];
77
+ agent.heartbeat.status = 'failed';
78
+ }
79
+ fallback.assignedTaskIds = [...fallback.assignedTaskIds, ...orphaned];
80
+ return orphaned;
81
+ }
82
+ /** Return all agents assigned to a given role */
83
+ getByRole(role) {
84
+ return [...this.agents.values()].filter((a) => a.role === role);
85
+ }
86
+ /** Append an action log entry for a registered agent (no-op if unknown) */
87
+ logAction(agentId, log) {
88
+ const agent = this.agents.get(agentId);
89
+ if (agent)
90
+ agent.actionLog.push(log);
91
+ }
92
+ clear() {
93
+ this.agents.clear();
94
+ }
95
+ }
96
+ exports.AgentRegistry = AgentRegistry;
@@ -0,0 +1,54 @@
1
+ import type { TaskResult } from './scheduler';
2
+ export type AgentRole = 'planner' | 'executor' | 'reviewer' | 'verifier';
3
+ export interface AgentBudget {
4
+ max_tokens: number;
5
+ max_time_ms: number;
6
+ max_retries: number;
7
+ consumed_tokens: number;
8
+ consumed_time_ms: number;
9
+ consumed_retries: number;
10
+ }
11
+ export interface CooperativeHandoff {
12
+ handoff_id: string;
13
+ from_agent_id: string;
14
+ to_agent_id: string;
15
+ from_role: AgentRole;
16
+ to_role: AgentRole;
17
+ work_item_id: string;
18
+ context_pack_ref: string | null;
19
+ transferred_at: string;
20
+ }
21
+ export interface AgentActionLog {
22
+ agent_id: string;
23
+ role: AgentRole;
24
+ work_item_id: string;
25
+ action: 'execute' | 'verify' | 'review' | 'plan';
26
+ result: 'success' | 'failure';
27
+ duration_ms: number;
28
+ timestamp: string;
29
+ }
30
+ export declare function createBudget(opts?: Partial<Omit<AgentBudget, 'consumed_tokens' | 'consumed_time_ms' | 'consumed_retries'>>): AgentBudget;
31
+ export declare function consumeBudget(budget: AgentBudget, delta: {
32
+ tokens?: number;
33
+ time_ms?: number;
34
+ retries?: number;
35
+ }): AgentBudget;
36
+ export declare function isBudgetExhausted(budget: AgentBudget): boolean;
37
+ export declare function buildHandoff(opts: {
38
+ from_agent_id: string;
39
+ to_agent_id: string;
40
+ from_role: AgentRole;
41
+ to_role: AgentRole;
42
+ work_item_id: string;
43
+ context_pack_ref?: string | null;
44
+ }): CooperativeHandoff;
45
+ export declare class ArbitrationEngine {
46
+ /**
47
+ * Choose the best result from multiple competing agents.
48
+ * Prefers success; among successes prefers more evidence; falls back to first.
49
+ */
50
+ arbitrate(results: Array<{
51
+ agent_id: string;
52
+ result: TaskResult;
53
+ }>): TaskResult;
54
+ }
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ArbitrationEngine = void 0;
4
+ exports.createBudget = createBudget;
5
+ exports.consumeBudget = consumeBudget;
6
+ exports.isBudgetExhausted = isBudgetExhausted;
7
+ exports.buildHandoff = buildHandoff;
8
+ function createBudget(opts = {}) {
9
+ return {
10
+ max_tokens: opts.max_tokens ?? Infinity,
11
+ max_time_ms: opts.max_time_ms ?? Infinity,
12
+ max_retries: opts.max_retries ?? Infinity,
13
+ consumed_tokens: 0,
14
+ consumed_time_ms: 0,
15
+ consumed_retries: 0,
16
+ };
17
+ }
18
+ function consumeBudget(budget, delta) {
19
+ return {
20
+ ...budget,
21
+ consumed_tokens: budget.consumed_tokens + (delta.tokens ?? 0),
22
+ consumed_time_ms: budget.consumed_time_ms + (delta.time_ms ?? 0),
23
+ consumed_retries: budget.consumed_retries + (delta.retries ?? 0),
24
+ };
25
+ }
26
+ function isBudgetExhausted(budget) {
27
+ return (budget.consumed_tokens >= budget.max_tokens ||
28
+ budget.consumed_time_ms >= budget.max_time_ms ||
29
+ budget.consumed_retries >= budget.max_retries);
30
+ }
31
+ function buildHandoff(opts) {
32
+ return {
33
+ handoff_id: `hoff-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`,
34
+ from_agent_id: opts.from_agent_id,
35
+ to_agent_id: opts.to_agent_id,
36
+ from_role: opts.from_role,
37
+ to_role: opts.to_role,
38
+ work_item_id: opts.work_item_id,
39
+ context_pack_ref: opts.context_pack_ref ?? null,
40
+ transferred_at: new Date().toISOString(),
41
+ };
42
+ }
43
+ // ─── ArbitrationEngine ────────────────────────────────────────────────────────
44
+ class ArbitrationEngine {
45
+ /**
46
+ * Choose the best result from multiple competing agents.
47
+ * Prefers success; among successes prefers more evidence; falls back to first.
48
+ */
49
+ arbitrate(results) {
50
+ if (results.length === 0) {
51
+ return { success: false, failure_class: null, evidence: [], output: 'no results to arbitrate' };
52
+ }
53
+ const successes = results.filter((r) => r.result.success);
54
+ if (successes.length === 0) {
55
+ return results[0].result;
56
+ }
57
+ // Among successes, prefer the one with most evidence
58
+ successes.sort((a, b) => b.result.evidence.length - a.result.evidence.length);
59
+ return successes[0].result;
60
+ }
61
+ }
62
+ exports.ArbitrationEngine = ArbitrationEngine;
@@ -1 +1,4 @@
1
1
  export * from './scheduler';
2
+ export * from './run-journal';
3
+ export * from './agent-registry';
4
+ export * from './agent-roles';
@@ -15,3 +15,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./scheduler"), exports);
18
+ __exportStar(require("./run-journal"), exports);
19
+ __exportStar(require("./agent-registry"), exports);
20
+ __exportStar(require("./agent-roles"), exports);
@@ -1,6 +1,7 @@
1
1
  import type { ExecutionGraph } from '../compiler/graph-compiler';
2
2
  import type { WorkspaceManager } from '../workspace/workspace-manager';
3
3
  import type { TaskExecutor, SchedulerContext } from './scheduler';
4
+ import type { CooperativeHandoff } from './agent-roles';
4
5
  export type CoordinationMode = 'parallel' | 'competitive' | 'cooperative';
5
6
  export interface AgentSpec {
6
7
  id: string;
@@ -28,6 +29,7 @@ export interface CoordinationResult {
28
29
  completed: string[];
29
30
  failed: string[];
30
31
  }>;
32
+ handoffs?: CooperativeHandoff[];
31
33
  }
32
34
  export declare class MultiAgentCoordinator {
33
35
  run(graph: ExecutionGraph, opts: CoordinationOptions): Promise<CoordinationResult>;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MultiAgentCoordinator = void 0;
4
4
  const bus_1 = require("../events/bus");
5
5
  const scheduler_1 = require("./scheduler");
6
+ const agent_roles_1 = require("./agent-roles");
6
7
  // ─── Parallel mode ───────────────────────────────────────────────────────────
7
8
  // Tasks are partitioned across agents. Each agent runs its own Scheduler
8
9
  // on a sub-graph. Results are merged.
@@ -132,16 +133,102 @@ async function competeTwoAgents(nodeId, node, agentA, agentB, opts) {
132
133
  }
133
134
  return winner;
134
135
  }
136
+ // ─── Cooperative mode ────────────────────────────────────────────────────────
137
+ // planner (agent[0]) does a dry-run to collect context, then hands off to
138
+ // executor (agent[1]) which performs the real run. Handoffs are recorded.
139
+ async function runCooperative(graph, opts) {
140
+ if (opts.agents.length < 2) {
141
+ throw new Error('Cooperative mode requires at least 2 agents');
142
+ }
143
+ const [planner, executor] = opts.agents;
144
+ const { projectRoot, sessionId, runId } = opts;
145
+ const handoffs = [];
146
+ (0, bus_1.appendEvent)(projectRoot, sessionId, {
147
+ type: 'RunStarted',
148
+ run_id: runId,
149
+ payload: { mode: 'cooperative', planner: planner.id, executor: executor.id },
150
+ });
151
+ const completed = [];
152
+ const failed = [];
153
+ for (const wave of graph.waves) {
154
+ for (const nodeId of wave.node_ids) {
155
+ const node = graph.nodes.get(nodeId);
156
+ // Phase 1: planner allocates workspace + signals readiness (no output used)
157
+ const planAlloc = await planner.workspaceManager.allocate({
158
+ work_item_id: nodeId,
159
+ attempt_number: 1,
160
+ strategy: node.workspace_strategy,
161
+ mutation_scope: node.mutation_scope,
162
+ });
163
+ await planner.workspaceManager.dispose(planAlloc.workspace_id).catch(() => { });
164
+ const handoff = (0, agent_roles_1.buildHandoff)({
165
+ from_agent_id: planner.id,
166
+ to_agent_id: executor.id,
167
+ from_role: 'planner',
168
+ to_role: 'executor',
169
+ work_item_id: nodeId,
170
+ context_pack_ref: null,
171
+ });
172
+ handoffs.push(handoff);
173
+ (0, bus_1.appendEvent)(projectRoot, sessionId, {
174
+ type: 'AttemptStarted',
175
+ run_id: runId,
176
+ work_item_id: nodeId,
177
+ payload: { mode: 'cooperative', handoff_id: handoff.handoff_id },
178
+ });
179
+ // Phase 2: executor performs the real task
180
+ const execAlloc = await executor.workspaceManager.allocate({
181
+ work_item_id: nodeId,
182
+ attempt_number: 1,
183
+ strategy: node.workspace_strategy,
184
+ mutation_scope: node.mutation_scope,
185
+ });
186
+ let result;
187
+ try {
188
+ result = await executor.executor.execute(node, execAlloc, runId, 1);
189
+ }
190
+ catch (e) {
191
+ result = { success: false, failure_class: 'env', evidence: [], output: String(e) };
192
+ }
193
+ await executor.workspaceManager.dispose(execAlloc.workspace_id).catch(() => { });
194
+ if (result.success) {
195
+ completed.push(nodeId);
196
+ (0, bus_1.appendEvent)(projectRoot, sessionId, { type: 'WorkItemCompleted', run_id: runId, work_item_id: nodeId, payload: { mode: 'cooperative' } });
197
+ }
198
+ else {
199
+ failed.push(nodeId);
200
+ (0, bus_1.appendEvent)(projectRoot, sessionId, { type: 'WorkItemBlocked', run_id: runId, work_item_id: nodeId, payload: { mode: 'cooperative', failure_class: result.failure_class } });
201
+ break;
202
+ }
203
+ }
204
+ if (failed.length > 0)
205
+ break;
206
+ }
207
+ (0, bus_1.appendEvent)(projectRoot, sessionId, {
208
+ type: 'RunCompleted',
209
+ run_id: runId,
210
+ payload: { mode: 'cooperative', completed: completed.length, failed: failed.length },
211
+ });
212
+ return {
213
+ mode: 'cooperative',
214
+ run_id: runId,
215
+ completed,
216
+ failed,
217
+ blocked: [],
218
+ agent_results: [
219
+ { agent_id: planner.id, completed: [], failed: [] },
220
+ { agent_id: executor.id, completed, failed },
221
+ ],
222
+ handoffs,
223
+ };
224
+ }
135
225
  // ─── Public API ──────────────────────────────────────────────────────────────
136
226
  class MultiAgentCoordinator {
137
227
  async run(graph, opts) {
138
228
  switch (opts.mode) {
139
229
  case 'parallel': return runParallel(graph, opts);
140
230
  case 'competitive': return runCompetitive(graph, opts);
141
- case 'cooperative':
142
- // Cooperative mode: planner (agent[0]) prepares context,
143
- // executor (agent[1]) implements. For R3, delegate to sequential parallel.
144
- return runParallel(graph, { ...opts, mode: 'parallel' });
231
+ case 'cooperative': return runCooperative(graph, opts);
145
232
  default:
146
233
  throw new Error(`Unknown coordination mode: ${opts.mode}`);
147
234
  }
@@ -0,0 +1,18 @@
1
+ import type { RunResult } from './scheduler';
2
+ export interface RunJournal {
3
+ run_id: string;
4
+ paused_at: string | null;
5
+ cancelled: boolean;
6
+ eligible_work_items: string[];
7
+ completed_work_items: string[];
8
+ failed_work_items: string[];
9
+ blocked_work_items: string[];
10
+ pending_gates: string[];
11
+ replay_cursor: string | null;
12
+ scheduler_state: 'running' | 'paused' | 'cancelled' | 'completed';
13
+ partial_result: Omit<RunResult, 'status'> | null;
14
+ }
15
+ export declare function saveJournal(projectRoot: string, runId: string, journal: RunJournal): void;
16
+ export declare function loadJournal(projectRoot: string, runId: string): RunJournal | null;
17
+ export declare function deleteJournal(projectRoot: string, runId: string): void;
18
+ export declare function createJournal(runId: string): RunJournal;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.saveJournal = saveJournal;
7
+ exports.loadJournal = loadJournal;
8
+ exports.deleteJournal = deleteJournal;
9
+ exports.createJournal = createJournal;
10
+ const path_1 = __importDefault(require("path"));
11
+ const fs_1 = __importDefault(require("fs"));
12
+ function journalPath(projectRoot, runId) {
13
+ return path_1.default.join(projectRoot, '.oxe', 'runs', runId, 'journal.json');
14
+ }
15
+ function saveJournal(projectRoot, runId, journal) {
16
+ const p = journalPath(projectRoot, runId);
17
+ fs_1.default.mkdirSync(path_1.default.dirname(p), { recursive: true });
18
+ fs_1.default.writeFileSync(p, JSON.stringify(journal, null, 2), 'utf8');
19
+ }
20
+ function loadJournal(projectRoot, runId) {
21
+ const p = journalPath(projectRoot, runId);
22
+ if (!fs_1.default.existsSync(p))
23
+ return null;
24
+ try {
25
+ return JSON.parse(fs_1.default.readFileSync(p, 'utf8'));
26
+ }
27
+ catch {
28
+ return null;
29
+ }
30
+ }
31
+ function deleteJournal(projectRoot, runId) {
32
+ const p = journalPath(projectRoot, runId);
33
+ try {
34
+ fs_1.default.unlinkSync(p);
35
+ }
36
+ catch {
37
+ // ignore if not found
38
+ }
39
+ }
40
+ function createJournal(runId) {
41
+ return {
42
+ run_id: runId,
43
+ paused_at: null,
44
+ cancelled: false,
45
+ eligible_work_items: [],
46
+ completed_work_items: [],
47
+ failed_work_items: [],
48
+ blocked_work_items: [],
49
+ pending_gates: [],
50
+ replay_cursor: null,
51
+ scheduler_state: 'running',
52
+ partial_result: null,
53
+ };
54
+ }
@@ -2,6 +2,7 @@ import type { OxeEvent } from '../events/envelope';
2
2
  import type { ExecutionGraph, GraphNode } from '../compiler/graph-compiler';
3
3
  import type { WorkspaceManager } from '../workspace/workspace-manager';
4
4
  import type { WorkspaceLease } from '../models/workspace';
5
+ import type { RunJournal } from './run-journal';
5
6
  export interface TaskResult {
6
7
  success: boolean;
7
8
  failure_class: 'env' | 'policy' | 'test' | 'timeout' | null;
@@ -21,7 +22,7 @@ export interface SchedulerContext {
21
22
  }
22
23
  export interface RunResult {
23
24
  run_id: string;
24
- status: 'completed' | 'failed' | 'cancelled';
25
+ status: 'completed' | 'failed' | 'cancelled' | 'paused';
25
26
  completed: string[];
26
27
  failed: string[];
27
28
  blocked: string[];
@@ -29,11 +30,20 @@ export interface RunResult {
29
30
  export declare class Scheduler {
30
31
  private cancelled;
31
32
  private paused;
33
+ private journal;
34
+ private ctx;
32
35
  run(graph: ExecutionGraph, ctx: SchedulerContext): Promise<RunResult>;
36
+ /**
37
+ * Recover a previously paused run by loading its journal and re-running
38
+ * only the work items that haven't completed yet.
39
+ */
40
+ recover(runId: string, ctx: SchedulerContext, graph: ExecutionGraph): Promise<RunResult | null>;
33
41
  private runWave;
34
42
  private runNode;
35
43
  pause(): void;
36
44
  resume(): void;
37
45
  cancel(): void;
46
+ getJournal(): RunJournal | null;
47
+ static loadJournal(projectRoot: string, runId: string): RunJournal | null;
38
48
  private emit;
39
49
  }