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.
- package/.cursor/commands/oxe-ask.md +1 -1
- package/.cursor/commands/oxe-capabilities.md +1 -1
- package/.cursor/commands/oxe-checkpoint.md +1 -1
- package/.cursor/commands/oxe-compact.md +1 -1
- package/.cursor/commands/oxe-dashboard.md +1 -1
- package/.cursor/commands/oxe-debug.md +1 -1
- package/.cursor/commands/oxe-discuss.md +1 -1
- package/.cursor/commands/oxe-execute.md +2 -2
- package/.cursor/commands/oxe-forensics.md +1 -1
- package/.cursor/commands/oxe-help.md +1 -1
- package/.cursor/commands/oxe-loop.md +1 -1
- package/.cursor/commands/oxe-milestone.md +1 -1
- package/.cursor/commands/oxe-next.md +1 -1
- package/.cursor/commands/oxe-obs.md +1 -1
- package/.cursor/commands/oxe-plan-agent.md +1 -1
- package/.cursor/commands/oxe-plan.md +1 -1
- package/.cursor/commands/oxe-project.md +1 -1
- package/.cursor/commands/oxe-quick.md +1 -1
- package/.cursor/commands/oxe-research.md +1 -1
- package/.cursor/commands/oxe-retro.md +1 -1
- package/.cursor/commands/oxe-review-pr.md +1 -1
- package/.cursor/commands/oxe-route.md +1 -1
- package/.cursor/commands/oxe-scan.md +1 -1
- package/.cursor/commands/oxe-security.md +1 -1
- package/.cursor/commands/oxe-session.md +2 -2
- package/.cursor/commands/oxe-ship.md +45 -0
- package/.cursor/commands/oxe-skill.md +1 -1
- package/.cursor/commands/oxe-spec.md +1 -1
- package/.cursor/commands/oxe-ui-review.md +1 -1
- package/.cursor/commands/oxe-ui-spec.md +1 -1
- package/.cursor/commands/oxe-update.md +1 -1
- package/.cursor/commands/oxe-validate-gaps.md +1 -1
- package/.cursor/commands/oxe-verify.md +1 -1
- package/.cursor/commands/oxe-workstream.md +1 -1
- package/.cursor/commands/oxe.md +4 -4
- package/.github/copilot-instructions.md +91 -1
- package/.github/prompts/oxe-ask.prompt.md +1 -1
- package/.github/prompts/oxe-capabilities.prompt.md +1 -1
- package/.github/prompts/oxe-checkpoint.prompt.md +1 -1
- package/.github/prompts/oxe-compact.prompt.md +1 -1
- package/.github/prompts/oxe-dashboard.prompt.md +1 -1
- package/.github/prompts/oxe-debug.prompt.md +1 -1
- package/.github/prompts/oxe-discuss.prompt.md +1 -1
- package/.github/prompts/oxe-execute.prompt.md +2 -2
- package/.github/prompts/oxe-forensics.prompt.md +1 -1
- package/.github/prompts/oxe-help.prompt.md +1 -1
- package/.github/prompts/oxe-loop.prompt.md +1 -1
- package/.github/prompts/oxe-milestone.prompt.md +1 -1
- package/.github/prompts/oxe-next.prompt.md +1 -1
- package/.github/prompts/oxe-obs.prompt.md +1 -1
- package/.github/prompts/oxe-plan-agent.prompt.md +1 -1
- package/.github/prompts/oxe-plan.prompt.md +1 -1
- package/.github/prompts/oxe-project.prompt.md +1 -1
- package/.github/prompts/oxe-quick.prompt.md +1 -1
- package/.github/prompts/oxe-research.prompt.md +1 -1
- package/.github/prompts/oxe-retro.prompt.md +1 -1
- package/.github/prompts/oxe-review-pr.prompt.md +1 -1
- package/.github/prompts/oxe-route.prompt.md +1 -1
- package/.github/prompts/oxe-scan.prompt.md +1 -1
- package/.github/prompts/oxe-security.prompt.md +1 -1
- package/.github/prompts/oxe-session.prompt.md +2 -2
- package/.github/prompts/oxe-ship.prompt.md +45 -0
- package/.github/prompts/oxe-skill.prompt.md +1 -1
- package/.github/prompts/oxe-spec.prompt.md +1 -1
- package/.github/prompts/oxe-ui-review.prompt.md +1 -1
- package/.github/prompts/oxe-ui-spec.prompt.md +1 -1
- package/.github/prompts/oxe-update.prompt.md +1 -1
- package/.github/prompts/oxe-validate-gaps.prompt.md +1 -1
- package/.github/prompts/oxe-verify.prompt.md +1 -1
- package/.github/prompts/oxe-workstream.prompt.md +1 -1
- package/.github/prompts/oxe.prompt.md +3 -3
- package/AGENTS.md +43 -28
- package/CHANGELOG.md +158 -0
- package/README.md +72 -50
- package/bin/banner.txt +1 -1
- package/bin/lib/oxe-project-health.cjs +1 -1
- package/commands/oxe/ask.md +5 -1
- package/commands/oxe/checkpoint.md +1 -1
- package/commands/oxe/compact.md +1 -1
- package/commands/oxe/debug.md +1 -1
- package/commands/oxe/execute.md +2 -2
- package/commands/oxe/forensics.md +1 -1
- package/commands/oxe/loop.md +1 -1
- package/commands/oxe/milestone.md +1 -1
- package/commands/oxe/next.md +1 -1
- package/commands/oxe/obs.md +1 -1
- package/commands/oxe/oxe.md +3 -3
- package/commands/oxe/project.md +1 -1
- package/commands/oxe/research.md +1 -1
- package/commands/oxe/retro.md +1 -1
- package/commands/oxe/review-pr.md +1 -1
- package/commands/oxe/route.md +1 -1
- package/commands/oxe/scan.md +1 -1
- package/commands/oxe/security.md +1 -1
- package/commands/oxe/session.md +2 -2
- package/commands/oxe/ship.md +49 -0
- package/commands/oxe/spec.md +2 -2
- package/commands/oxe/ui-review.md +1 -1
- package/commands/oxe/ui-spec.md +1 -1
- package/commands/oxe/validate-gaps.md +1 -1
- package/commands/oxe/verify.md +2 -2
- package/commands/oxe/workstream.md +1 -1
- package/lib/runtime/audit/audit-trail.d.ts +71 -0
- package/lib/runtime/audit/audit-trail.js +154 -0
- package/lib/runtime/audit/index.d.ts +2 -0
- package/lib/runtime/audit/index.js +18 -0
- package/lib/runtime/audit/policy-pack.d.ts +15 -0
- package/lib/runtime/audit/policy-pack.js +57 -0
- package/lib/runtime/context/context-pack-builder.d.ts +15 -0
- package/lib/runtime/context/context-pack-builder.js +42 -0
- package/lib/runtime/context/context-pack-store.d.ts +38 -0
- package/lib/runtime/context/context-pack-store.js +142 -0
- package/lib/runtime/context/context-profiles.d.ts +11 -0
- package/lib/runtime/context/context-profiles.js +51 -0
- package/lib/runtime/context/index.d.ts +2 -0
- package/lib/runtime/context/index.js +2 -0
- package/lib/runtime/decision/decision-engine.d.ts +43 -0
- package/lib/runtime/decision/decision-engine.js +127 -0
- package/lib/runtime/decision/decision-memo.d.ts +53 -0
- package/lib/runtime/decision/decision-memo.js +173 -0
- package/lib/runtime/decision/index.d.ts +2 -0
- package/lib/runtime/decision/index.js +18 -0
- package/lib/runtime/delivery/index.d.ts +1 -0
- package/lib/runtime/delivery/index.js +1 -0
- package/lib/runtime/delivery/promotion-pipeline.d.ts +39 -0
- package/lib/runtime/delivery/promotion-pipeline.js +127 -0
- package/lib/runtime/index.d.ts +3 -0
- package/lib/runtime/index.js +4 -0
- package/lib/runtime/plugins/capability-matrix.d.ts +20 -0
- package/lib/runtime/plugins/capability-matrix.js +59 -0
- package/lib/runtime/plugins/index.d.ts +2 -0
- package/lib/runtime/plugins/index.js +2 -0
- package/lib/runtime/plugins/plugin-manifest.d.ts +22 -0
- package/lib/runtime/plugins/plugin-manifest.js +91 -0
- package/lib/runtime/plugins/plugin-registry.js +5 -0
- package/lib/runtime/policy/policy-engine.d.ts +28 -1
- package/lib/runtime/policy/policy-engine.js +96 -5
- package/lib/runtime/reducers/run-state-reducer.d.ts +26 -0
- package/lib/runtime/reducers/run-state-reducer.js +117 -1
- package/lib/runtime/scheduler/agent-registry.d.ts +44 -0
- package/lib/runtime/scheduler/agent-registry.js +96 -0
- package/lib/runtime/scheduler/agent-roles.d.ts +54 -0
- package/lib/runtime/scheduler/agent-roles.js +62 -0
- package/lib/runtime/scheduler/index.d.ts +3 -0
- package/lib/runtime/scheduler/index.js +3 -0
- package/lib/runtime/scheduler/multi-agent-coordinator.d.ts +2 -0
- package/lib/runtime/scheduler/multi-agent-coordinator.js +91 -4
- package/lib/runtime/scheduler/run-journal.d.ts +18 -0
- package/lib/runtime/scheduler/run-journal.js +54 -0
- package/lib/runtime/scheduler/scheduler.d.ts +11 -1
- package/lib/runtime/scheduler/scheduler.js +135 -7
- package/lib/runtime/verification/index.d.ts +1 -0
- package/lib/runtime/verification/index.js +1 -0
- package/lib/runtime/verification/verification-manifest.d.ts +58 -0
- package/lib/runtime/verification/verification-manifest.js +129 -0
- package/oxe/workflows/ask.md +4 -0
- package/oxe/workflows/checkpoint.md +14 -10
- package/oxe/workflows/debug.md +19 -15
- package/oxe/workflows/execute.md +30 -2
- package/oxe/workflows/forensics.md +13 -9
- package/oxe/workflows/help.md +97 -49
- package/oxe/workflows/loop.md +17 -13
- package/oxe/workflows/obs.md +4 -0
- package/oxe/workflows/oxe.md +64 -31
- package/oxe/workflows/project.md +6 -1
- package/oxe/workflows/references/workflow-runtime-contracts.json +23 -0
- package/oxe/workflows/research.md +32 -28
- package/oxe/workflows/retro.md +4 -0
- package/oxe/workflows/review-pr.md +15 -11
- package/oxe/workflows/scan.md +4 -0
- package/oxe/workflows/security.md +14 -10
- package/oxe/workflows/session.md +17 -1
- package/oxe/workflows/ship.md +142 -0
- package/oxe/workflows/spec.md +15 -0
- package/oxe/workflows/ui-review.md +20 -16
- package/oxe/workflows/ui-spec.md +7 -3
- package/oxe/workflows/validate-gaps.md +13 -9
- package/oxe/workflows/verify.md +42 -3
- package/package.json +1 -1
- package/packages/runtime/src/audit/audit-trail.ts +243 -0
- package/packages/runtime/src/audit/index.ts +2 -0
- package/packages/runtime/src/audit/policy-pack.ts +62 -0
- package/packages/runtime/src/context/context-pack-builder.ts +66 -0
- package/packages/runtime/src/context/context-pack-store.ts +197 -0
- package/packages/runtime/src/context/context-profiles.ts +60 -0
- package/packages/runtime/src/context/index.ts +2 -0
- package/packages/runtime/src/decision/decision-engine.ts +174 -0
- package/packages/runtime/src/decision/decision-memo.ts +211 -0
- package/packages/runtime/src/decision/index.ts +2 -0
- package/packages/runtime/src/delivery/index.ts +1 -0
- package/packages/runtime/src/delivery/promotion-pipeline.ts +180 -0
- package/packages/runtime/src/index.ts +5 -0
- package/packages/runtime/src/plugins/capability-matrix.ts +83 -0
- package/packages/runtime/src/plugins/index.ts +2 -0
- package/packages/runtime/src/plugins/plugin-manifest.ts +113 -0
- package/packages/runtime/src/plugins/plugin-registry.ts +5 -0
- package/packages/runtime/src/policy/policy-engine.ts +138 -7
- package/packages/runtime/src/reducers/run-state-reducer.ts +143 -1
- package/packages/runtime/src/scheduler/agent-registry.ts +132 -0
- package/packages/runtime/src/scheduler/agent-roles.ts +109 -0
- package/packages/runtime/src/scheduler/index.ts +3 -0
- package/packages/runtime/src/scheduler/multi-agent-coordinator.ts +106 -4
- package/packages/runtime/src/scheduler/run-journal.ts +62 -0
- package/packages/runtime/src/scheduler/scheduler.ts +168 -8
- package/packages/runtime/src/verification/index.ts +1 -0
- package/packages/runtime/src/verification/verification-manifest.ts +192 -0
- package/vscode-extension/oxe-agents-1.0.0.vsix +0 -0
|
@@ -16,3 +16,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./plugin-abi"), exports);
|
|
18
18
|
__exportStar(require("./plugin-registry"), exports);
|
|
19
|
+
__exportStar(require("./plugin-manifest"), exports);
|
|
20
|
+
__exportStar(require("./capability-matrix"), exports);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { OxePlugin } from './plugin-abi';
|
|
2
|
+
export declare const CURRENT_ABI_VERSION = "1.0.0";
|
|
3
|
+
export interface PluginManifest {
|
|
4
|
+
name: string;
|
|
5
|
+
version: string;
|
|
6
|
+
abi_version: string;
|
|
7
|
+
capabilities: Array<'tool' | 'workspace' | 'verifier' | 'context' | 'hooks'>;
|
|
8
|
+
tool_action_types?: string[];
|
|
9
|
+
workspace_strategies?: string[];
|
|
10
|
+
verifier_check_types?: string[];
|
|
11
|
+
context_provider_names?: string[];
|
|
12
|
+
hook_names?: string[];
|
|
13
|
+
}
|
|
14
|
+
export interface PluginValidationResult {
|
|
15
|
+
valid: boolean;
|
|
16
|
+
errors: string[];
|
|
17
|
+
warnings: string[];
|
|
18
|
+
}
|
|
19
|
+
export declare function extractManifest(plugin: OxePlugin): PluginManifest;
|
|
20
|
+
export declare function validatePlugin(plugin: OxePlugin): PluginValidationResult;
|
|
21
|
+
export declare function isAbiCompatible(pluginAbiVersion: string): boolean;
|
|
22
|
+
export declare function sandboxInvoke<T>(fn: () => Promise<T>, timeoutMs?: number): Promise<T>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CURRENT_ABI_VERSION = void 0;
|
|
4
|
+
exports.extractManifest = extractManifest;
|
|
5
|
+
exports.validatePlugin = validatePlugin;
|
|
6
|
+
exports.isAbiCompatible = isAbiCompatible;
|
|
7
|
+
exports.sandboxInvoke = sandboxInvoke;
|
|
8
|
+
exports.CURRENT_ABI_VERSION = '1.0.0';
|
|
9
|
+
function extractManifest(plugin) {
|
|
10
|
+
const capabilities = [];
|
|
11
|
+
if (plugin.toolProviders?.length)
|
|
12
|
+
capabilities.push('tool');
|
|
13
|
+
if (plugin.workspaceProviders?.length)
|
|
14
|
+
capabilities.push('workspace');
|
|
15
|
+
if (plugin.verifierProviders?.length)
|
|
16
|
+
capabilities.push('verifier');
|
|
17
|
+
if (plugin.contextProviders?.length)
|
|
18
|
+
capabilities.push('context');
|
|
19
|
+
if (plugin.hooks && Object.keys(plugin.hooks).length > 0)
|
|
20
|
+
capabilities.push('hooks');
|
|
21
|
+
return {
|
|
22
|
+
name: plugin.name,
|
|
23
|
+
version: plugin.version ?? '0.0.0',
|
|
24
|
+
abi_version: exports.CURRENT_ABI_VERSION,
|
|
25
|
+
capabilities,
|
|
26
|
+
tool_action_types: plugin.toolProviders?.flatMap((p) => ['read_code', 'generate_patch', 'run_tests', 'collect_evidence', 'custom'].filter((t) => p.supports(t))) ?? [],
|
|
27
|
+
workspace_strategies: plugin.workspaceProviders?.map((p) => p.name) ?? [],
|
|
28
|
+
verifier_check_types: plugin.verifierProviders?.flatMap((p) => ['unit', 'integration', 'smoke', 'policy', 'security', 'custom'].filter((t) => p.supports(t))) ?? [],
|
|
29
|
+
context_provider_names: plugin.contextProviders?.map((p) => p.name) ?? [],
|
|
30
|
+
hook_names: plugin.hooks ? Object.keys(plugin.hooks) : [],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function validatePlugin(plugin) {
|
|
34
|
+
const errors = [];
|
|
35
|
+
const warnings = [];
|
|
36
|
+
if (!plugin.name || typeof plugin.name !== 'string') {
|
|
37
|
+
errors.push('Plugin must have a non-empty string name');
|
|
38
|
+
}
|
|
39
|
+
if (plugin.version && !/^\d+\.\d+\.\d+/.test(plugin.version)) {
|
|
40
|
+
warnings.push(`Plugin version "${plugin.version}" does not follow semver`);
|
|
41
|
+
}
|
|
42
|
+
if (!plugin.toolProviders?.length &&
|
|
43
|
+
!plugin.workspaceProviders?.length &&
|
|
44
|
+
!plugin.verifierProviders?.length &&
|
|
45
|
+
!plugin.contextProviders?.length &&
|
|
46
|
+
!plugin.hooks) {
|
|
47
|
+
warnings.push('Plugin declares no providers or hooks — it has no effect');
|
|
48
|
+
}
|
|
49
|
+
// Validate each tool provider
|
|
50
|
+
for (const tp of plugin.toolProviders ?? []) {
|
|
51
|
+
if (!tp.name)
|
|
52
|
+
errors.push('ToolProvider missing name');
|
|
53
|
+
if (typeof tp.supports !== 'function')
|
|
54
|
+
errors.push(`ToolProvider "${tp.name}" missing supports() method`);
|
|
55
|
+
if (typeof tp.invoke !== 'function')
|
|
56
|
+
errors.push(`ToolProvider "${tp.name}" missing invoke() method`);
|
|
57
|
+
}
|
|
58
|
+
// Validate each workspace provider
|
|
59
|
+
for (const wp of plugin.workspaceProviders ?? []) {
|
|
60
|
+
if (!wp.name)
|
|
61
|
+
errors.push('WorkspaceProvider missing name');
|
|
62
|
+
if (typeof wp.supportsStrategy !== 'function')
|
|
63
|
+
errors.push(`WorkspaceProvider "${wp.name}" missing supportsStrategy()`);
|
|
64
|
+
if (typeof wp.allocate !== 'function')
|
|
65
|
+
errors.push(`WorkspaceProvider "${wp.name}" missing allocate()`);
|
|
66
|
+
}
|
|
67
|
+
// Validate each verifier provider
|
|
68
|
+
for (const vp of plugin.verifierProviders ?? []) {
|
|
69
|
+
if (!vp.name)
|
|
70
|
+
errors.push('VerifierProvider missing name');
|
|
71
|
+
if (typeof vp.supports !== 'function')
|
|
72
|
+
errors.push(`VerifierProvider "${vp.name}" missing supports()`);
|
|
73
|
+
if (typeof vp.execute !== 'function')
|
|
74
|
+
errors.push(`VerifierProvider "${vp.name}" missing execute()`);
|
|
75
|
+
}
|
|
76
|
+
return { valid: errors.length === 0, errors, warnings };
|
|
77
|
+
}
|
|
78
|
+
function isAbiCompatible(pluginAbiVersion) {
|
|
79
|
+
// Major version must match; minor/patch are backwards-compatible
|
|
80
|
+
const [currMajor] = exports.CURRENT_ABI_VERSION.split('.').map(Number);
|
|
81
|
+
const [plugMajor] = pluginAbiVersion.split('.').map(Number);
|
|
82
|
+
return currMajor === plugMajor;
|
|
83
|
+
}
|
|
84
|
+
function sandboxInvoke(fn, timeoutMs = 10000) {
|
|
85
|
+
return new Promise((resolve, reject) => {
|
|
86
|
+
const timer = setTimeout(() => {
|
|
87
|
+
reject(new Error(`Plugin invocation timed out after ${timeoutMs}ms`));
|
|
88
|
+
}, timeoutMs);
|
|
89
|
+
fn().then((result) => { clearTimeout(timer); resolve(result); }, (err) => { clearTimeout(timer); reject(err instanceof Error ? err : new Error(String(err))); });
|
|
90
|
+
});
|
|
91
|
+
}
|
|
@@ -8,6 +8,7 @@ exports.globalRegistry = globalRegistry;
|
|
|
8
8
|
exports.resetGlobalRegistry = resetGlobalRegistry;
|
|
9
9
|
const fs_1 = __importDefault(require("fs"));
|
|
10
10
|
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const plugin_manifest_1 = require("./plugin-manifest");
|
|
11
12
|
class PluginRegistry {
|
|
12
13
|
constructor() {
|
|
13
14
|
this.plugins = [];
|
|
@@ -16,6 +17,10 @@ class PluginRegistry {
|
|
|
16
17
|
if (this.plugins.some((p) => p.name === plugin.name)) {
|
|
17
18
|
throw new Error(`Plugin "${plugin.name}" is already registered`);
|
|
18
19
|
}
|
|
20
|
+
const validation = (0, plugin_manifest_1.validatePlugin)(plugin);
|
|
21
|
+
if (!validation.valid && validation.errors.length > 0) {
|
|
22
|
+
throw new Error(`Plugin "${plugin.name}" failed validation: ${validation.errors.join('; ')}`);
|
|
23
|
+
}
|
|
19
24
|
this.plugins.push(plugin);
|
|
20
25
|
}
|
|
21
26
|
unregister(name) {
|
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
export type PolicyAction = 'allow' | 'deny' | 'require_human_gate';
|
|
2
|
+
export type SideEffectClass = 'read_fs' | 'write_fs' | 'spawn_process' | 'network_call' | 'git_mutation' | 'db_change' | 'secret_access' | 'infra_operation';
|
|
3
|
+
export type AutonomyTier = 'L0' | 'L1' | 'L2' | 'L3';
|
|
2
4
|
export interface PolicyWhenClause {
|
|
3
5
|
tool?: string;
|
|
4
6
|
env?: string;
|
|
5
7
|
kind?: string;
|
|
8
|
+
side_effect_class?: SideEffectClass;
|
|
9
|
+
autonomy_tier?: AutonomyTier;
|
|
6
10
|
}
|
|
7
11
|
export interface PolicyAssertClause {
|
|
8
12
|
diff_within_scope?: boolean;
|
|
9
13
|
}
|
|
14
|
+
export interface NodePolicyConfig {
|
|
15
|
+
max_retries: number;
|
|
16
|
+
mutation_budget?: number;
|
|
17
|
+
autonomy_tier?: AutonomyTier;
|
|
18
|
+
allowed_side_effects?: SideEffectClass[];
|
|
19
|
+
}
|
|
20
|
+
export interface EnvironmentGuardrail {
|
|
21
|
+
protected_paths: string[];
|
|
22
|
+
protected_branches: string[];
|
|
23
|
+
require_human_gate_on: SideEffectClass[];
|
|
24
|
+
}
|
|
10
25
|
export interface PolicyRule {
|
|
11
26
|
id: string;
|
|
12
27
|
when: PolicyWhenClause;
|
|
@@ -19,6 +34,10 @@ export interface PolicyContext {
|
|
|
19
34
|
kind?: string;
|
|
20
35
|
mutation_scope?: string[];
|
|
21
36
|
affected_paths?: string[];
|
|
37
|
+
side_effect_class?: SideEffectClass;
|
|
38
|
+
autonomy_tier?: AutonomyTier;
|
|
39
|
+
mutation_count?: number;
|
|
40
|
+
node_policy?: NodePolicyConfig;
|
|
22
41
|
}
|
|
23
42
|
export interface PolicyDecision {
|
|
24
43
|
allowed: boolean;
|
|
@@ -28,13 +47,21 @@ export interface PolicyDecision {
|
|
|
28
47
|
}
|
|
29
48
|
export declare class PolicyEngine {
|
|
30
49
|
private readonly rules;
|
|
31
|
-
|
|
50
|
+
private readonly guardrail;
|
|
51
|
+
constructor(rules?: PolicyRule[], guardrail?: EnvironmentGuardrail);
|
|
32
52
|
evaluate(ctx: PolicyContext): PolicyDecision;
|
|
53
|
+
private checkGuardrails;
|
|
54
|
+
private checkAutonomyTier;
|
|
55
|
+
private checkMutationBudget;
|
|
33
56
|
private matches;
|
|
34
57
|
private checkAssert;
|
|
35
58
|
withRule(rule: PolicyRule): PolicyEngine;
|
|
59
|
+
withGuardrail(guardrail: EnvironmentGuardrail): PolicyEngine;
|
|
60
|
+
getGuardrail(): EnvironmentGuardrail;
|
|
36
61
|
static fromConfig(config: {
|
|
37
62
|
policies?: PolicyRule[];
|
|
63
|
+
guardrail?: EnvironmentGuardrail;
|
|
38
64
|
}): PolicyEngine;
|
|
39
65
|
static fromConfigFile(configPath: string): PolicyEngine;
|
|
66
|
+
static defaultGuardrail(): EnvironmentGuardrail;
|
|
40
67
|
}
|
|
@@ -7,11 +7,37 @@ const ALLOW_ALL = {
|
|
|
7
7
|
reason: 'no matching policy — default allow',
|
|
8
8
|
rule_id: null,
|
|
9
9
|
};
|
|
10
|
+
const DEFAULT_GUARDRAIL = {
|
|
11
|
+
protected_paths: ['.oxe/config.json', '.env', 'package.json'],
|
|
12
|
+
protected_branches: ['main', 'master', 'production', 'release'],
|
|
13
|
+
require_human_gate_on: ['infra_operation', 'db_change', 'secret_access'],
|
|
14
|
+
};
|
|
15
|
+
// Autonomy tier → max side effect class allowed without a gate
|
|
16
|
+
const TIER_SIDE_EFFECT_MAP = {
|
|
17
|
+
L0: ['read_fs'],
|
|
18
|
+
L1: ['read_fs', 'write_fs', 'spawn_process'],
|
|
19
|
+
L2: ['read_fs', 'write_fs', 'spawn_process', 'network_call', 'git_mutation'],
|
|
20
|
+
L3: ['read_fs', 'write_fs', 'spawn_process', 'network_call', 'git_mutation', 'db_change', 'secret_access', 'infra_operation'],
|
|
21
|
+
};
|
|
10
22
|
class PolicyEngine {
|
|
11
|
-
constructor(rules = []) {
|
|
23
|
+
constructor(rules = [], guardrail = DEFAULT_GUARDRAIL) {
|
|
12
24
|
this.rules = rules;
|
|
25
|
+
this.guardrail = guardrail;
|
|
13
26
|
}
|
|
14
27
|
evaluate(ctx) {
|
|
28
|
+
// Check autonomy tier first — a denial takes priority over guardrail gates
|
|
29
|
+
const tierDecision = this.checkAutonomyTier(ctx);
|
|
30
|
+
if (tierDecision)
|
|
31
|
+
return tierDecision;
|
|
32
|
+
// Check environment guardrails (may require gate even when tier permits)
|
|
33
|
+
const guardrailDecision = this.checkGuardrails(ctx);
|
|
34
|
+
if (guardrailDecision)
|
|
35
|
+
return guardrailDecision;
|
|
36
|
+
// Check mutation budget
|
|
37
|
+
const budgetDecision = this.checkMutationBudget(ctx);
|
|
38
|
+
if (budgetDecision)
|
|
39
|
+
return budgetDecision;
|
|
40
|
+
// Evaluate rules (first match wins)
|
|
15
41
|
for (const rule of this.rules) {
|
|
16
42
|
if (!this.matches(rule.when, ctx))
|
|
17
43
|
continue;
|
|
@@ -37,6 +63,59 @@ class PolicyEngine {
|
|
|
37
63
|
}
|
|
38
64
|
return ALLOW_ALL;
|
|
39
65
|
}
|
|
66
|
+
checkGuardrails(ctx) {
|
|
67
|
+
// Protected path check
|
|
68
|
+
const affected = ctx.affected_paths ?? [];
|
|
69
|
+
for (const p of affected) {
|
|
70
|
+
if (this.guardrail.protected_paths.some((pp) => p === pp || p.startsWith(pp + '/'))) {
|
|
71
|
+
return {
|
|
72
|
+
allowed: true,
|
|
73
|
+
gate_required: true,
|
|
74
|
+
reason: `Protected path affected: ${p}`,
|
|
75
|
+
rule_id: '__guardrail_path',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Side effect class requiring gate
|
|
80
|
+
if (ctx.side_effect_class && this.guardrail.require_human_gate_on.includes(ctx.side_effect_class)) {
|
|
81
|
+
return {
|
|
82
|
+
allowed: true,
|
|
83
|
+
gate_required: true,
|
|
84
|
+
reason: `Side effect class '${ctx.side_effect_class}' requires human gate`,
|
|
85
|
+
rule_id: '__guardrail_side_effect',
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
checkAutonomyTier(ctx) {
|
|
91
|
+
if (!ctx.autonomy_tier || !ctx.side_effect_class)
|
|
92
|
+
return null;
|
|
93
|
+
const allowed = TIER_SIDE_EFFECT_MAP[ctx.autonomy_tier] ?? [];
|
|
94
|
+
if (!allowed.includes(ctx.side_effect_class)) {
|
|
95
|
+
return {
|
|
96
|
+
allowed: false,
|
|
97
|
+
gate_required: false,
|
|
98
|
+
reason: `Autonomy tier ${ctx.autonomy_tier} does not permit side effect '${ctx.side_effect_class}'`,
|
|
99
|
+
rule_id: '__autonomy_tier',
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
checkMutationBudget(ctx) {
|
|
105
|
+
const budget = ctx.node_policy?.mutation_budget;
|
|
106
|
+
if (budget === undefined || budget === null)
|
|
107
|
+
return null;
|
|
108
|
+
const count = ctx.mutation_count ?? 0;
|
|
109
|
+
if (count >= budget) {
|
|
110
|
+
return {
|
|
111
|
+
allowed: false,
|
|
112
|
+
gate_required: false,
|
|
113
|
+
reason: `Mutation budget exhausted: ${count}/${budget}`,
|
|
114
|
+
rule_id: '__mutation_budget',
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
40
119
|
matches(when, ctx) {
|
|
41
120
|
if (when.tool && when.tool !== ctx.tool)
|
|
42
121
|
return false;
|
|
@@ -44,6 +123,10 @@ class PolicyEngine {
|
|
|
44
123
|
return false;
|
|
45
124
|
if (when.kind && when.kind !== ctx.kind)
|
|
46
125
|
return false;
|
|
126
|
+
if (when.side_effect_class && when.side_effect_class !== ctx.side_effect_class)
|
|
127
|
+
return false;
|
|
128
|
+
if (when.autonomy_tier && when.autonomy_tier !== ctx.autonomy_tier)
|
|
129
|
+
return false;
|
|
47
130
|
return true;
|
|
48
131
|
}
|
|
49
132
|
checkAssert(assert, ctx) {
|
|
@@ -51,7 +134,7 @@ class PolicyEngine {
|
|
|
51
134
|
const scope = ctx.mutation_scope ?? [];
|
|
52
135
|
const affected = ctx.affected_paths ?? [];
|
|
53
136
|
if (scope.length === 0)
|
|
54
|
-
return null;
|
|
137
|
+
return null;
|
|
55
138
|
const outsideScope = affected.filter((p) => !scope.some((s) => p.startsWith(s) || s.startsWith(p)));
|
|
56
139
|
if (outsideScope.length > 0) {
|
|
57
140
|
return `paths outside mutation scope: ${outsideScope.join(', ')}`;
|
|
@@ -60,14 +143,19 @@ class PolicyEngine {
|
|
|
60
143
|
return null;
|
|
61
144
|
}
|
|
62
145
|
withRule(rule) {
|
|
63
|
-
return new PolicyEngine([...this.rules, rule]);
|
|
146
|
+
return new PolicyEngine([...this.rules, rule], this.guardrail);
|
|
147
|
+
}
|
|
148
|
+
withGuardrail(guardrail) {
|
|
149
|
+
return new PolicyEngine(this.rules, guardrail);
|
|
150
|
+
}
|
|
151
|
+
getGuardrail() {
|
|
152
|
+
return this.guardrail;
|
|
64
153
|
}
|
|
65
154
|
static fromConfig(config) {
|
|
66
|
-
return new PolicyEngine(config.policies ?? []);
|
|
155
|
+
return new PolicyEngine(config.policies ?? [], config.guardrail ?? DEFAULT_GUARDRAIL);
|
|
67
156
|
}
|
|
68
157
|
static fromConfigFile(configPath) {
|
|
69
158
|
try {
|
|
70
|
-
// Dynamic require to avoid bundling issues
|
|
71
159
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
72
160
|
const cfg = require(configPath);
|
|
73
161
|
return PolicyEngine.fromConfig(cfg);
|
|
@@ -76,5 +164,8 @@ class PolicyEngine {
|
|
|
76
164
|
return new PolicyEngine();
|
|
77
165
|
}
|
|
78
166
|
}
|
|
167
|
+
static defaultGuardrail() {
|
|
168
|
+
return { ...DEFAULT_GUARDRAIL };
|
|
169
|
+
}
|
|
79
170
|
}
|
|
80
171
|
exports.PolicyEngine = PolicyEngine;
|
|
@@ -3,6 +3,17 @@ import type { Run } from '../models/run';
|
|
|
3
3
|
import type { WorkItem } from '../models/work-item';
|
|
4
4
|
import type { Attempt } from '../models/attempt';
|
|
5
5
|
import type { Workspace } from '../models/workspace';
|
|
6
|
+
export interface PolicyDecisionRecord {
|
|
7
|
+
allowed: boolean;
|
|
8
|
+
gate_required: boolean;
|
|
9
|
+
reason: string;
|
|
10
|
+
rule_id: string | null;
|
|
11
|
+
}
|
|
12
|
+
export interface ToolFailureRecord {
|
|
13
|
+
tool: string;
|
|
14
|
+
error: string;
|
|
15
|
+
timestamp: string;
|
|
16
|
+
}
|
|
6
17
|
export interface RunState {
|
|
7
18
|
run: Run | null;
|
|
8
19
|
workItems: Map<string, WorkItem>;
|
|
@@ -11,6 +22,16 @@ export interface RunState {
|
|
|
11
22
|
completedWorkItems: Set<string>;
|
|
12
23
|
failedWorkItems: Set<string>;
|
|
13
24
|
blockedWorkItems: Set<string>;
|
|
25
|
+
retryCounts: Map<string, number>;
|
|
26
|
+
policyDecisions: Map<string, PolicyDecisionRecord>;
|
|
27
|
+
pendingGates: Set<string>;
|
|
28
|
+
resolvedGates: Map<string, {
|
|
29
|
+
decision: string;
|
|
30
|
+
actor?: string;
|
|
31
|
+
}>;
|
|
32
|
+
verificationStatus: Map<string, 'started' | 'completed' | 'failed'>;
|
|
33
|
+
evidenceRefs: Map<string, string[]>;
|
|
34
|
+
toolFailures: Map<string, ToolFailureRecord[]>;
|
|
14
35
|
}
|
|
15
36
|
export declare function createEmptyRunState(): RunState;
|
|
16
37
|
export declare function reduce(events: OxeEvent[]): RunState;
|
|
@@ -18,3 +39,8 @@ export { applyEvent as applyEventExported };
|
|
|
18
39
|
declare function applyEvent(state: RunState, event: OxeEvent): RunState;
|
|
19
40
|
export declare function getWorkItemStatus(state: RunState, workItemId: string): WorkItem['status'] | null;
|
|
20
41
|
export declare function getAttemptCount(state: RunState, workItemId: string): number;
|
|
42
|
+
export declare function getRetryCount(state: RunState, workItemId: string): number;
|
|
43
|
+
export declare function getPolicyDecision(state: RunState, workItemId: string): PolicyDecisionRecord | null;
|
|
44
|
+
export declare function getVerificationStatus(state: RunState, workItemId: string): 'started' | 'completed' | 'failed' | null;
|
|
45
|
+
export declare function getEvidenceRefs(state: RunState, workItemId: string): string[];
|
|
46
|
+
export declare function getToolFailures(state: RunState, workItemId: string): ToolFailureRecord[];
|
|
@@ -5,6 +5,11 @@ exports.reduce = reduce;
|
|
|
5
5
|
exports.applyEventExported = applyEvent;
|
|
6
6
|
exports.getWorkItemStatus = getWorkItemStatus;
|
|
7
7
|
exports.getAttemptCount = getAttemptCount;
|
|
8
|
+
exports.getRetryCount = getRetryCount;
|
|
9
|
+
exports.getPolicyDecision = getPolicyDecision;
|
|
10
|
+
exports.getVerificationStatus = getVerificationStatus;
|
|
11
|
+
exports.getEvidenceRefs = getEvidenceRefs;
|
|
12
|
+
exports.getToolFailures = getToolFailures;
|
|
8
13
|
function createEmptyRunState() {
|
|
9
14
|
return {
|
|
10
15
|
run: null,
|
|
@@ -14,6 +19,13 @@ function createEmptyRunState() {
|
|
|
14
19
|
completedWorkItems: new Set(),
|
|
15
20
|
failedWorkItems: new Set(),
|
|
16
21
|
blockedWorkItems: new Set(),
|
|
22
|
+
retryCounts: new Map(),
|
|
23
|
+
policyDecisions: new Map(),
|
|
24
|
+
pendingGates: new Set(),
|
|
25
|
+
resolvedGates: new Map(),
|
|
26
|
+
verificationStatus: new Map(),
|
|
27
|
+
evidenceRefs: new Map(),
|
|
28
|
+
toolFailures: new Map(),
|
|
17
29
|
};
|
|
18
30
|
}
|
|
19
31
|
function reduce(events) {
|
|
@@ -43,7 +55,6 @@ function applyEvent(state, event) {
|
|
|
43
55
|
workItems.set(event.work_item_id, { ...existing, status: 'ready' });
|
|
44
56
|
}
|
|
45
57
|
else {
|
|
46
|
-
// First time we see this work item — create from payload
|
|
47
58
|
const item = event.payload;
|
|
48
59
|
workItems.set(event.work_item_id, { ...item, work_item_id: event.work_item_id, status: 'ready' });
|
|
49
60
|
}
|
|
@@ -85,6 +96,14 @@ function applyEvent(state, event) {
|
|
|
85
96
|
workItems.set(event.work_item_id, { ...item, status: 'completed' });
|
|
86
97
|
const completedWorkItems = new Set(state.completedWorkItems);
|
|
87
98
|
completedWorkItems.add(event.work_item_id);
|
|
99
|
+
// Collect evidence refs from payload
|
|
100
|
+
const evidence = event.payload.evidence ?? [];
|
|
101
|
+
if (evidence.length > 0) {
|
|
102
|
+
const evidenceRefs = new Map(state.evidenceRefs);
|
|
103
|
+
const existing = evidenceRefs.get(event.work_item_id) ?? [];
|
|
104
|
+
evidenceRefs.set(event.work_item_id, [...existing, ...evidence]);
|
|
105
|
+
return { ...state, workItems, completedWorkItems, evidenceRefs };
|
|
106
|
+
}
|
|
88
107
|
return { ...state, workItems, completedWorkItems };
|
|
89
108
|
}
|
|
90
109
|
case 'WorkItemBlocked': {
|
|
@@ -98,6 +117,88 @@ function applyEvent(state, event) {
|
|
|
98
117
|
blockedWorkItems.add(event.work_item_id);
|
|
99
118
|
return { ...state, workItems, blockedWorkItems };
|
|
100
119
|
}
|
|
120
|
+
case 'RetryScheduled': {
|
|
121
|
+
if (!event.work_item_id)
|
|
122
|
+
return state;
|
|
123
|
+
const retryCounts = new Map(state.retryCounts);
|
|
124
|
+
const current = retryCounts.get(event.work_item_id) ?? 0;
|
|
125
|
+
retryCounts.set(event.work_item_id, current + 1);
|
|
126
|
+
return { ...state, retryCounts };
|
|
127
|
+
}
|
|
128
|
+
case 'PolicyEvaluated': {
|
|
129
|
+
const p = event.payload;
|
|
130
|
+
const key = p.work_item_id ?? event.work_item_id;
|
|
131
|
+
if (!key)
|
|
132
|
+
return state;
|
|
133
|
+
const policyDecisions = new Map(state.policyDecisions);
|
|
134
|
+
policyDecisions.set(key, {
|
|
135
|
+
allowed: p.allowed ?? true,
|
|
136
|
+
gate_required: p.gate_required ?? false,
|
|
137
|
+
reason: p.reason ?? '',
|
|
138
|
+
rule_id: p.rule_id ?? null,
|
|
139
|
+
});
|
|
140
|
+
return { ...state, policyDecisions };
|
|
141
|
+
}
|
|
142
|
+
case 'GateRequested': {
|
|
143
|
+
const gateId = event.payload.gate_id;
|
|
144
|
+
if (!gateId)
|
|
145
|
+
return state;
|
|
146
|
+
const pendingGates = new Set(state.pendingGates);
|
|
147
|
+
pendingGates.add(gateId);
|
|
148
|
+
return { ...state, pendingGates };
|
|
149
|
+
}
|
|
150
|
+
case 'GateResolved': {
|
|
151
|
+
const p = event.payload;
|
|
152
|
+
if (!p.gate_id)
|
|
153
|
+
return state;
|
|
154
|
+
const pendingGates = new Set(state.pendingGates);
|
|
155
|
+
pendingGates.delete(p.gate_id);
|
|
156
|
+
const resolvedGates = new Map(state.resolvedGates);
|
|
157
|
+
resolvedGates.set(p.gate_id, { decision: p.decision ?? 'approved', actor: p.actor });
|
|
158
|
+
return { ...state, pendingGates, resolvedGates };
|
|
159
|
+
}
|
|
160
|
+
case 'VerificationStarted': {
|
|
161
|
+
const key = event.work_item_id ?? event.payload.work_item_id;
|
|
162
|
+
if (!key)
|
|
163
|
+
return state;
|
|
164
|
+
const verificationStatus = new Map(state.verificationStatus);
|
|
165
|
+
verificationStatus.set(key, 'started');
|
|
166
|
+
return { ...state, verificationStatus };
|
|
167
|
+
}
|
|
168
|
+
case 'VerificationCompleted': {
|
|
169
|
+
const p = event.payload;
|
|
170
|
+
const key = event.work_item_id ?? p.work_item_id;
|
|
171
|
+
if (!key)
|
|
172
|
+
return state;
|
|
173
|
+
const verificationStatus = new Map(state.verificationStatus);
|
|
174
|
+
verificationStatus.set(key, p.status ?? 'completed');
|
|
175
|
+
return { ...state, verificationStatus };
|
|
176
|
+
}
|
|
177
|
+
case 'ToolFailed': {
|
|
178
|
+
if (!event.work_item_id)
|
|
179
|
+
return state;
|
|
180
|
+
const p = event.payload;
|
|
181
|
+
const toolFailures = new Map(state.toolFailures);
|
|
182
|
+
const existing = toolFailures.get(event.work_item_id) ?? [];
|
|
183
|
+
toolFailures.set(event.work_item_id, [
|
|
184
|
+
...existing,
|
|
185
|
+
{ tool: p.tool ?? 'unknown', error: p.error ?? '', timestamp: event.timestamp },
|
|
186
|
+
]);
|
|
187
|
+
return { ...state, toolFailures };
|
|
188
|
+
}
|
|
189
|
+
case 'EvidenceCollected': {
|
|
190
|
+
const p = event.payload;
|
|
191
|
+
const key = event.work_item_id ?? p.work_item_id;
|
|
192
|
+
if (!key)
|
|
193
|
+
return state;
|
|
194
|
+
const refs = p.refs ?? (p.ref ? [p.ref] : []);
|
|
195
|
+
if (refs.length === 0)
|
|
196
|
+
return state;
|
|
197
|
+
const evidenceRefs = new Map(state.evidenceRefs);
|
|
198
|
+
const existing = evidenceRefs.get(key) ?? [];
|
|
199
|
+
evidenceRefs.set(key, [...existing, ...refs]);
|
|
200
|
+
return { ...state, evidenceRefs };
|
|
201
|
+
}
|
|
101
202
|
default:
|
|
102
203
|
return state;
|
|
103
204
|
}
|
|
@@ -108,3 +209,18 @@ function getWorkItemStatus(state, workItemId) {
|
|
|
108
209
|
function getAttemptCount(state, workItemId) {
|
|
109
210
|
return state.attempts.get(workItemId)?.length ?? 0;
|
|
110
211
|
}
|
|
212
|
+
function getRetryCount(state, workItemId) {
|
|
213
|
+
return state.retryCounts.get(workItemId) ?? 0;
|
|
214
|
+
}
|
|
215
|
+
function getPolicyDecision(state, workItemId) {
|
|
216
|
+
return state.policyDecisions.get(workItemId) ?? null;
|
|
217
|
+
}
|
|
218
|
+
function getVerificationStatus(state, workItemId) {
|
|
219
|
+
return state.verificationStatus.get(workItemId) ?? null;
|
|
220
|
+
}
|
|
221
|
+
function getEvidenceRefs(state, workItemId) {
|
|
222
|
+
return state.evidenceRefs.get(workItemId) ?? [];
|
|
223
|
+
}
|
|
224
|
+
function getToolFailures(state, workItemId) {
|
|
225
|
+
return state.toolFailures.get(workItemId) ?? [];
|
|
226
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { TaskExecutor } from './scheduler';
|
|
2
|
+
import type { WorkspaceManager } from '../workspace/workspace-manager';
|
|
3
|
+
import type { AgentRole, AgentActionLog } from './agent-roles';
|
|
4
|
+
export type AgentStatus = 'idle' | 'running' | 'paused' | 'failed' | 'timeout';
|
|
5
|
+
export interface AgentHeartbeat {
|
|
6
|
+
agent_id: string;
|
|
7
|
+
last_seen: string;
|
|
8
|
+
current_task: string | null;
|
|
9
|
+
status: AgentStatus;
|
|
10
|
+
}
|
|
11
|
+
export interface RegisteredAgent {
|
|
12
|
+
id: string;
|
|
13
|
+
executor: TaskExecutor;
|
|
14
|
+
workspaceManager: WorkspaceManager;
|
|
15
|
+
assignedTaskIds: string[];
|
|
16
|
+
heartbeat: AgentHeartbeat;
|
|
17
|
+
role?: AgentRole;
|
|
18
|
+
actionLog: AgentActionLog[];
|
|
19
|
+
}
|
|
20
|
+
export declare class AgentRegistry {
|
|
21
|
+
private agents;
|
|
22
|
+
private readonly heartbeatTimeoutMs;
|
|
23
|
+
constructor(heartbeatTimeoutMs?: number);
|
|
24
|
+
register(id: string, executor: TaskExecutor, workspaceManager: WorkspaceManager, assignedTaskIds?: string[], role?: AgentRole): RegisteredAgent;
|
|
25
|
+
unregister(id: string): void;
|
|
26
|
+
beat(id: string, currentTask?: string | null): void;
|
|
27
|
+
setStatus(id: string, status: AgentStatus): void;
|
|
28
|
+
isAlive(id: string): boolean;
|
|
29
|
+
/** Returns agents that haven't sent a heartbeat within the timeout window */
|
|
30
|
+
timedOut(): RegisteredAgent[];
|
|
31
|
+
liveAgents(): RegisteredAgent[];
|
|
32
|
+
get(id: string): RegisteredAgent | null;
|
|
33
|
+
list(): RegisteredAgent[];
|
|
34
|
+
/**
|
|
35
|
+
* Reassign orphaned tasks from timed-out agents to a fallback agent.
|
|
36
|
+
* Returns the list of task IDs that were reassigned.
|
|
37
|
+
*/
|
|
38
|
+
failover(fallbackAgentId: string): string[];
|
|
39
|
+
/** Return all agents assigned to a given role */
|
|
40
|
+
getByRole(role: AgentRole): RegisteredAgent[];
|
|
41
|
+
/** Append an action log entry for a registered agent (no-op if unknown) */
|
|
42
|
+
logAction(agentId: string, log: AgentActionLog): void;
|
|
43
|
+
clear(): void;
|
|
44
|
+
}
|