oxe-cc 1.5.1 → 1.7.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 (125) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +45 -0
  3. package/README.md +19 -15
  4. package/bin/lib/oxe-agent-install.cjs +125 -24
  5. package/bin/lib/oxe-dashboard.cjs +21 -5
  6. package/bin/lib/oxe-project-health.cjs +120 -42
  7. package/bin/lib/oxe-release.cjs +77 -4
  8. package/bin/oxe-cc.js +155 -78
  9. package/commands/oxe/debug.md +6 -1
  10. package/commands/oxe/discuss.md +7 -2
  11. package/commands/oxe/execute.md +7 -2
  12. package/commands/oxe/plan-agent.md +7 -2
  13. package/commands/oxe/plan.md +7 -2
  14. package/commands/oxe/scan.md +6 -1
  15. package/commands/oxe/spec.md +6 -1
  16. package/commands/oxe/verify.md +6 -1
  17. package/docs/CONTENT-MIGRATION-AUDIT.md +49 -0
  18. package/docs/RELEASE-READINESS.md +8 -0
  19. package/docs/RUNTIME-SMOKE-MATRIX.md +9 -2
  20. package/lib/runtime/compiler/graph-compiler.js +32 -0
  21. package/lib/runtime/context/context-pack-builder.d.ts +15 -0
  22. package/lib/runtime/context/context-pack-builder.js +78 -0
  23. package/lib/runtime/events/catalog.d.ts +1 -1
  24. package/lib/runtime/events/catalog.js +5 -0
  25. package/lib/runtime/executor/action-tool-map.d.ts +3 -0
  26. package/lib/runtime/executor/action-tool-map.js +41 -0
  27. package/lib/runtime/executor/built-in-tools.d.ts +8 -0
  28. package/lib/runtime/executor/built-in-tools.js +267 -0
  29. package/lib/runtime/executor/index.d.ts +6 -0
  30. package/lib/runtime/executor/index.js +12 -0
  31. package/lib/runtime/executor/llm-task-executor.d.ts +29 -0
  32. package/lib/runtime/executor/llm-task-executor.js +138 -0
  33. package/lib/runtime/executor/node-prompt-builder.d.ts +3 -0
  34. package/lib/runtime/executor/node-prompt-builder.js +36 -0
  35. package/lib/runtime/executor/stream-completion.d.ts +38 -0
  36. package/lib/runtime/executor/stream-completion.js +105 -0
  37. package/lib/runtime/index.d.ts +1 -0
  38. package/lib/runtime/index.js +2 -0
  39. package/lib/runtime/models/failure.d.ts +5 -0
  40. package/lib/runtime/models/failure.js +2 -0
  41. package/lib/runtime/plugins/capability-adapter.d.ts +9 -0
  42. package/lib/runtime/plugins/capability-adapter.js +111 -8
  43. package/lib/runtime/plugins/plugin-abi.d.ts +8 -0
  44. package/lib/runtime/plugins/plugin-registry.d.ts +2 -1
  45. package/lib/runtime/plugins/plugin-registry.js +6 -1
  46. package/lib/runtime/reducers/run-state-reducer.js +39 -2
  47. package/lib/runtime/scheduler/scheduler.d.ts +14 -2
  48. package/lib/runtime/scheduler/scheduler.js +131 -11
  49. package/lib/runtime/verification/verification-manifest.d.ts +5 -2
  50. package/lib/sdk/index.cjs +10 -5
  51. package/lib/sdk/index.d.ts +21 -10
  52. package/oxe/agents/oxe-assumptions-analyzer.md +136 -0
  53. package/oxe/agents/oxe-codebase-mapper.md +142 -0
  54. package/oxe/agents/oxe-debugger.md +145 -0
  55. package/oxe/agents/oxe-executor.md +139 -0
  56. package/oxe/agents/oxe-integration-checker.md +142 -0
  57. package/oxe/agents/oxe-plan-checker.md +143 -0
  58. package/oxe/agents/oxe-planner.md +151 -0
  59. package/oxe/agents/oxe-research-synthesizer.md +146 -0
  60. package/oxe/agents/oxe-researcher.md +163 -0
  61. package/oxe/agents/oxe-ui-auditor.md +151 -0
  62. package/oxe/agents/oxe-ui-checker.md +157 -0
  63. package/oxe/agents/oxe-ui-researcher.md +179 -0
  64. package/oxe/agents/oxe-validation-auditor.md +154 -0
  65. package/oxe/agents/oxe-verifier.md +132 -0
  66. package/oxe/personas/README.md +91 -39
  67. package/oxe/personas/architect.md +149 -37
  68. package/oxe/personas/db-specialist.md +149 -36
  69. package/oxe/personas/debugger.md +155 -38
  70. package/oxe/personas/executor.md +164 -38
  71. package/oxe/personas/planner.md +165 -36
  72. package/oxe/personas/researcher.md +148 -35
  73. package/oxe/personas/ui-specialist.md +164 -36
  74. package/oxe/personas/verifier.md +174 -39
  75. package/oxe/templates/CONFIG.md +3 -3
  76. package/oxe/templates/EXECUTION-RUNTIME.template.md +1 -1
  77. package/oxe/templates/FIXTURE-PACK.template.json +29 -22
  78. package/oxe/templates/FIXTURE-PACK.template.md +20 -11
  79. package/oxe/templates/IMPLEMENTATION-PACK.template.json +55 -39
  80. package/oxe/templates/IMPLEMENTATION-PACK.template.md +28 -16
  81. package/oxe/templates/INVESTIGATION.template.md +38 -38
  82. package/oxe/templates/PLAN.template.md +63 -32
  83. package/oxe/templates/REFERENCE-ANCHORS.template.md +18 -14
  84. package/oxe/templates/RESEARCH.template.md +11 -11
  85. package/oxe/templates/SPEC.template.md +6 -6
  86. package/oxe/templates/SUMMARY.template.md +33 -3
  87. package/oxe/templates/config.template.json +1 -1
  88. package/oxe/workflows/debug.md +9 -7
  89. package/oxe/workflows/execute.md +31 -28
  90. package/oxe/workflows/forensics.md +5 -3
  91. package/oxe/workflows/milestone.md +12 -12
  92. package/oxe/workflows/next.md +1 -1
  93. package/oxe/workflows/plan.md +409 -132
  94. package/oxe/workflows/references/adaptive-discovery.md +27 -27
  95. package/oxe/workflows/references/flow-robustness-contract.md +80 -80
  96. package/oxe/workflows/references/session-path-resolution.md +71 -71
  97. package/oxe/workflows/references/workflow-runtime-contracts.json +127 -127
  98. package/oxe/workflows/scan.md +355 -69
  99. package/oxe/workflows/spec.md +302 -9
  100. package/oxe/workflows/ui-review.md +5 -4
  101. package/oxe/workflows/ui-spec.md +4 -3
  102. package/oxe/workflows/verify.md +12 -9
  103. package/oxe/workflows/workstream.md +16 -16
  104. package/package.json +1 -1
  105. package/packages/runtime/package.json +1 -1
  106. package/packages/runtime/src/compiler/graph-compiler.ts +40 -0
  107. package/packages/runtime/src/context/context-pack-builder.ts +80 -0
  108. package/packages/runtime/src/events/catalog.ts +5 -0
  109. package/packages/runtime/src/executor/action-tool-map.ts +46 -0
  110. package/packages/runtime/src/executor/built-in-tools.ts +276 -0
  111. package/packages/runtime/src/executor/index.ts +6 -0
  112. package/packages/runtime/src/executor/llm-task-executor.ts +194 -0
  113. package/packages/runtime/src/executor/node-prompt-builder.ts +45 -0
  114. package/packages/runtime/src/executor/stream-completion.ts +145 -0
  115. package/packages/runtime/src/index.ts +3 -0
  116. package/packages/runtime/src/models/failure.ts +11 -0
  117. package/packages/runtime/src/plugins/capability-adapter.ts +117 -10
  118. package/packages/runtime/src/plugins/plugin-abi.ts +9 -0
  119. package/packages/runtime/src/plugins/plugin-registry.ts +10 -1
  120. package/packages/runtime/src/reducers/run-state-reducer.ts +59 -2
  121. package/packages/runtime/src/scheduler/scheduler.ts +152 -14
  122. package/packages/runtime/src/verification/verification-manifest.ts +12 -8
  123. package/vscode-extension/oxe-agents-1.6.0.vsix +0 -0
  124. package/vscode-extension/oxe-agents-1.7.0.vsix +0 -0
  125. package/vscode-extension/package.json +1 -1
