mova_agent 0.1.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/README.md +94 -0
- package/build/scripts/check_docs.d.ts +2 -0
- package/build/scripts/check_docs.d.ts.map +1 -0
- package/build/scripts/check_docs.js +48 -0
- package/build/scripts/check_docs.js.map +1 -0
- package/build/scripts/check_schema_paths.d.ts +3 -0
- package/build/scripts/check_schema_paths.d.ts.map +1 -0
- package/build/scripts/check_schema_paths.js +15 -0
- package/build/scripts/check_schema_paths.js.map +1 -0
- package/build/scripts/check_schema_paths_dist.d.ts +3 -0
- package/build/scripts/check_schema_paths_dist.d.ts.map +1 -0
- package/build/scripts/check_schema_paths_dist.js +16 -0
- package/build/scripts/check_schema_paths_dist.js.map +1 -0
- package/build/scripts/check_structure.d.ts +2 -0
- package/build/scripts/check_structure.d.ts.map +1 -0
- package/build/scripts/check_structure.js +59 -0
- package/build/scripts/check_structure.js.map +1 -0
- package/build/scripts/debug_validate.d.ts +3 -0
- package/build/scripts/debug_validate.d.ts.map +1 -0
- package/build/scripts/debug_validate.js +39 -0
- package/build/scripts/debug_validate.js.map +1 -0
- package/build/scripts/debug_validate2.d.ts +3 -0
- package/build/scripts/debug_validate2.d.ts.map +1 -0
- package/build/scripts/debug_validate2.js +18 -0
- package/build/scripts/debug_validate2.js.map +1 -0
- package/build/scripts/debug_validate_runtime.d.ts +3 -0
- package/build/scripts/debug_validate_runtime.d.ts.map +1 -0
- package/build/scripts/debug_validate_runtime.js +39 -0
- package/build/scripts/debug_validate_runtime.js.map +1 -0
- package/build/scripts/dump_ajv_schemas.d.ts +2 -0
- package/build/scripts/dump_ajv_schemas.d.ts.map +1 -0
- package/build/scripts/dump_ajv_schemas.js +22 -0
- package/build/scripts/dump_ajv_schemas.js.map +1 -0
- package/build/scripts/dump_resolved_schema.d.ts +3 -0
- package/build/scripts/dump_resolved_schema.d.ts.map +1 -0
- package/build/scripts/dump_resolved_schema.js +22 -0
- package/build/scripts/dump_resolved_schema.js.map +1 -0
- package/build/scripts/gen_types.d.ts +2 -0
- package/build/scripts/gen_types.d.ts.map +1 -0
- package/build/scripts/gen_types.js +133 -0
- package/build/scripts/gen_types.js.map +1 -0
- package/build/scripts/inspect_validation.d.ts +3 -0
- package/build/scripts/inspect_validation.d.ts.map +1 -0
- package/build/scripts/inspect_validation.js +36 -0
- package/build/scripts/inspect_validation.js.map +1 -0
- package/build/scripts/legacy/check_errors.d.ts +3 -0
- package/build/scripts/legacy/check_errors.d.ts.map +1 -0
- package/build/scripts/legacy/check_errors.js +14 -0
- package/build/scripts/legacy/check_errors.js.map +1 -0
- package/build/scripts/legacy/compile_test.d.ts +5 -0
- package/build/scripts/legacy/compile_test.d.ts.map +1 -0
- package/build/scripts/legacy/compile_test.js +25 -0
- package/build/scripts/legacy/compile_test.js.map +1 -0
- package/build/scripts/legacy/debug_neg.d.ts +6 -0
- package/build/scripts/legacy/debug_neg.d.ts.map +1 -0
- package/build/scripts/legacy/debug_neg.js +230 -0
- package/build/scripts/legacy/debug_neg.js.map +1 -0
- package/build/scripts/legacy/debug_quality_pos.d.ts +5 -0
- package/build/scripts/legacy/debug_quality_pos.d.ts.map +1 -0
- package/build/scripts/legacy/debug_quality_pos.js +195 -0
- package/build/scripts/legacy/debug_quality_pos.js.map +1 -0
- package/build/scripts/legacy/debug_test.d.ts +1 -0
- package/build/scripts/legacy/debug_test.d.ts.map +1 -0
- package/build/scripts/legacy/debug_test.js +51 -0
- package/build/scripts/legacy/debug_test.js.map +1 -0
- package/build/scripts/legacy/hello_test.d.ts +1 -0
- package/build/scripts/legacy/hello_test.d.ts.map +1 -0
- package/build/scripts/legacy/hello_test.js +3 -0
- package/build/scripts/legacy/hello_test.js.map +1 -0
- package/build/scripts/legacy/import_test.d.ts +2 -0
- package/build/scripts/legacy/import_test.d.ts.map +1 -0
- package/build/scripts/legacy/import_test.js +4 -0
- package/build/scripts/legacy/import_test.js.map +1 -0
- package/build/scripts/legacy/quality_neg.d.ts +6 -0
- package/build/scripts/legacy/quality_neg.d.ts.map +1 -0
- package/build/scripts/legacy/quality_neg.js +362 -0
- package/build/scripts/legacy/quality_neg.js.map +1 -0
- package/build/scripts/legacy/quality_pos.d.ts +7 -0
- package/build/scripts/legacy/quality_pos.d.ts.map +1 -0
- package/build/scripts/legacy/quality_pos.js +345 -0
- package/build/scripts/legacy/quality_pos.js.map +1 -0
- package/build/scripts/legacy/simple_test.d.ts +2 -0
- package/build/scripts/legacy/simple_test.d.ts.map +1 -0
- package/build/scripts/legacy/simple_test.js +4 -0
- package/build/scripts/legacy/simple_test.js.map +1 -0
- package/build/scripts/legacy/test.d.ts +1 -0
- package/build/scripts/legacy/test.d.ts.map +1 -0
- package/build/scripts/legacy/test.js +3 -0
- package/build/scripts/legacy/test.js.map +1 -0
- package/build/scripts/legacy/test_additional_props.d.ts +6 -0
- package/build/scripts/legacy/test_additional_props.d.ts.map +1 -0
- package/build/scripts/legacy/test_additional_props.js +45 -0
- package/build/scripts/legacy/test_additional_props.js.map +1 -0
- package/build/scripts/legacy/test_ajv_validation.d.ts +2 -0
- package/build/scripts/legacy/test_ajv_validation.d.ts.map +1 -0
- package/build/scripts/legacy/test_ajv_validation.js +35 -0
- package/build/scripts/legacy/test_ajv_validation.js.map +1 -0
- package/build/scripts/legacy/test_build.d.ts +2 -0
- package/build/scripts/legacy/test_build.d.ts.map +1 -0
- package/build/scripts/legacy/test_build.js +14 -0
- package/build/scripts/legacy/test_build.js.map +1 -0
- package/build/scripts/legacy/test_direct.d.ts +3 -0
- package/build/scripts/legacy/test_direct.d.ts.map +1 -0
- package/build/scripts/legacy/test_direct.js +24 -0
- package/build/scripts/legacy/test_direct.js.map +1 -0
- package/build/scripts/legacy/test_import.d.ts +2 -0
- package/build/scripts/legacy/test_import.d.ts.map +1 -0
- package/build/scripts/legacy/test_import.js +7 -0
- package/build/scripts/legacy/test_import.js.map +1 -0
- package/build/scripts/legacy/test_mova_schemas.d.ts +2 -0
- package/build/scripts/legacy/test_mova_schemas.d.ts.map +1 -0
- package/build/scripts/legacy/test_mova_schemas.js +37 -0
- package/build/scripts/legacy/test_mova_schemas.js.map +1 -0
- package/build/scripts/legacy/test_scenario_integration.d.ts +13 -0
- package/build/scripts/legacy/test_scenario_integration.d.ts.map +1 -0
- package/build/scripts/legacy/test_scenario_integration.js +156 -0
- package/build/scripts/legacy/test_scenario_integration.js.map +1 -0
- package/build/scripts/legacy/test_schemas.d.ts +2 -0
- package/build/scripts/legacy/test_schemas.d.ts.map +1 -0
- package/build/scripts/legacy/test_schemas.js +48 -0
- package/build/scripts/legacy/test_schemas.js.map +1 -0
- package/build/scripts/legacy/test_schemas_simple.d.ts +2 -0
- package/build/scripts/legacy/test_schemas_simple.d.ts.map +1 -0
- package/build/scripts/legacy/test_schemas_simple.js +36 -0
- package/build/scripts/legacy/test_schemas_simple.js.map +1 -0
- package/build/scripts/legacy/test_simple.d.ts +4 -0
- package/build/scripts/legacy/test_simple.d.ts.map +1 -0
- package/build/scripts/legacy/test_simple.js +17 -0
- package/build/scripts/legacy/test_simple.js.map +1 -0
- package/build/scripts/legacy/test_warnings.d.ts +6 -0
- package/build/scripts/legacy/test_warnings.d.ts.map +1 -0
- package/build/scripts/legacy/test_warnings.js +48 -0
- package/build/scripts/legacy/test_warnings.js.map +1 -0
- package/build/scripts/legacy/very_simple_test.d.ts +1 -0
- package/build/scripts/legacy/very_simple_test.d.ts.map +1 -0
- package/build/scripts/legacy/very_simple_test.js +3 -0
- package/build/scripts/legacy/very_simple_test.js.map +1 -0
- package/build/scripts/merge_and_validate.d.ts +6 -0
- package/build/scripts/merge_and_validate.d.ts.map +1 -0
- package/build/scripts/merge_and_validate.js +53 -0
- package/build/scripts/merge_and_validate.js.map +1 -0
- package/build/src/ajv/ajv_loader.d.ts +47 -0
- package/build/src/ajv/ajv_loader.d.ts.map +1 -0
- package/build/src/ajv/ajv_loader.js +274 -0
- package/build/src/ajv/ajv_loader.js.map +1 -0
- package/build/src/ajv/schema_registry.d.ts +19 -0
- package/build/src/ajv/schema_registry.d.ts.map +1 -0
- package/build/src/ajv/schema_registry.js +121 -0
- package/build/src/ajv/schema_registry.js.map +1 -0
- package/build/src/drivers/__tests__/registry.test.d.ts +2 -0
- package/build/src/drivers/__tests__/registry.test.d.ts.map +1 -0
- package/build/src/drivers/__tests__/registry.test.js +100 -0
- package/build/src/drivers/__tests__/registry.test.js.map +1 -0
- package/build/src/drivers/httpDriver.d.ts +3 -0
- package/build/src/drivers/httpDriver.d.ts.map +1 -0
- package/build/src/drivers/httpDriver.js +76 -0
- package/build/src/drivers/httpDriver.js.map +1 -0
- package/build/src/drivers/index.d.ts +20 -0
- package/build/src/drivers/index.d.ts.map +1 -0
- package/build/src/drivers/index.js +33 -0
- package/build/src/drivers/index.js.map +1 -0
- package/build/src/drivers/restrictedShellDriver.d.ts +12 -0
- package/build/src/drivers/restrictedShellDriver.d.ts.map +1 -0
- package/build/src/drivers/restrictedShellDriver.js +88 -0
- package/build/src/drivers/restrictedShellDriver.js.map +1 -0
- package/build/src/episodes/__tests__/episode_writer_atomic.test.d.ts +2 -0
- package/build/src/episodes/__tests__/episode_writer_atomic.test.d.ts.map +1 -0
- package/build/src/episodes/__tests__/episode_writer_atomic.test.js +86 -0
- package/build/src/episodes/__tests__/episode_writer_atomic.test.js.map +1 -0
- package/build/src/episodes/episode_writer.d.ts +100 -0
- package/build/src/episodes/episode_writer.d.ts.map +1 -0
- package/build/src/episodes/episode_writer.js +503 -0
- package/build/src/episodes/episode_writer.js.map +1 -0
- package/build/src/episodes/episode_writer_atomic.d.ts +10 -0
- package/build/src/episodes/episode_writer_atomic.d.ts.map +1 -0
- package/build/src/episodes/episode_writer_atomic.js +46 -0
- package/build/src/episodes/episode_writer_atomic.js.map +1 -0
- package/build/src/evidence/evidence_writer.d.ts +16 -0
- package/build/src/evidence/evidence_writer.d.ts.map +1 -0
- package/build/src/evidence/evidence_writer.js +78 -0
- package/build/src/evidence/evidence_writer.js.map +1 -0
- package/build/src/handlers/registry.d.ts +20 -0
- package/build/src/handlers/registry.d.ts.map +1 -0
- package/build/src/handlers/registry.js +38 -0
- package/build/src/handlers/registry.js.map +1 -0
- package/build/src/interpreter/interpreter.d.ts +90 -0
- package/build/src/interpreter/interpreter.d.ts.map +1 -0
- package/build/src/interpreter/interpreter.js +732 -0
- package/build/src/interpreter/interpreter.js.map +1 -0
- package/build/src/interpreter/interpreter_with_budget.d.ts +92 -0
- package/build/src/interpreter/interpreter_with_budget.d.ts.map +1 -0
- package/build/src/interpreter/interpreter_with_budget.js +678 -0
- package/build/src/interpreter/interpreter_with_budget.js.map +1 -0
- package/build/src/logging/logger.d.ts +83 -0
- package/build/src/logging/logger.d.ts.map +1 -0
- package/build/src/logging/logger.js +226 -0
- package/build/src/logging/logger.js.map +1 -0
- package/build/src/policy/policy_engine.d.ts +116 -0
- package/build/src/policy/policy_engine.d.ts.map +1 -0
- package/build/src/policy/policy_engine.js +296 -0
- package/build/src/policy/policy_engine.js.map +1 -0
- package/build/src/skills/skills_layer.d.ts +94 -0
- package/build/src/skills/skills_layer.d.ts.map +1 -0
- package/build/src/skills/skills_layer.js +405 -0
- package/build/src/skills/skills_layer.js.map +1 -0
- package/build/src/telemetry/token_budget_enforcer.d.ts +113 -0
- package/build/src/telemetry/token_budget_enforcer.d.ts.map +1 -0
- package/build/src/telemetry/token_budget_enforcer.js +253 -0
- package/build/src/telemetry/token_budget_enforcer.js.map +1 -0
- package/build/src/telemetry/token_budget_loader.d.ts +29 -0
- package/build/src/telemetry/token_budget_loader.d.ts.map +1 -0
- package/build/src/telemetry/token_budget_loader.js +112 -0
- package/build/src/telemetry/token_budget_loader.js.map +1 -0
- package/build/src/telemetry/token_meter.d.ts +65 -0
- package/build/src/telemetry/token_meter.d.ts.map +1 -0
- package/build/src/telemetry/token_meter.js +153 -0
- package/build/src/telemetry/token_meter.js.map +1 -0
- package/build/src/ux/cli_interface.d.ts +85 -0
- package/build/src/ux/cli_interface.d.ts.map +1 -0
- package/build/src/ux/cli_interface.js +651 -0
- package/build/src/ux/cli_interface.js.map +1 -0
- package/build/test/drivers/registry.test.d.ts +2 -0
- package/build/test/drivers/registry.test.d.ts.map +1 -0
- package/build/test/drivers/registry.test.js +100 -0
- package/build/test/drivers/registry.test.js.map +1 -0
- package/build/test/episodes/episode_writer_atomic.test.d.ts +2 -0
- package/build/test/episodes/episode_writer_atomic.test.d.ts.map +1 -0
- package/build/test/episodes/episode_writer_atomic.test.js +86 -0
- package/build/test/episodes/episode_writer_atomic.test.js.map +1 -0
- package/build/tests/ajv/check_schemas.d.ts +4 -0
- package/build/tests/ajv/check_schemas.d.ts.map +1 -0
- package/build/tests/ajv/check_schemas.js +27 -0
- package/build/tests/ajv/check_schemas.js.map +1 -0
- package/build/tests/ajv/final_test.d.ts +2 -0
- package/build/tests/ajv/final_test.d.ts.map +1 -0
- package/build/tests/ajv/final_test.js +42 -0
- package/build/tests/ajv/final_test.js.map +1 -0
- package/build/tests/ajv/simple_test.d.ts +2 -0
- package/build/tests/ajv/simple_test.d.ts.map +1 -0
- package/build/tests/ajv/simple_test.js +40 -0
- package/build/tests/ajv/simple_test.js.map +1 -0
- package/build/tests/ajv/test_schema_loading.d.ts +2 -0
- package/build/tests/ajv/test_schema_loading.d.ts.map +1 -0
- package/build/tests/ajv/test_schema_loading.js +39 -0
- package/build/tests/ajv/test_schema_loading.js.map +1 -0
- package/build/tools/mova-agent.d.ts +2 -0
- package/build/tools/mova-agent.d.ts.map +1 -0
- package/build/tools/mova-agent.js +15 -0
- package/build/tools/mova-agent.js.map +1 -0
- package/package.json +63 -0
- package/src/ajv/ajv_loader.ts +311 -0
- package/src/ajv/schema_registry.ts +92 -0
- package/src/drivers/httpDriver.ts +90 -0
- package/src/drivers/index.ts +48 -0
- package/src/drivers/restrictedShellDriver.ts +68 -0
- package/src/episodes/episode_writer.ts +675 -0
- package/src/episodes/episode_writer_atomic.ts +44 -0
- package/src/evidence/evidence_writer.ts +74 -0
- package/src/handlers/registry.ts +53 -0
- package/src/interpreter/interpreter.ts +1009 -0
- package/src/interpreter/interpreter_with_budget.ts +923 -0
- package/src/logging/logger.ts +269 -0
- package/src/policy/policy_engine.ts +395 -0
- package/src/skills/skills_layer.ts +510 -0
- package/src/telemetry/token_budget_enforcer.ts +338 -0
- package/src/telemetry/token_budget_loader.ts +133 -0
- package/src/telemetry/token_meter.ts +185 -0
- package/src/types/generated/ds.connector_core_v1.d.ts +116 -0
- package/src/types/generated/ds.instruction_profile_core_v1.d.ts +141 -0
- package/src/types/generated/ds.mova4_core_catalog_v1.d.ts +80 -0
- package/src/types/generated/ds.mova_agent_run_summary_v1.d.ts +120 -0
- package/src/types/generated/ds.mova_agent_step_v1.d.ts +53 -0
- package/src/types/generated/ds.mova_agent_tool_pool_v1.d.ts +152 -0
- package/src/types/generated/ds.mova_agent_validation_report_v1.d.ts +43 -0
- package/src/types/generated/ds.mova_episode_core_v1.d.ts +56 -0
- package/src/types/generated/ds.mova_schema_core_v1.d.ts +49 -0
- package/src/types/generated/ds.mova_token_budget_contract_v0.d.ts +140 -0
- package/src/types/generated/ds.runtime_binding_core_v1.d.ts +112 -0
- package/src/types/generated/ds.security_event_episode_core_v1.d.ts +138 -0
- package/src/types/generated/ds.ui_text_bundle_core_v1.d.ts +118 -0
- package/src/types/generated/env.instruction_profile_publish_v1.d.ts +159 -0
- package/src/types/generated/env.mova4_core_catalog_publish_v1.d.ts +25 -0
- package/src/types/generated/env.mova_agent_plan_v1.d.ts +40 -0
- package/src/types/generated/env.mova_agent_request_v1.d.ts +43 -0
- package/src/types/generated/env.security_event_store_v1.d.ts +191 -0
- package/src/types/generated/index.d.ts +25 -0
- package/src/ux/cli_interface.ts +719 -0
|
@@ -0,0 +1,732 @@
|
|
|
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.Interpreter = void 0;
|
|
7
|
+
const ajv_loader_1 = require("../ajv/ajv_loader");
|
|
8
|
+
const schema_registry_1 = require("../ajv/schema_registry");
|
|
9
|
+
const registry_1 = require("../handlers/registry");
|
|
10
|
+
const evidence_writer_1 = require("../evidence/evidence_writer");
|
|
11
|
+
const episode_writer_1 = require("../episodes/episode_writer");
|
|
12
|
+
const policy_engine_1 = require("../policy/policy_engine");
|
|
13
|
+
const crypto_1 = require("crypto");
|
|
14
|
+
const token_meter_1 = require("../telemetry/token_meter");
|
|
15
|
+
const token_budget_enforcer_1 = require("../telemetry/token_budget_enforcer");
|
|
16
|
+
const token_budget_loader_1 = require("../telemetry/token_budget_loader");
|
|
17
|
+
const logger_1 = require("../logging/logger");
|
|
18
|
+
const path_1 = __importDefault(require("path"));
|
|
19
|
+
/**
|
|
20
|
+
* Основной интерпретатор планов MOVA Agent
|
|
21
|
+
*/
|
|
22
|
+
class Interpreter {
|
|
23
|
+
evidenceWriter;
|
|
24
|
+
episodeWriter; // Base episode writer
|
|
25
|
+
policyEngine;
|
|
26
|
+
tokenBudgetLoader;
|
|
27
|
+
schemaInitializationPromise;
|
|
28
|
+
constructor() {
|
|
29
|
+
this.evidenceWriter = new evidence_writer_1.EvidenceWriter();
|
|
30
|
+
this.episodeWriter = new episode_writer_1.EpisodeWriter();
|
|
31
|
+
this.policyEngine = new policy_engine_1.PolicyEngine();
|
|
32
|
+
this.tokenBudgetLoader = new token_budget_loader_1.TokenBudgetLoader();
|
|
33
|
+
// Initialize schemas and track the promise
|
|
34
|
+
this.schemaInitializationPromise = this.initializeSchemas();
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Wait for the interpreter to be fully initialized with all schemas
|
|
38
|
+
*/
|
|
39
|
+
async ready() {
|
|
40
|
+
await this.schemaInitializationPromise;
|
|
41
|
+
}
|
|
42
|
+
async initializeSchemas() {
|
|
43
|
+
try {
|
|
44
|
+
const registry = new schema_registry_1.SchemaRegistry(ajv_loader_1.ajvLoader);
|
|
45
|
+
await registry.loadAllSchemas(); // Load both MOVA and local schemas
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
(0, logger_1.getLogger)().info(`Warning: Could not initialize schema registry: ${error.message}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Основной метод запуска выполнения плана
|
|
53
|
+
*/
|
|
54
|
+
async runPlan(params) {
|
|
55
|
+
// Wait for schema initialization to complete
|
|
56
|
+
await this.ready();
|
|
57
|
+
const { planEnvelope, toolPool, instructionProfile, tokenBudgetPath, tokenBudgetProfile } = params;
|
|
58
|
+
try {
|
|
59
|
+
// Load token budget contract
|
|
60
|
+
const tokenBudgetContract = await this.tokenBudgetLoader.load(tokenBudgetPath, tokenBudgetProfile);
|
|
61
|
+
// Set tool pool and instruction profile in policy engine for enforcement
|
|
62
|
+
this.policyEngine.setToolPool(toolPool);
|
|
63
|
+
this.policyEngine.setInstructionProfile(instructionProfile);
|
|
64
|
+
// 1) Ajv validate plan envelope
|
|
65
|
+
const planValidation = await (0, ajv_loader_1.validate)('env.mova_agent_plan_v1', planEnvelope);
|
|
66
|
+
if (!planValidation.ok) {
|
|
67
|
+
const error = `Plan validation failed: ${planValidation.errors?.join(', ')}`;
|
|
68
|
+
await this.recordSecurityEvent('validation_failed', { error, planEnvelope });
|
|
69
|
+
return { success: false, error };
|
|
70
|
+
}
|
|
71
|
+
// 2) Ajv validate tool pool
|
|
72
|
+
// Note: Since we don't have a specific schema for the full tool pool structure yet,
|
|
73
|
+
// we'll validate individual tools as we encounter them
|
|
74
|
+
if (!toolPool || !toolPool.tools || !Array.isArray(toolPool.tools)) {
|
|
75
|
+
const error = 'Invalid tool pool structure';
|
|
76
|
+
await this.recordSecurityEvent('validation_failed', { error, toolPool });
|
|
77
|
+
return { success: false, error };
|
|
78
|
+
}
|
|
79
|
+
// 3) Validate instruction profile with deny-by-default policy
|
|
80
|
+
const profileValidation = this.validateInstructionProfile(instructionProfile);
|
|
81
|
+
if (!profileValidation.ok) {
|
|
82
|
+
const error = `Instruction profile validation failed: ${profileValidation.errors?.join(', ')}`;
|
|
83
|
+
await this.recordSecurityEvent('validation_failed', { error, instructionProfile });
|
|
84
|
+
return { success: false, error };
|
|
85
|
+
}
|
|
86
|
+
// 4) Build an execution context
|
|
87
|
+
const runId = `run_${(0, crypto_1.randomUUID)()}`;
|
|
88
|
+
const requestId = `req_${(0, crypto_1.randomUUID)()}`;
|
|
89
|
+
const evidenceDir = await this.evidenceWriter.createRunDirectory(requestId, runId);
|
|
90
|
+
// Create run-specific episode writer
|
|
91
|
+
const runEpisodeWriter = this.episodeWriter.createRunWriter(requestId, runId);
|
|
92
|
+
// Initialize token meter and budget enforcer
|
|
93
|
+
const tokenMeter = new token_meter_1.TokenMeter(evidenceDir);
|
|
94
|
+
const tokenBudgetEnforcer = new token_budget_enforcer_1.TokenBudgetEnforcer(tokenBudgetContract, tokenMeter);
|
|
95
|
+
// Save the resolved token budget contract
|
|
96
|
+
this.tokenBudgetLoader.saveResolvedContract(tokenBudgetContract, evidenceDir);
|
|
97
|
+
const context = {
|
|
98
|
+
run_id: runId,
|
|
99
|
+
request_id: requestId,
|
|
100
|
+
evidence_dir: evidenceDir,
|
|
101
|
+
instructionProfile: instructionProfile,
|
|
102
|
+
caps: instructionProfile.caps,
|
|
103
|
+
redaction_rules: instructionProfile.redaction_rules,
|
|
104
|
+
step_inputs: new Map(),
|
|
105
|
+
step_outputs: new Map(),
|
|
106
|
+
step_bindings: new Map(),
|
|
107
|
+
current_step_index: 0,
|
|
108
|
+
step_security_events: [],
|
|
109
|
+
has_fatal_security_event: false,
|
|
110
|
+
episodeWriter: runEpisodeWriter,
|
|
111
|
+
tokenMeter,
|
|
112
|
+
tokenBudgetEnforcer,
|
|
113
|
+
budgetStatus: 'passed',
|
|
114
|
+
budgetViolations: [],
|
|
115
|
+
};
|
|
116
|
+
// Write initial artifacts
|
|
117
|
+
await this.evidenceWriter.writeArtifact(evidenceDir, 'request.envelope.json', params.requestEnvelope || {});
|
|
118
|
+
await this.evidenceWriter.writeArtifact(evidenceDir, 'plan.envelope.json', planEnvelope);
|
|
119
|
+
await this.evidenceWriter.writeArtifact(evidenceDir, 'tool_pool.resolved.json', toolPool);
|
|
120
|
+
await this.evidenceWriter.writeArtifact(evidenceDir, 'instruction_profile.resolved.json', instructionProfile);
|
|
121
|
+
// 5) Execute steps in order
|
|
122
|
+
let executionError;
|
|
123
|
+
try {
|
|
124
|
+
for (const step of planEnvelope.payload.steps) {
|
|
125
|
+
// Check if we can execute this step based on token budget
|
|
126
|
+
const budgetStatus = context.tokenBudgetEnforcer.checkModelCall();
|
|
127
|
+
if (budgetStatus.exceeded) {
|
|
128
|
+
// Handle budget violation
|
|
129
|
+
context.budgetStatus = budgetStatus.action === 'fail' ? 'failed' : 'warned';
|
|
130
|
+
context.budgetViolations.push(...budgetStatus.violations);
|
|
131
|
+
// Create budget violation episodes for each violation
|
|
132
|
+
for (const violation of budgetStatus.violations) {
|
|
133
|
+
const budgetViolationEpisode = context.tokenBudgetEnforcer.createBudgetViolationEpisode(violation, {
|
|
134
|
+
run_id: context.run_id,
|
|
135
|
+
request_id: context.request_id,
|
|
136
|
+
step_id: step.id,
|
|
137
|
+
});
|
|
138
|
+
await context.episodeWriter.writeSecurityEvent(budgetViolationEpisode);
|
|
139
|
+
}
|
|
140
|
+
if (budgetStatus.action === 'fail') {
|
|
141
|
+
executionError = `Budget exceeded: ${budgetStatus.violations.map((v) => v.message).join('; ')}`;
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
else if (budgetStatus.action === 'warn') {
|
|
145
|
+
(0, logger_1.getLogger)().info(`Budget warning: ${budgetStatus.violations.map((v) => v.message).join('; ')}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const stepResult = await this.executeStep(step, context, toolPool);
|
|
149
|
+
if (!stepResult.success && step.on_error !== 'soft') {
|
|
150
|
+
// Step failed and is marked as fatal
|
|
151
|
+
executionError = `Step ${step.id} failed: ${stepResult.error}`;
|
|
152
|
+
break; // Exit the loop on fatal error
|
|
153
|
+
}
|
|
154
|
+
// Store output for potential use by subsequent steps
|
|
155
|
+
if (stepResult.output) {
|
|
156
|
+
context.step_outputs.set(step.id, stepResult.output);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// 6) Emit final run summary episode and return structured result
|
|
160
|
+
// Check if there were any fatal security events or execution errors
|
|
161
|
+
const finalStatus = context.has_fatal_security_event || executionError ? 'failed' : 'completed';
|
|
162
|
+
const finalError = context.has_fatal_security_event
|
|
163
|
+
? 'Fatal security event occurred during execution'
|
|
164
|
+
: executionError;
|
|
165
|
+
const runSummary = await this.createRunSummary(context, finalStatus, finalError);
|
|
166
|
+
await this.evidenceWriter.writeArtifact(evidenceDir, 'run_summary.json', runSummary);
|
|
167
|
+
// Create a final execution episode for the overall run
|
|
168
|
+
await context.episodeWriter.writeExecutionEpisode({
|
|
169
|
+
episode_type: 'execution_run_summary',
|
|
170
|
+
result_status: finalStatus,
|
|
171
|
+
result_summary: finalError ? `Run failed: ${finalError}` : 'Run completed successfully',
|
|
172
|
+
input_data_refs: [
|
|
173
|
+
{
|
|
174
|
+
data_type: 'ds.mova_agent_plan_v1',
|
|
175
|
+
data_id: planEnvelope.payload.steps.length > 0
|
|
176
|
+
? planEnvelope.payload.steps[0].id
|
|
177
|
+
: 'unknown',
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
payload: {
|
|
181
|
+
run_id: context.run_id,
|
|
182
|
+
request_id: context.request_id,
|
|
183
|
+
total_steps: planEnvelope.payload.steps.length,
|
|
184
|
+
steps_completed: context.step_outputs.size,
|
|
185
|
+
status: finalStatus,
|
|
186
|
+
error: finalError,
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
return {
|
|
190
|
+
success: !context.has_fatal_security_event && !executionError,
|
|
191
|
+
run_summary: runSummary,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
finally {
|
|
195
|
+
// Always save token usage report in the evidence directory, even if execution fails
|
|
196
|
+
// Determine if this is a quality suite run based on the evidence directory path
|
|
197
|
+
const isQualitySuite = evidenceDir.includes('quality');
|
|
198
|
+
context.tokenMeter.saveReport(path_1.default.join(evidenceDir, 'token_usage.json'), isQualitySuite ? 'quality suite uses noop handlers' : undefined);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
const errorMessage = `Unexpected error during plan execution: ${error.message}`;
|
|
203
|
+
// We don't have context here, so we'll call without it
|
|
204
|
+
await this.recordSecurityEvent('execution_error', {
|
|
205
|
+
error: errorMessage,
|
|
206
|
+
stack: error.stack,
|
|
207
|
+
});
|
|
208
|
+
return { success: false, error: errorMessage };
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Выполнить один шаг плана
|
|
213
|
+
*/
|
|
214
|
+
async executeStep(step, context, toolPool) {
|
|
215
|
+
try {
|
|
216
|
+
// a) Resolve input
|
|
217
|
+
let input = step.input;
|
|
218
|
+
if (step.input_from) {
|
|
219
|
+
const referencedStepId = step.input_from.step_id;
|
|
220
|
+
if (!context.step_outputs.has(referencedStepId)) {
|
|
221
|
+
throw new Error(`Input references non-existent step: ${referencedStepId}`);
|
|
222
|
+
}
|
|
223
|
+
const referencedOutput = context.step_outputs.get(referencedStepId);
|
|
224
|
+
if (step.input_from.path) {
|
|
225
|
+
// Simple path resolution - in a real implementation this would be more sophisticated
|
|
226
|
+
input = this.resolvePath(referencedOutput, step.input_from.path);
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
input = referencedOutput;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// b) Validate input schema (if provided)
|
|
233
|
+
if (step.expected_output_schema_ref && input) {
|
|
234
|
+
const inputValidation = await (0, ajv_loader_1.validate)(step.expected_output_schema_ref, input);
|
|
235
|
+
if (!inputValidation.ok) {
|
|
236
|
+
const error = `Step input validation failed: ${inputValidation.errors?.join(', ')}`;
|
|
237
|
+
// Emit failure episode using MOVA 4.1.1 format
|
|
238
|
+
await context.episodeWriter.writeExecutionEpisode({
|
|
239
|
+
episode_type: 'execution_step',
|
|
240
|
+
result_status: 'failed',
|
|
241
|
+
result_summary: `Step ${step.id} failed: ${error}`,
|
|
242
|
+
input_data_refs: [{ data_type: 'ds.mova_agent_step_v1', data_id: step.id }],
|
|
243
|
+
payload: {
|
|
244
|
+
step_id: step.id,
|
|
245
|
+
error: error,
|
|
246
|
+
status: 'failed',
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
await this.recordSecurityEvent('input_validation_failed', { step_id: step.id, error, input }, context);
|
|
250
|
+
return { success: false, error };
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
// c) Policy checks using the policy engine
|
|
254
|
+
const tool = toolPool.tools.find((t) => t.id === step.connector_id);
|
|
255
|
+
if (!tool) {
|
|
256
|
+
const error = `Tool not found in pool: ${step.connector_id}`;
|
|
257
|
+
// Emit failure episode using MOVA 4.1.1 format
|
|
258
|
+
await context.episodeWriter.writeExecutionEpisode({
|
|
259
|
+
episode_type: 'execution_step',
|
|
260
|
+
result_status: 'failed',
|
|
261
|
+
result_summary: `Step ${step.id} failed: ${error}`,
|
|
262
|
+
input_data_refs: [{ data_type: 'ds.mova_agent_step_v1', data_id: step.id }],
|
|
263
|
+
payload: {
|
|
264
|
+
step_id: step.id,
|
|
265
|
+
error: error,
|
|
266
|
+
status: 'failed',
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
await this.recordSecurityEvent('tool_not_allowlisted', { step_id: step.id, error, connector_id: step.connector_id }, context);
|
|
270
|
+
return { success: false, error };
|
|
271
|
+
}
|
|
272
|
+
// Perform comprehensive policy check
|
|
273
|
+
const policyEvaluation = this.policyEngine.evaluateStepComprehensive({
|
|
274
|
+
id: step.id,
|
|
275
|
+
verb: step.verb,
|
|
276
|
+
connector_id: step.connector_id,
|
|
277
|
+
input: input,
|
|
278
|
+
tool_binding: tool.binding,
|
|
279
|
+
});
|
|
280
|
+
if (!policyEvaluation.allowed) {
|
|
281
|
+
const error = `Step failed policy evaluation: ${policyEvaluation.reason}`;
|
|
282
|
+
// Emit failure episode using MOVA 4.1.1 format
|
|
283
|
+
await context.episodeWriter.writeExecutionEpisode({
|
|
284
|
+
episode_type: 'execution_step',
|
|
285
|
+
result_status: 'failed',
|
|
286
|
+
result_summary: `Step ${step.id} failed: ${error}`,
|
|
287
|
+
input_data_refs: [{ data_type: 'ds.mova_agent_step_v1', data_id: step.id }],
|
|
288
|
+
payload: {
|
|
289
|
+
step_id: step.id,
|
|
290
|
+
error: error,
|
|
291
|
+
status: 'failed',
|
|
292
|
+
},
|
|
293
|
+
});
|
|
294
|
+
await this.recordSecurityEvent('policy_check_failed', {
|
|
295
|
+
step_id: step.id,
|
|
296
|
+
error,
|
|
297
|
+
policy_reason: policyEvaluation.reason,
|
|
298
|
+
input,
|
|
299
|
+
binding: tool.binding,
|
|
300
|
+
}, context);
|
|
301
|
+
return { success: false, error };
|
|
302
|
+
}
|
|
303
|
+
// Additional specific checks for destinations
|
|
304
|
+
if (input && (input.url || input.endpoint)) {
|
|
305
|
+
const destUrl = input.url || input.endpoint;
|
|
306
|
+
const allowlist = tool.binding.destination_allowlist;
|
|
307
|
+
if (allowlist && allowlist.length > 0) {
|
|
308
|
+
const isAllowed = allowlist.some((allowed) => {
|
|
309
|
+
try {
|
|
310
|
+
const inputUrl = new URL(destUrl);
|
|
311
|
+
const allowedUrl = new URL(allowed);
|
|
312
|
+
return (inputUrl.hostname === allowedUrl.hostname &&
|
|
313
|
+
inputUrl.protocol === allowedUrl.protocol &&
|
|
314
|
+
(inputUrl.port === allowedUrl.port || allowedUrl.port === ''));
|
|
315
|
+
}
|
|
316
|
+
catch {
|
|
317
|
+
return false; // If we can't parse the URL, consider it not allowed
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
if (!isAllowed) {
|
|
321
|
+
const error = `Destination not allowlisted: ${destUrl}`;
|
|
322
|
+
// Emit failure episode using MOVA 4.1.1 format
|
|
323
|
+
await context.episodeWriter.writeExecutionEpisode({
|
|
324
|
+
episode_type: 'execution_step',
|
|
325
|
+
result_status: 'failed',
|
|
326
|
+
result_summary: `Step ${step.id} failed: ${error}`,
|
|
327
|
+
input_data_refs: [{ data_type: 'ds.mova_agent_step_v1', data_id: step.id }],
|
|
328
|
+
payload: {
|
|
329
|
+
step_id: step.id,
|
|
330
|
+
error: error,
|
|
331
|
+
status: 'failed',
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
await this.recordSecurityEvent('destination_not_allowlisted', {
|
|
335
|
+
step_id: step.id,
|
|
336
|
+
error,
|
|
337
|
+
destination: destUrl,
|
|
338
|
+
allowed_destinations: allowlist,
|
|
339
|
+
}, context);
|
|
340
|
+
return { success: false, error };
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
// Check limits are present (already checked in policy engine, but keeping as extra safeguard)
|
|
345
|
+
if (!tool.binding.limits || !tool.binding.limits.timeout_ms) {
|
|
346
|
+
const error = `Required limits not specified for tool: ${step.connector_id}`;
|
|
347
|
+
// Emit failure episode using MOVA 4.1.1 format
|
|
348
|
+
await context.episodeWriter.writeExecutionEpisode({
|
|
349
|
+
episode_type: 'execution_step',
|
|
350
|
+
result_status: 'failed',
|
|
351
|
+
result_summary: `Step ${step.id} failed: ${error}`,
|
|
352
|
+
input_data_refs: [{ data_type: 'ds.mova_agent_step_v1', data_id: step.id }],
|
|
353
|
+
payload: {
|
|
354
|
+
step_id: step.id,
|
|
355
|
+
error: error,
|
|
356
|
+
status: 'failed',
|
|
357
|
+
},
|
|
358
|
+
});
|
|
359
|
+
await this.recordSecurityEvent('limits_not_specified', { step_id: step.id, error, connector_id: step.connector_id }, context);
|
|
360
|
+
return { success: false, error };
|
|
361
|
+
}
|
|
362
|
+
// Check tool call budget
|
|
363
|
+
const toolBudgetStatus = context.tokenBudgetEnforcer.checkToolCall();
|
|
364
|
+
if (toolBudgetStatus.exceeded) {
|
|
365
|
+
// Handle budget violation
|
|
366
|
+
context.budgetStatus = toolBudgetStatus.action === 'fail' ? 'failed' : 'warned';
|
|
367
|
+
context.budgetViolations.push(...toolBudgetStatus.violations);
|
|
368
|
+
// Create budget violation episodes for each violation
|
|
369
|
+
for (const violation of toolBudgetStatus.violations) {
|
|
370
|
+
const budgetViolationEpisode = context.tokenBudgetEnforcer.createBudgetViolationEpisode(violation, { run_id: context.run_id, request_id: context.request_id, step_id: step.id });
|
|
371
|
+
await context.episodeWriter.writeSecurityEvent(budgetViolationEpisode);
|
|
372
|
+
}
|
|
373
|
+
if (toolBudgetStatus.action === 'fail') {
|
|
374
|
+
const error = `Tool call budget exceeded: ${toolBudgetStatus.violations.map((v) => v.message).join('; ')}`;
|
|
375
|
+
// Emit failure episode using MOVA 4.1.1 format
|
|
376
|
+
await context.episodeWriter.writeExecutionEpisode({
|
|
377
|
+
episode_type: 'execution_step',
|
|
378
|
+
result_status: 'failed',
|
|
379
|
+
result_summary: `Step ${step.id} failed: ${error}`,
|
|
380
|
+
input_data_refs: [{ data_type: 'ds.mova_agent_step_v1', data_id: step.id }],
|
|
381
|
+
payload: {
|
|
382
|
+
step_id: step.id,
|
|
383
|
+
error: error,
|
|
384
|
+
status: 'failed',
|
|
385
|
+
},
|
|
386
|
+
});
|
|
387
|
+
return { success: false, error };
|
|
388
|
+
}
|
|
389
|
+
else if (toolBudgetStatus.action === 'warn') {
|
|
390
|
+
(0, logger_1.getLogger)().info(`Tool call budget warning: ${toolBudgetStatus.violations.map((v) => v.message).join('; ')}`);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
// d) Dispatch handler by binding.driver_kind from static registry
|
|
394
|
+
const handler = registry_1.HandlerRegistry.getInstance()[tool.binding.driver_kind];
|
|
395
|
+
if (!handler) {
|
|
396
|
+
const error = `Handler not found for driver kind: ${tool.binding.driver_kind}`;
|
|
397
|
+
// Emit failure episode using MOVA 4.1.1 format
|
|
398
|
+
await context.episodeWriter.writeExecutionEpisode({
|
|
399
|
+
episode_type: 'execution_step',
|
|
400
|
+
result_status: 'failed',
|
|
401
|
+
result_summary: `Step ${step.id} failed: ${error}`,
|
|
402
|
+
input_data_refs: [{ data_type: 'ds.mova_agent_step_v1', data_id: step.id }],
|
|
403
|
+
payload: {
|
|
404
|
+
step_id: step.id,
|
|
405
|
+
error: error,
|
|
406
|
+
status: 'failed',
|
|
407
|
+
},
|
|
408
|
+
});
|
|
409
|
+
await this.recordSecurityEvent('handler_not_found', { step_id: step.id, error, driver_kind: tool.binding.driver_kind }, context);
|
|
410
|
+
return { success: false, error };
|
|
411
|
+
}
|
|
412
|
+
// e) Execute handler
|
|
413
|
+
const handlerResult = await handler(input, tool, context);
|
|
414
|
+
// Check tool output budget
|
|
415
|
+
const outputStr = JSON.stringify(handlerResult);
|
|
416
|
+
const toolOutputBudgetStatus = context.tokenBudgetEnforcer.checkToolOutput(outputStr);
|
|
417
|
+
if (toolOutputBudgetStatus.exceeded) {
|
|
418
|
+
// Handle budget violation
|
|
419
|
+
context.budgetStatus = toolOutputBudgetStatus.action === 'fail' ? 'failed' : 'warned';
|
|
420
|
+
context.budgetViolations.push(...toolOutputBudgetStatus.violations);
|
|
421
|
+
// Create budget violation episodes for each violation
|
|
422
|
+
for (const violation of toolOutputBudgetStatus.violations) {
|
|
423
|
+
const budgetViolationEpisode = context.tokenBudgetEnforcer.createBudgetViolationEpisode(violation, { run_id: context.run_id, request_id: context.request_id, step_id: step.id });
|
|
424
|
+
await context.episodeWriter.writeSecurityEvent(budgetViolationEpisode);
|
|
425
|
+
}
|
|
426
|
+
if (toolOutputBudgetStatus.action === 'fail') {
|
|
427
|
+
const error = `Tool output budget exceeded: ${toolOutputBudgetStatus.violations.map((v) => v.message).join('; ')}`;
|
|
428
|
+
// Emit failure episode using MOVA 4.1.1 format
|
|
429
|
+
await context.episodeWriter.writeExecutionEpisode({
|
|
430
|
+
episode_type: 'execution_step',
|
|
431
|
+
result_status: 'failed',
|
|
432
|
+
result_summary: `Step ${step.id} failed: ${error}`,
|
|
433
|
+
input_data_refs: [{ data_type: 'ds.mova_agent_step_v1', data_id: step.id }],
|
|
434
|
+
payload: {
|
|
435
|
+
step_id: step.id,
|
|
436
|
+
error: error,
|
|
437
|
+
status: 'failed',
|
|
438
|
+
},
|
|
439
|
+
});
|
|
440
|
+
return { success: false, error };
|
|
441
|
+
}
|
|
442
|
+
else if (toolOutputBudgetStatus.action === 'warn') {
|
|
443
|
+
(0, logger_1.getLogger)().info(`Tool output budget warning: ${toolOutputBudgetStatus.violations.map((v) => v.message).join('; ')}`);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
// Record successful tool call
|
|
447
|
+
context.tokenBudgetEnforcer.recordSuccessfulToolCall(outputStr);
|
|
448
|
+
// f) Validate output schema (if provided)
|
|
449
|
+
if (tool.binding.schema_refs?.output && handlerResult) {
|
|
450
|
+
const outputValidation = await (0, ajv_loader_1.validate)(tool.binding.schema_refs.output, handlerResult);
|
|
451
|
+
if (!outputValidation.ok) {
|
|
452
|
+
const error = `Step output validation failed: ${outputValidation.errors?.join(', ')}`;
|
|
453
|
+
// Emit failure episode using MOVA 4.1.1 format
|
|
454
|
+
await context.episodeWriter.writeExecutionEpisode({
|
|
455
|
+
episode_type: 'execution_step',
|
|
456
|
+
result_status: 'failed',
|
|
457
|
+
result_summary: `Step ${step.id} failed: ${error}`,
|
|
458
|
+
input_data_refs: [{ data_type: 'ds.mova_agent_step_v1', data_id: step.id }],
|
|
459
|
+
payload: {
|
|
460
|
+
step_id: step.id,
|
|
461
|
+
error: error,
|
|
462
|
+
status: 'failed',
|
|
463
|
+
},
|
|
464
|
+
});
|
|
465
|
+
await this.recordSecurityEvent('output_validation_failed', { step_id: step.id, error, output: handlerResult }, context);
|
|
466
|
+
return { success: false, error };
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
// g) Write artifacts (inputs/outputs/logs)
|
|
470
|
+
await this.evidenceWriter.writeArtifact(context.evidence_dir, `logs/${step.id}.log`, {
|
|
471
|
+
input,
|
|
472
|
+
output: handlerResult,
|
|
473
|
+
timestamp: new Date().toISOString(),
|
|
474
|
+
});
|
|
475
|
+
// h) Emit episode for the step using MOVA 4.1.1 format
|
|
476
|
+
await context.episodeWriter.writeExecutionEpisode({
|
|
477
|
+
episode_type: 'execution_step',
|
|
478
|
+
result_status: 'completed',
|
|
479
|
+
result_summary: `Step ${step.id} executed successfully`,
|
|
480
|
+
input_data_refs: [{ data_type: 'ds.mova_agent_step_v1', data_id: step.id }],
|
|
481
|
+
payload: {
|
|
482
|
+
step_id: step.id,
|
|
483
|
+
input,
|
|
484
|
+
output: handlerResult,
|
|
485
|
+
status: 'success',
|
|
486
|
+
},
|
|
487
|
+
});
|
|
488
|
+
return { success: true, output: handlerResult };
|
|
489
|
+
}
|
|
490
|
+
catch (error) {
|
|
491
|
+
const errorMessage = `Step execution failed: ${error.message}`;
|
|
492
|
+
// Write error log
|
|
493
|
+
await this.evidenceWriter.writeArtifact(context.evidence_dir, `logs/${step.id}.log`, {
|
|
494
|
+
error: errorMessage,
|
|
495
|
+
timestamp: new Date().toISOString(),
|
|
496
|
+
});
|
|
497
|
+
// Emit failure episode using MOVA 4.1.1 format
|
|
498
|
+
await context.episodeWriter.writeExecutionEpisode({
|
|
499
|
+
episode_type: 'execution_step',
|
|
500
|
+
result_status: 'failed',
|
|
501
|
+
result_summary: `Step ${step.id} failed: ${errorMessage}`,
|
|
502
|
+
input_data_refs: [{ data_type: 'ds.mova_agent_step_v1', data_id: step.id }],
|
|
503
|
+
payload: {
|
|
504
|
+
step_id: step.id,
|
|
505
|
+
error: errorMessage,
|
|
506
|
+
status: 'failed',
|
|
507
|
+
},
|
|
508
|
+
});
|
|
509
|
+
return { success: false, error: errorMessage };
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Простой резолвер пути для получения значения из объекта по строковому пути
|
|
514
|
+
*/
|
|
515
|
+
resolvePath(obj, path) {
|
|
516
|
+
return path.split('.').reduce((current, part) => current?.[part], obj);
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Записать событие безопасности
|
|
520
|
+
*/
|
|
521
|
+
async recordSecurityEvent(eventType, details, context) {
|
|
522
|
+
// Determine if this is a high severity event that should mark the run as failed
|
|
523
|
+
const severity = this.getSecurityEventSeverity(eventType);
|
|
524
|
+
const isHighSeverity = ['high', 'critical'].includes(severity);
|
|
525
|
+
// If we have context, mark the run as having a fatal security event
|
|
526
|
+
if (context && isHighSeverity) {
|
|
527
|
+
context.has_fatal_security_event = true;
|
|
528
|
+
}
|
|
529
|
+
// Use the run-specific episode writer to write security events
|
|
530
|
+
const runEpisodeWriter = context?.episodeWriter ||
|
|
531
|
+
this.episodeWriter.createRunWriter(context?.request_id || 'unknown', context?.run_id || 'unknown');
|
|
532
|
+
// Explicitly type the category and severity to satisfy TypeScript
|
|
533
|
+
const category = this.getSecurityEventCategory(eventType);
|
|
534
|
+
const eventSeverity = severity;
|
|
535
|
+
await runEpisodeWriter.writeSecurityEvent({
|
|
536
|
+
episode_type: `security_event/${eventType}`,
|
|
537
|
+
security_event_type: eventType,
|
|
538
|
+
security_event_category: category,
|
|
539
|
+
severity: eventSeverity,
|
|
540
|
+
result_status: 'failed', // Security events typically represent failures
|
|
541
|
+
result_summary: `Security event: ${eventType}`,
|
|
542
|
+
detection_source: 'runtime_guard',
|
|
543
|
+
policy_profile_id: 'mova_security_default_v1',
|
|
544
|
+
security_model_version: '1.0.0',
|
|
545
|
+
// Include context information in meta_episode
|
|
546
|
+
meta_episode: {
|
|
547
|
+
request_id: context?.request_id || 'unknown',
|
|
548
|
+
run_id: context?.run_id || 'unknown',
|
|
549
|
+
step_id: details?.step_id,
|
|
550
|
+
},
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Определить категорию события безопасности
|
|
555
|
+
*/
|
|
556
|
+
getSecurityEventCategory(eventType) {
|
|
557
|
+
const categoryMap = {
|
|
558
|
+
validation_failed: 'policy_violation',
|
|
559
|
+
tool_not_allowlisted: 'authorization',
|
|
560
|
+
destination_not_allowlisted: 'authorization',
|
|
561
|
+
limits_not_specified: 'config',
|
|
562
|
+
input_validation_failed: 'policy_violation',
|
|
563
|
+
output_validation_failed: 'policy_violation',
|
|
564
|
+
handler_not_found: 'config',
|
|
565
|
+
execution_error: 'infrastructure',
|
|
566
|
+
timeout: 'rate_limit',
|
|
567
|
+
};
|
|
568
|
+
return categoryMap[eventType] || 'other';
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Определить уровень серьезности события безопасности
|
|
572
|
+
*/
|
|
573
|
+
getSecurityEventSeverity(eventType) {
|
|
574
|
+
const severityMap = {
|
|
575
|
+
validation_failed: 'high',
|
|
576
|
+
tool_not_allowlisted: 'high',
|
|
577
|
+
destination_not_allowlisted: 'high',
|
|
578
|
+
limits_not_specified: 'medium',
|
|
579
|
+
input_validation_failed: 'medium',
|
|
580
|
+
output_validation_failed: 'medium',
|
|
581
|
+
handler_not_found: 'high',
|
|
582
|
+
execution_error: 'high',
|
|
583
|
+
timeout: 'high',
|
|
584
|
+
};
|
|
585
|
+
return severityMap[eventType] || 'low';
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Валидация профиля инструкций с политикой deny-by-default
|
|
589
|
+
*/
|
|
590
|
+
validateInstructionProfile(profile) {
|
|
591
|
+
// Проверяем, что профиль не пустой
|
|
592
|
+
if (!profile) {
|
|
593
|
+
return { ok: false, errors: ['Instruction profile is required'] };
|
|
594
|
+
}
|
|
595
|
+
// Валидация лимитов
|
|
596
|
+
if (profile.caps) {
|
|
597
|
+
const errors = [];
|
|
598
|
+
if (profile.caps.max_timeout_ms && typeof profile.caps.max_timeout_ms !== 'number') {
|
|
599
|
+
errors.push('max_timeout_ms must be a number');
|
|
600
|
+
}
|
|
601
|
+
if (profile.caps.max_data_size && typeof profile.caps.max_data_size !== 'number') {
|
|
602
|
+
errors.push('max_data_size must be a number');
|
|
603
|
+
}
|
|
604
|
+
if (profile.caps.max_steps && typeof profile.caps.max_steps !== 'number') {
|
|
605
|
+
errors.push('max_steps must be a number');
|
|
606
|
+
}
|
|
607
|
+
if (errors.length > 0) {
|
|
608
|
+
return { ok: false, errors };
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
// Проверка правил маскировки
|
|
612
|
+
if (profile.redaction_rules && !Array.isArray(profile.redaction_rules)) {
|
|
613
|
+
return { ok: false, errors: ['redaction_rules must be an array'] };
|
|
614
|
+
}
|
|
615
|
+
return { ok: true };
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Создать сводку выполнения
|
|
619
|
+
*/
|
|
620
|
+
async createRunSummary(context, status, error) {
|
|
621
|
+
// Save token usage report
|
|
622
|
+
context.tokenMeter.saveReport();
|
|
623
|
+
return {
|
|
624
|
+
run_id: context.run_id,
|
|
625
|
+
request_id: context.request_id,
|
|
626
|
+
status,
|
|
627
|
+
timestamp_start: new Date().toISOString(), // в реальности это должно быть точное время начала
|
|
628
|
+
timestamp_end: new Date().toISOString(),
|
|
629
|
+
steps_executed: context.step_outputs.size,
|
|
630
|
+
steps_successful: status === 'completed' ? context.step_outputs.size : context.step_outputs.size - 1, // упрощённо
|
|
631
|
+
steps_failed: status === 'failed' ? 1 : 0, // упрощённо
|
|
632
|
+
total_duration_ms: 0, // в реальности нужно отслеживать время
|
|
633
|
+
error_summary: error,
|
|
634
|
+
token_budget: context.tokenBudgetEnforcer.getBudgetInfo(),
|
|
635
|
+
token_usage: context.tokenMeter.getBriefSummary(),
|
|
636
|
+
budget_status: context.budgetStatus,
|
|
637
|
+
budget_violations: context.budgetViolations,
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Основной метод выполнения плана
|
|
642
|
+
*/
|
|
643
|
+
async executePlan(plan) {
|
|
644
|
+
// Инициализируем контекст выполнения
|
|
645
|
+
const tokenMeter = new token_meter_1.TokenMeter();
|
|
646
|
+
const tokenBudgetEnforcer = new token_budget_enforcer_1.TokenBudgetEnforcer({ version: '1.0', limits: {}, policy: { on_budget_exceeded: 'warn' } }, tokenMeter);
|
|
647
|
+
const context = {
|
|
648
|
+
run_id: (0, crypto_1.randomUUID)(),
|
|
649
|
+
request_id: plan.request_id || (0, crypto_1.randomUUID)(),
|
|
650
|
+
evidence_dir: 'artifacts/mova_agent/evidence',
|
|
651
|
+
instructionProfile: plan.instruction_profile,
|
|
652
|
+
step_inputs: new Map(),
|
|
653
|
+
step_outputs: new Map(),
|
|
654
|
+
step_bindings: new Map(),
|
|
655
|
+
current_step_index: 0,
|
|
656
|
+
step_security_events: [],
|
|
657
|
+
has_fatal_security_event: false,
|
|
658
|
+
episodeWriter: this.episodeWriter.createRunWriter(plan.request_id || (0, crypto_1.randomUUID)(), (0, crypto_1.randomUUID)()),
|
|
659
|
+
tokenMeter,
|
|
660
|
+
tokenBudgetEnforcer,
|
|
661
|
+
budgetStatus: 'passed',
|
|
662
|
+
budgetViolations: [],
|
|
663
|
+
};
|
|
664
|
+
try {
|
|
665
|
+
// Валидация профиля инструкций
|
|
666
|
+
const profileValidation = this.validateInstructionProfile(plan.instruction_profile);
|
|
667
|
+
if (!profileValidation.ok) {
|
|
668
|
+
throw new Error(`Instruction profile validation failed: ${profileValidation.errors?.join(', ')}`);
|
|
669
|
+
}
|
|
670
|
+
(0, logger_1.getLogger)().info(`Starting execution for plan: ${plan.plan_id}`);
|
|
671
|
+
// Создадим минимальный тулпул для тестирования
|
|
672
|
+
const toolPool = {
|
|
673
|
+
tools: [],
|
|
674
|
+
};
|
|
675
|
+
// Выполняем каждый шаг плана
|
|
676
|
+
for (const step of plan.steps) {
|
|
677
|
+
// Адаптируем структуру шага для внутреннего формата
|
|
678
|
+
const internalStep = {
|
|
679
|
+
id: step.step_id,
|
|
680
|
+
verb: step.verb,
|
|
681
|
+
connector_id: '', // будет установлено из binding
|
|
682
|
+
input: step.input,
|
|
683
|
+
};
|
|
684
|
+
// Добавляем обработку binding в контекст для данного шага
|
|
685
|
+
if (step.tool_binding) {
|
|
686
|
+
context.step_bindings.set(step.step_id, step.tool_binding);
|
|
687
|
+
// Найдем соответствующий тул в тулпуле или создадим его
|
|
688
|
+
const toolId = `${step.verb}_tool`;
|
|
689
|
+
const toolExists = toolPool.tools.some((tool) => tool.id === toolId);
|
|
690
|
+
if (!toolExists) {
|
|
691
|
+
toolPool.tools.push({
|
|
692
|
+
id: toolId,
|
|
693
|
+
connector: { verb: step.verb }, // упрощенно
|
|
694
|
+
binding: step.tool_binding,
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
const stepResult = await this.executeStep(internalStep, context, toolPool);
|
|
699
|
+
if (stepResult.success) {
|
|
700
|
+
context.step_outputs.set(step.step_id, stepResult.output);
|
|
701
|
+
}
|
|
702
|
+
else {
|
|
703
|
+
throw new Error(`Step ${step.step_id} failed: ${stepResult.error}`);
|
|
704
|
+
}
|
|
705
|
+
context.current_step_index++;
|
|
706
|
+
}
|
|
707
|
+
// Создаем итоговую сводку
|
|
708
|
+
const summary = await this.createRunSummary(context, 'success');
|
|
709
|
+
return {
|
|
710
|
+
ok: true,
|
|
711
|
+
summary,
|
|
712
|
+
outputs: Object.fromEntries(context.step_outputs),
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
catch (error) {
|
|
716
|
+
// Регистрируем ошибку безопасности
|
|
717
|
+
await this.recordSecurityEvent('execution_failure', {
|
|
718
|
+
error: error.message,
|
|
719
|
+
plan_id: plan.plan_id,
|
|
720
|
+
});
|
|
721
|
+
// Создаем сводку с ошибкой
|
|
722
|
+
const summary = await this.createRunSummary(context, 'failure', error.message);
|
|
723
|
+
return {
|
|
724
|
+
ok: false,
|
|
725
|
+
error: error.message,
|
|
726
|
+
summary,
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
exports.Interpreter = Interpreter;
|
|
732
|
+
//# sourceMappingURL=interpreter.js.map
|