@@ -1,3 +1,5 @@
1
+ import path from 'path';
2
+ import fs from 'fs';
1
3
  import { appendEvent } from '../events/bus';
2
4
  import type { OxeEvent } from '../events/envelope';
3
5
  import type { EventInput } from '../events/bus';
@@ -18,10 +20,14 @@ import {
18
20
  createJournal,
19
21
  } from './run-journal';
20
22
  import type { RunJournal } from './run-journal';
23
+ import type { FailureClass } from '../models/failure';
24
+ import { listMemos } from '../decision/decision-memo';
25
+ import type { RollbackPlan } from '../decision/decision-memo';
26
+ import { runCapabilityAsync } from '../plugins/capability-adapter';
21
27
 
22
28
  export interface TaskResult {
23
29
  success: boolean;
24
- failure_class: 'env' | 'policy' | 'test' | 'timeout' | null;
30
+ failure_class: FailureClass;
25
31
  evidence: string[];
26
32
  output: string;
27
33
  }
@@ -35,6 +41,11 @@ export interface TaskExecutor {
35
41
  ): Promise<TaskResult>;
36
42
  }
37
43
 
44
+ export interface SchedulerOptions {
45
+ maxRunDurationMs?: number; // default: 30 min
46
+ staleProgressMs?: number; // default: 5 min without any task completing
47
+ }
48
+
38
49
  export interface SchedulerContext {
39
50
  projectRoot: string;
40
51
  sessionId: string | null;
@@ -48,15 +59,17 @@ export interface SchedulerContext {
48
59
  quota?: RunQuota;
49
60
  policyActor?: string;
50
61
  onEvent?: (event: OxeEvent) => void;
62
+ options?: SchedulerOptions;
51
63
  }
52
64
 
53
65
  export interface RunResult {
54
66
  run_id: string;
55
- status: 'completed' | 'failed' | 'blocked' | 'cancelled' | 'paused';
67
+ status: 'completed' | 'failed' | 'blocked' | 'cancelled' | 'paused' | 'aborted';
56
68
  completed: string[];
57
69
  failed: string[];
58
70
  blocked: string[];
59
71
  pending_gates?: string[];
72
+ reason?: string;
60
73
  }
61
74
 
62
75
  type NodeStatus = 'pending' | 'ready' | 'running' | 'completed' | 'failed' | 'blocked';
@@ -66,11 +79,43 @@ export class Scheduler {
66
79
  private paused = false;
67
80
  private journal: RunJournal | null = null;
68
81
  private ctx: SchedulerContext | null = null;
82
+ private runStartMs = 0;
83
+ private lastProgressMs = 0;
84
+
85
+ private recordProgress(): void {
86
+ this.lastProgressMs = Date.now();
87
+ }
88
+
89
+ private async executeRollback(plan: RollbackPlan, ctx: SchedulerContext): Promise<void> {
90
+ try {
91
+ switch (plan.strategy) {
92
+ case 'revert_commit':
93
+ await runCapabilityAsync('git', ['revert', 'HEAD', '--no-edit'], {}, ctx.projectRoot, 30_000);
94
+ break;
95
+ case 'restore_workspace':
96
+ await runCapabilityAsync('git', ['checkout', '.'], {}, ctx.projectRoot, 30_000);
97
+ break;
98
+ case 'undo_patch':
99
+ for (const p of plan.steps) {
100
+ await runCapabilityAsync('git', ['checkout', 'HEAD', '--', p], {}, ctx.projectRoot, 10_000);
101
+ }
102
+ break;
103
+ case 'no_rollback':
104
+ default:
105
+ break;
106
+ }
107
+ this.emit(ctx, { type: 'RollbackExecuted', payload: { strategy: plan.strategy } });
108
+ } catch (err) {
109
+ this.emit(ctx, { type: 'RollbackFailed', payload: { strategy: plan.strategy, error: String(err) } });
110
+ }
111
+ }
69
112
 
70
113
  async run(graph: ExecutionGraph, ctx: SchedulerContext): Promise<RunResult> {
71
114
  this.cancelled = false;
72
115
  this.paused = false;
73
116
  this.ctx = ctx;
117
+ this.runStartMs = Date.now();
118
+ this.lastProgressMs = Date.now();
74
119
 
75
120
  const status = new Map<string, NodeStatus>();
76
121
  for (const id of graph.nodes.keys()) status.set(id, 'pending');
@@ -79,6 +124,30 @@ export class Scheduler {
79
124
  const failed: string[] = [];
80
125
  const blocked: string[] = [];
81
126
 
127
+ // Plan hash drift detection: abort if the graph was recompiled since ACTIVE-RUN was saved
128
+ const activeRunPath = ctx.sessionId
129
+ ? path.join(ctx.projectRoot, '.oxe', ctx.sessionId, 'execution', 'ACTIVE-RUN.json')
130
+ : path.join(ctx.projectRoot, '.oxe', 'ACTIVE-RUN.json');
131
+ if (fs.existsSync(activeRunPath)) {
132
+ try {
133
+ const activeRun = JSON.parse(fs.readFileSync(activeRunPath, 'utf8')) as Record<string, unknown>;
134
+ const savedHash = activeRun.plan_hash as string | undefined;
135
+ const currentHash = graph.metadata.plan_hash;
136
+ if (savedHash && savedHash !== currentHash) {
137
+ return {
138
+ run_id: ctx.runId,
139
+ status: 'aborted',
140
+ completed: [],
141
+ failed: [],
142
+ blocked: [],
143
+ reason: `plan_drift: graph recompiled (${savedHash} → ${currentHash}). Run /oxe-plan --replan to realign.`,
144
+ };
145
+ }
146
+ } catch {
147
+ // ACTIVE-RUN not parseable — continue without drift check
148
+ }
149
+ }
150
+
82
151
  this.journal = createJournal(ctx.runId);
83
152
  saveJournal(ctx.projectRoot, ctx.runId, this.journal);
84
153
 
@@ -88,9 +157,24 @@ export class Scheduler {
88
157
  detail: { session_id: ctx.sessionId ?? null },
89
158
  });
90
159
 
160
+ const maxRunMs = ctx.options?.maxRunDurationMs ?? 30 * 60_000;
161
+ const staleMs = ctx.options?.staleProgressMs ?? 5 * 60_000;
162
+
91
163
  for (const wave of graph.waves) {
92
164
  if (this.cancelled) break;
93
165
 
166
+ // Global run timeout
167
+ if (Date.now() - this.runStartMs > maxRunMs) {
168
+ this.emit(ctx, { type: 'RunAborted', payload: { reason: 'global_timeout' } });
169
+ return { run_id: ctx.runId, status: 'aborted', completed: [], failed: [], blocked: [], reason: 'global_timeout' };
170
+ }
171
+
172
+ // Stale progress timeout (no task completed in staleMs)
173
+ if (Date.now() - this.lastProgressMs > staleMs) {
174
+ this.emit(ctx, { type: 'RunAborted', payload: { reason: 'no_progress_timeout' } });
175
+ return { run_id: ctx.runId, status: 'aborted', completed: [], failed: [], blocked: [], reason: 'no_progress_timeout' };
176
+ }
177
+
94
178
  // Respect pause: persist journal and return paused result
95
179
  if (this.paused) {
96
180
  this.journal.scheduler_state = 'paused';
@@ -123,7 +207,17 @@ export class Scheduler {
123
207
  this.journal.blocked_work_items = blocked.slice();
124
208
  saveJournal(ctx.projectRoot, ctx.runId, this.journal);
125
209
 
126
- if (waveFailed) break;
210
+ if (waveFailed) {
211
+ // Execute rollback plan if one was created for this run
212
+ const memos = listMemos(ctx.projectRoot, ctx.runId);
213
+ for (const memo of memos) {
214
+ if (memo.rollback_plan.strategy !== 'no_rollback') {
215
+ await this.executeRollback(memo.rollback_plan, ctx);
216
+ break; // apply at most one rollback plan per wave failure
217
+ }
218
+ }
219
+ break;
220
+ }
127
221
  }
128
222
 
129
223
  // Any remaining pending nodes become blocked
@@ -305,6 +399,15 @@ export class Scheduler {
305
399
  };
306
400
  }
307
401
 
402
+ private isConcurrentSafe(nodeId: string, graph: ExecutionGraph, ctx: SchedulerContext): boolean {
403
+ const node = graph.nodes.get(nodeId)!;
404
+ if (node.mutation_scope.length > 0) return false;
405
+ const primaryAction = pickPrimaryAction(node, ctx.pluginRegistry);
406
+ if (!primaryAction) return true;
407
+ const provider = ctx.pluginRegistry?.toolProviderFor(primaryAction.type);
408
+ return provider?.idempotent ?? true;
409
+ }
410
+
308
411
  private async runWave(
309
412
  nodeIds: string[],
310
413
  graph: ExecutionGraph,
@@ -338,10 +441,7 @@ export class Scheduler {
338
441
  });
339
442
  }
340
443
 
341
- const readOnly = eligible.filter((id) => {
342
- const node = graph.nodes.get(id)!;
343
- return node.mutation_scope.length === 0;
344
- });
444
+ const readOnly = eligible.filter((id) => this.isConcurrentSafe(id, graph, ctx));
345
445
  const mutations = eligible.filter((id) => !readOnly.includes(id));
346
446
 
347
447
  if (readOnly.length > 0) {
@@ -453,6 +553,7 @@ export class Scheduler {
453
553
  });
454
554
  status.set(nodeId, 'completed');
455
555
  completed.push(nodeId);
556
+ this.recordProgress();
456
557
  return;
457
558
  }
458
559
 
@@ -464,29 +565,47 @@ export class Scheduler {
464
565
  this.blockNode(nodeId, ctx, status, blocked, 'quota_exceeded', retryBlocked);
465
566
  return;
466
567
  }
568
+ // Exponential backoff with jitter: 1s * 2^(attempt-1) + [0, 500ms], capped at 30s
569
+ const backoffMs = Math.min(
570
+ 1_000 * Math.pow(2, attempt - 1) + Math.random() * 500,
571
+ 30_000
572
+ );
573
+ await new Promise<void>(resolve => setTimeout(resolve, backoffMs));
467
574
  this.emit(ctx, {
468
575
  type: 'RetryScheduled',
469
576
  work_item_id: nodeId,
470
- payload: { next_attempt: attempt + 1, reason: lastResult.failure_class },
577
+ payload: { next_attempt: attempt + 1, reason: lastResult.failure_class, backoff_ms: backoffMs },
471
578
  });
472
579
  }
473
- } catch (err) {
580
+ } catch (err: unknown) {
581
+ // Error boundary: isolate task failure, emit structured event, do not crash scheduler
582
+ const message = err instanceof Error ? err.message : String(err);
583
+ const stack = err instanceof Error ? err.stack : undefined;
584
+ this.emit(ctx, {
585
+ type: 'TaskErrorBoundaryTripped',
586
+ work_item_id: nodeId,
587
+ payload: { message, stack, attempt },
588
+ });
474
589
  lastResult = {
475
590
  success: false,
476
591
  failure_class: 'env',
477
592
  evidence: [],
478
- output: String(err),
593
+ output: `[error_boundary] ${message}`,
479
594
  };
480
595
  if (attempt < maxAttempts) {
596
+ const backoffMs = Math.min(1_000 * Math.pow(2, attempt - 1) + Math.random() * 500, 30_000);
597
+ await new Promise<void>(resolve => setTimeout(resolve, backoffMs));
481
598
  this.emit(ctx, {
482
599
  type: 'RetryScheduled',
483
600
  work_item_id: nodeId,
484
- payload: { next_attempt: attempt + 1, reason: 'env' },
601
+ payload: { next_attempt: attempt + 1, reason: 'env', backoff_ms: backoffMs },
485
602
  });
486
603
  }
487
604
  } finally {
488
605
  if (lease) {
489
- await ctx.workspaceManager.dispose(lease.workspace_id).catch(() => {});
606
+ await ctx.workspaceManager.dispose(lease.workspace_id).catch((e: unknown) =>
607
+ this.emit(ctx!, { type: 'WorkspaceDisposeFailed', payload: { workspace_id: lease?.workspace_id, error: String(e) } })
608
+ );
490
609
  lease = null;
491
610
  }
492
611
  }
@@ -562,7 +681,7 @@ export class Scheduler {
562
681
  payload: { provider: provider.name, action_type: primaryAction.type },
563
682
  });
564
683
 
565
- const result = await provider.invoke({
684
+ const invocationInput = {
566
685
  action_type: primaryAction.type,
567
686
  work_item_id: node.id,
568
687
  run_id: ctx.runId,
@@ -572,7 +691,26 @@ export class Scheduler {
572
691
  targets: primaryAction.targets ?? [],
573
692
  },
574
693
  workspace_root: lease.root_path,
575
- });
694
+ };
695
+
696
+ if (provider.preInvoke) {
697
+ const preCheck = await provider.preInvoke(invocationInput);
698
+ if (!preCheck.allowed) {
699
+ this.emit(ctx, {
700
+ type: 'ToolFailed',
701
+ work_item_id: node.id,
702
+ attempt_id: attemptId,
703
+ payload: { provider: provider.name, action_type: primaryAction.type, error: preCheck.reason ?? 'pre_invoke blocked', evidence_paths: [], side_effects_applied: [] },
704
+ });
705
+ return { success: false, failure_class: 'policy', evidence: [], output: preCheck.reason ?? 'pre_invoke blocked' };
706
+ }
707
+ }
708
+
709
+ const result = await provider.invoke(invocationInput);
710
+
711
+ if (provider.postInvoke) {
712
+ await provider.postInvoke(invocationInput, result).catch(() => {});
713
+ }
576
714
 
577
715
  this.emit(ctx, {
578
716
  type: result.success ? 'ToolCompleted' : 'ToolFailed',
@@ -6,13 +6,17 @@ import type { CheckResult } from './verification-compiler';
6
6
 
7
7
  export type VerificationProfile = 'quick' | 'standard' | 'critical';
8
8
 
9
- export type FailureClass =
10
- | 'deterministic'
11
- | 'flaky'
12
- | 'timeout'
13
- | 'env_setup'
14
- | 'policy_failure'
15
- | 'evidence_missing';
9
+ /** Verification-specific failure classification (why a check failed, not why a task failed). */
10
+ export type VerificationFailureClass =
11
+ | 'deterministic' // check always fails regardless of retry
12
+ | 'flaky' // check outcome is non-deterministic
13
+ | 'timeout' // check exceeded time budget
14
+ | 'env_setup' // environment/infrastructure prevented check from running
15
+ | 'policy_failure' // policy blocked the check
16
+ | 'evidence_missing'; // required evidence was never collected
17
+
18
+ /** @deprecated Use VerificationFailureClass. Kept for backwards compat. */
19
+ export type FailureClass = VerificationFailureClass;
16
20
 
17
21
  export type VerificationGranularity = 'work_item' | 'wave' | 'run';
18
22
 
@@ -20,7 +24,7 @@ export interface ManifestCheck {
20
24
  check_id: string;
21
25
  acceptance_ref: string | null;
22
26
  status: VerificationStatus;
23
- failure_class: FailureClass | null;
27
+ failure_class: VerificationFailureClass | null;
24
28
  evidence_refs: string[];
25
29
  duration_ms: number;
26
30
  }
@@ -2,7 +2,7 @@
2
2
  "name": "oxe-agents",
3
3
  "displayName": "OXE Agents",
4
4
  "description": "Agentes OXE para GitHub Copilot Chat — cada fase do ciclo como um @agente no VS Code",
5
- "version": "1.5.1",
5
+ "version": "1.7.0",
6
6
  "publisher": "oxe-cc",
7
7
  "license": "MIT",
8
8
  "engines": {