pm-orchestrator-runner 1.0.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.
- package/LICENSE +21 -0
- package/README.md +108 -0
- package/dist/cli/cli-interface.d.ts +150 -0
- package/dist/cli/cli-interface.d.ts.map +1 -0
- package/dist/cli/cli-interface.js +606 -0
- package/dist/cli/cli-interface.js.map +1 -0
- package/dist/cli/index.d.ts +13 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +243 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/llm-sentinel.d.ts +15 -0
- package/dist/cli/llm-sentinel.d.ts.map +1 -0
- package/dist/cli/llm-sentinel.js +184 -0
- package/dist/cli/llm-sentinel.js.map +1 -0
- package/dist/config/configuration-manager.d.ts +149 -0
- package/dist/config/configuration-manager.d.ts.map +1 -0
- package/dist/config/configuration-manager.js +241 -0
- package/dist/config/configuration-manager.js.map +1 -0
- package/dist/continuation/continuation-control-manager.d.ts +154 -0
- package/dist/continuation/continuation-control-manager.d.ts.map +1 -0
- package/dist/continuation/continuation-control-manager.js +303 -0
- package/dist/continuation/continuation-control-manager.js.map +1 -0
- package/dist/core/runner-core.d.ts +474 -0
- package/dist/core/runner-core.d.ts.map +1 -0
- package/dist/core/runner-core.js +1311 -0
- package/dist/core/runner-core.js.map +1 -0
- package/dist/errors/error-codes.d.ts +105 -0
- package/dist/errors/error-codes.d.ts.map +1 -0
- package/dist/errors/error-codes.js +198 -0
- package/dist/errors/error-codes.js.map +1 -0
- package/dist/errors/runner-error.d.ts +14 -0
- package/dist/errors/runner-error.d.ts.map +1 -0
- package/dist/errors/runner-error.js +33 -0
- package/dist/errors/runner-error.js.map +1 -0
- package/dist/evidence/evidence-manager.d.ts +112 -0
- package/dist/evidence/evidence-manager.d.ts.map +1 -0
- package/dist/evidence/evidence-manager.js +337 -0
- package/dist/evidence/evidence-manager.js.map +1 -0
- package/dist/executor/claude-code-executor.d.ts +136 -0
- package/dist/executor/claude-code-executor.d.ts.map +1 -0
- package/dist/executor/claude-code-executor.js +643 -0
- package/dist/executor/claude-code-executor.js.map +1 -0
- package/dist/executor/deterministic-executor.d.ts +40 -0
- package/dist/executor/deterministic-executor.d.ts.map +1 -0
- package/dist/executor/deterministic-executor.js +269 -0
- package/dist/executor/deterministic-executor.js.map +1 -0
- package/dist/lifecycle/lifecycle-controller.d.ts +270 -0
- package/dist/lifecycle/lifecycle-controller.d.ts.map +1 -0
- package/dist/lifecycle/lifecycle-controller.js +596 -0
- package/dist/lifecycle/lifecycle-controller.js.map +1 -0
- package/dist/limits/resource-limit-manager.d.ts +200 -0
- package/dist/limits/resource-limit-manager.d.ts.map +1 -0
- package/dist/limits/resource-limit-manager.js +376 -0
- package/dist/limits/resource-limit-manager.js.map +1 -0
- package/dist/locks/lock-manager.d.ts +116 -0
- package/dist/locks/lock-manager.d.ts.map +1 -0
- package/dist/locks/lock-manager.js +306 -0
- package/dist/locks/lock-manager.js.map +1 -0
- package/dist/logging/index.d.ts +8 -0
- package/dist/logging/index.d.ts.map +1 -0
- package/dist/logging/index.js +22 -0
- package/dist/logging/index.js.map +1 -0
- package/dist/logging/sensitive-data-masker.d.ts +90 -0
- package/dist/logging/sensitive-data-masker.d.ts.map +1 -0
- package/dist/logging/sensitive-data-masker.js +228 -0
- package/dist/logging/sensitive-data-masker.js.map +1 -0
- package/dist/logging/task-log-manager.d.ts +215 -0
- package/dist/logging/task-log-manager.d.ts.map +1 -0
- package/dist/logging/task-log-manager.js +743 -0
- package/dist/logging/task-log-manager.js.map +1 -0
- package/dist/mediation/fail-closed-runner.d.ts +131 -0
- package/dist/mediation/fail-closed-runner.d.ts.map +1 -0
- package/dist/mediation/fail-closed-runner.js +245 -0
- package/dist/mediation/fail-closed-runner.js.map +1 -0
- package/dist/mediation/llm-client-with-evidence.d.ts +123 -0
- package/dist/mediation/llm-client-with-evidence.d.ts.map +1 -0
- package/dist/mediation/llm-client-with-evidence.js +245 -0
- package/dist/mediation/llm-client-with-evidence.js.map +1 -0
- package/dist/mediation/llm-client.d.ts +102 -0
- package/dist/mediation/llm-client.d.ts.map +1 -0
- package/dist/mediation/llm-client.js +206 -0
- package/dist/mediation/llm-client.js.map +1 -0
- package/dist/mediation/llm-evidence-manager.d.ts +108 -0
- package/dist/mediation/llm-evidence-manager.d.ts.map +1 -0
- package/dist/mediation/llm-evidence-manager.js +230 -0
- package/dist/mediation/llm-evidence-manager.js.map +1 -0
- package/dist/mediation/llm-mediation-layer.d.ts +175 -0
- package/dist/mediation/llm-mediation-layer.d.ts.map +1 -0
- package/dist/mediation/llm-mediation-layer.js +315 -0
- package/dist/mediation/llm-mediation-layer.js.map +1 -0
- package/dist/mediation/llm-sentinel.d.ts +107 -0
- package/dist/mediation/llm-sentinel.d.ts.map +1 -0
- package/dist/mediation/llm-sentinel.js +187 -0
- package/dist/mediation/llm-sentinel.js.map +1 -0
- package/dist/mediation/real-llm-mediation-layer.d.ts +104 -0
- package/dist/mediation/real-llm-mediation-layer.d.ts.map +1 -0
- package/dist/mediation/real-llm-mediation-layer.js +322 -0
- package/dist/mediation/real-llm-mediation-layer.js.map +1 -0
- package/dist/mediation/verdict-reporter.d.ts +61 -0
- package/dist/mediation/verdict-reporter.d.ts.map +1 -0
- package/dist/mediation/verdict-reporter.js +178 -0
- package/dist/mediation/verdict-reporter.js.map +1 -0
- package/dist/models/enums.d.ts +133 -0
- package/dist/models/enums.d.ts.map +1 -0
- package/dist/models/enums.js +201 -0
- package/dist/models/enums.js.map +1 -0
- package/dist/models/evidence.d.ts +60 -0
- package/dist/models/evidence.d.ts.map +1 -0
- package/dist/models/evidence.js +135 -0
- package/dist/models/evidence.js.map +1 -0
- package/dist/models/execution-result.d.ts +89 -0
- package/dist/models/execution-result.d.ts.map +1 -0
- package/dist/models/execution-result.js +197 -0
- package/dist/models/execution-result.js.map +1 -0
- package/dist/models/file-lock.d.ts +62 -0
- package/dist/models/file-lock.d.ts.map +1 -0
- package/dist/models/file-lock.js +133 -0
- package/dist/models/file-lock.js.map +1 -0
- package/dist/models/index.d.ts +12 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/index.js +91 -0
- package/dist/models/index.js.map +1 -0
- package/dist/models/repl/index.d.ts +7 -0
- package/dist/models/repl/index.d.ts.map +1 -0
- package/dist/models/repl/index.js +32 -0
- package/dist/models/repl/index.js.map +1 -0
- package/dist/models/repl/model-registry.d.ts +73 -0
- package/dist/models/repl/model-registry.d.ts.map +1 -0
- package/dist/models/repl/model-registry.js +116 -0
- package/dist/models/repl/model-registry.js.map +1 -0
- package/dist/models/repl/repl-state.d.ts +86 -0
- package/dist/models/repl/repl-state.d.ts.map +1 -0
- package/dist/models/repl/repl-state.js +152 -0
- package/dist/models/repl/repl-state.js.map +1 -0
- package/dist/models/repl/task-log.d.ts +247 -0
- package/dist/models/repl/task-log.d.ts.map +1 -0
- package/dist/models/repl/task-log.js +178 -0
- package/dist/models/repl/task-log.js.map +1 -0
- package/dist/models/session.d.ts +71 -0
- package/dist/models/session.d.ts.map +1 -0
- package/dist/models/session.js +140 -0
- package/dist/models/session.js.map +1 -0
- package/dist/models/supporting.d.ts +97 -0
- package/dist/models/supporting.d.ts.map +1 -0
- package/dist/models/supporting.js +208 -0
- package/dist/models/supporting.js.map +1 -0
- package/dist/models/task.d.ts +77 -0
- package/dist/models/task.d.ts.map +1 -0
- package/dist/models/task.js +170 -0
- package/dist/models/task.js.map +1 -0
- package/dist/output/output-control-manager.d.ts +217 -0
- package/dist/output/output-control-manager.d.ts.map +1 -0
- package/dist/output/output-control-manager.js +378 -0
- package/dist/output/output-control-manager.js.map +1 -0
- package/dist/pool/agent-pool.d.ts +284 -0
- package/dist/pool/agent-pool.d.ts.map +1 -0
- package/dist/pool/agent-pool.js +451 -0
- package/dist/pool/agent-pool.js.map +1 -0
- package/dist/repl/commands/index.d.ts +12 -0
- package/dist/repl/commands/index.d.ts.map +1 -0
- package/dist/repl/commands/index.js +26 -0
- package/dist/repl/commands/index.js.map +1 -0
- package/dist/repl/commands/init.d.ts +31 -0
- package/dist/repl/commands/init.d.ts.map +1 -0
- package/dist/repl/commands/init.js +234 -0
- package/dist/repl/commands/init.js.map +1 -0
- package/dist/repl/commands/keys.d.ts +63 -0
- package/dist/repl/commands/keys.d.ts.map +1 -0
- package/dist/repl/commands/keys.js +114 -0
- package/dist/repl/commands/keys.js.map +1 -0
- package/dist/repl/commands/logs.d.ts +91 -0
- package/dist/repl/commands/logs.d.ts.map +1 -0
- package/dist/repl/commands/logs.js +200 -0
- package/dist/repl/commands/logs.js.map +1 -0
- package/dist/repl/commands/model.d.ts +85 -0
- package/dist/repl/commands/model.d.ts.map +1 -0
- package/dist/repl/commands/model.js +225 -0
- package/dist/repl/commands/model.js.map +1 -0
- package/dist/repl/commands/models.d.ts +50 -0
- package/dist/repl/commands/models.d.ts.map +1 -0
- package/dist/repl/commands/models.js +180 -0
- package/dist/repl/commands/models.js.map +1 -0
- package/dist/repl/commands/provider.d.ts +79 -0
- package/dist/repl/commands/provider.d.ts.map +1 -0
- package/dist/repl/commands/provider.js +291 -0
- package/dist/repl/commands/provider.js.map +1 -0
- package/dist/repl/commands/session.d.ts +50 -0
- package/dist/repl/commands/session.d.ts.map +1 -0
- package/dist/repl/commands/session.js +152 -0
- package/dist/repl/commands/session.js.map +1 -0
- package/dist/repl/commands/status.d.ts +55 -0
- package/dist/repl/commands/status.d.ts.map +1 -0
- package/dist/repl/commands/status.js +182 -0
- package/dist/repl/commands/status.js.map +1 -0
- package/dist/repl/index.d.ts +6 -0
- package/dist/repl/index.d.ts.map +1 -0
- package/dist/repl/index.js +25 -0
- package/dist/repl/index.js.map +1 -0
- package/dist/repl/repl-interface.d.ts +371 -0
- package/dist/repl/repl-interface.d.ts.map +1 -0
- package/dist/repl/repl-interface.js +1214 -0
- package/dist/repl/repl-interface.js.map +1 -0
- package/dist/session/session-manager.d.ts +85 -0
- package/dist/session/session-manager.d.ts.map +1 -0
- package/dist/session/session-manager.js +217 -0
- package/dist/session/session-manager.js.map +1 -0
- package/dist/supervisor/executor-supervisor.d.ts +90 -0
- package/dist/supervisor/executor-supervisor.d.ts.map +1 -0
- package/dist/supervisor/executor-supervisor.js +223 -0
- package/dist/supervisor/executor-supervisor.js.map +1 -0
- package/dist/supervisor/index.d.ts +5 -0
- package/dist/supervisor/index.d.ts.map +1 -0
- package/dist/supervisor/index.js +9 -0
- package/dist/supervisor/index.js.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* LLM Client with Evidence Recording
|
|
4
|
+
*
|
|
5
|
+
* Extends LLMClient with automatic evidence recording for fail-closed behavior.
|
|
6
|
+
* Every API call generates a proof file that can be verified.
|
|
7
|
+
*
|
|
8
|
+
* ARCHITECTURAL RULES:
|
|
9
|
+
* - Every LLM call MUST generate evidence
|
|
10
|
+
* - COMPLETE status can only be asserted if evidence exists
|
|
11
|
+
* - Double Execution Gate: API key check + Evidence file check
|
|
12
|
+
*/
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
+
}) : function(o, v) {
|
|
27
|
+
o["default"] = v;
|
|
28
|
+
});
|
|
29
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
+
var ownKeys = function(o) {
|
|
31
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
+
var ar = [];
|
|
33
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
return ownKeys(o);
|
|
37
|
+
};
|
|
38
|
+
return function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.LLMClientWithEvidence = void 0;
|
|
48
|
+
exports.createLLMClientWithEvidence = createLLMClientWithEvidence;
|
|
49
|
+
const crypto = __importStar(require("crypto"));
|
|
50
|
+
const llm_client_1 = require("./llm-client");
|
|
51
|
+
const llm_evidence_manager_1 = require("./llm-evidence-manager");
|
|
52
|
+
/**
|
|
53
|
+
* LLM Client with automatic evidence recording
|
|
54
|
+
*
|
|
55
|
+
* Implements Double Execution Gate:
|
|
56
|
+
* - Gate 1: API key validation (fail-closed)
|
|
57
|
+
* - Gate 2: Evidence directory ready (fail-closed)
|
|
58
|
+
*
|
|
59
|
+
* Both gates must pass before any LLM call can be made.
|
|
60
|
+
*/
|
|
61
|
+
class LLMClientWithEvidence {
|
|
62
|
+
client;
|
|
63
|
+
evidenceManager;
|
|
64
|
+
provider;
|
|
65
|
+
model;
|
|
66
|
+
gateCheckPerformed = false;
|
|
67
|
+
gateResult = null;
|
|
68
|
+
constructor(config) {
|
|
69
|
+
// Gate 1: API key validation (fail-closed)
|
|
70
|
+
// This will throw APIKeyMissingError if key is not set
|
|
71
|
+
this.client = llm_client_1.LLMClient.fromEnv(config.provider ?? 'openai', config.model, { temperature: config.temperature ?? 0.7 });
|
|
72
|
+
// Gate 2: Evidence directory initialization
|
|
73
|
+
this.evidenceManager = new llm_evidence_manager_1.LLMEvidenceManager(config.evidenceDir);
|
|
74
|
+
this.provider = config.provider ?? 'openai';
|
|
75
|
+
this.model = config.model ?? (config.provider === 'anthropic' ? 'claude-3-haiku-20240307' : 'gpt-4o-mini');
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Create client from environment with evidence recording
|
|
79
|
+
* @throws APIKeyMissingError if API key is not set (fail-closed)
|
|
80
|
+
*/
|
|
81
|
+
static fromEnv(evidenceDir, provider = 'openai', model, options) {
|
|
82
|
+
return new LLMClientWithEvidence({
|
|
83
|
+
provider,
|
|
84
|
+
model,
|
|
85
|
+
temperature: options?.temperature,
|
|
86
|
+
evidenceDir,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Check Double Execution Gate
|
|
91
|
+
*
|
|
92
|
+
* Gate 1: API key is valid (already checked in constructor)
|
|
93
|
+
* Gate 2: Evidence directory is ready
|
|
94
|
+
*
|
|
95
|
+
* @returns Gate check result
|
|
96
|
+
*/
|
|
97
|
+
checkExecutionGate() {
|
|
98
|
+
// Gate 1 passed if we got here (constructor would throw otherwise)
|
|
99
|
+
const gate1Pass = true;
|
|
100
|
+
// Gate 2: Evidence directory ready
|
|
101
|
+
const evidenceDir = this.evidenceManager.getEvidenceDir();
|
|
102
|
+
const gate2Pass = evidenceDir !== null && evidenceDir !== undefined;
|
|
103
|
+
this.gateResult = {
|
|
104
|
+
gate1_api_key: gate1Pass ? 'PASS' : 'FAIL',
|
|
105
|
+
gate2_evidence_ready: gate2Pass ? 'PASS' : 'FAIL',
|
|
106
|
+
can_execute: gate1Pass && gate2Pass,
|
|
107
|
+
failure_reason: !gate1Pass
|
|
108
|
+
? 'API key not configured'
|
|
109
|
+
: !gate2Pass
|
|
110
|
+
? 'Evidence directory not ready'
|
|
111
|
+
: undefined,
|
|
112
|
+
};
|
|
113
|
+
this.gateCheckPerformed = true;
|
|
114
|
+
return this.gateResult;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Send chat completion request with automatic evidence recording
|
|
118
|
+
*
|
|
119
|
+
* @throws Error if Double Execution Gate not passed
|
|
120
|
+
*/
|
|
121
|
+
async chat(messages) {
|
|
122
|
+
// Enforce Double Execution Gate check
|
|
123
|
+
if (!this.gateCheckPerformed) {
|
|
124
|
+
this.checkExecutionGate();
|
|
125
|
+
}
|
|
126
|
+
if (!this.gateResult?.can_execute) {
|
|
127
|
+
throw new Error(`LLM execution blocked by Double Execution Gate: ${this.gateResult?.failure_reason}`);
|
|
128
|
+
}
|
|
129
|
+
const callId = this.generateCallId();
|
|
130
|
+
const requestHash = (0, llm_evidence_manager_1.hashRequest)(messages);
|
|
131
|
+
const startTime = Date.now();
|
|
132
|
+
try {
|
|
133
|
+
// Make the actual API call
|
|
134
|
+
const response = await this.client.chat(messages);
|
|
135
|
+
const endTime = Date.now();
|
|
136
|
+
// Record success evidence
|
|
137
|
+
const evidence = {
|
|
138
|
+
call_id: callId,
|
|
139
|
+
provider: this.provider,
|
|
140
|
+
model: this.model,
|
|
141
|
+
request_hash: requestHash,
|
|
142
|
+
response_hash: (0, llm_evidence_manager_1.hashResponse)(response.content),
|
|
143
|
+
timestamp: new Date().toISOString(),
|
|
144
|
+
duration_ms: endTime - startTime,
|
|
145
|
+
success: true,
|
|
146
|
+
};
|
|
147
|
+
this.evidenceManager.recordEvidence(evidence);
|
|
148
|
+
return {
|
|
149
|
+
...response,
|
|
150
|
+
evidence_id: callId,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
const endTime = Date.now();
|
|
155
|
+
// Record failure evidence
|
|
156
|
+
const evidence = {
|
|
157
|
+
call_id: callId,
|
|
158
|
+
provider: this.provider,
|
|
159
|
+
model: this.model,
|
|
160
|
+
request_hash: requestHash,
|
|
161
|
+
response_hash: null,
|
|
162
|
+
timestamp: new Date().toISOString(),
|
|
163
|
+
duration_ms: endTime - startTime,
|
|
164
|
+
success: false,
|
|
165
|
+
error: error instanceof Error ? error.message : String(error),
|
|
166
|
+
};
|
|
167
|
+
this.evidenceManager.recordEvidence(evidence);
|
|
168
|
+
// Re-throw the error
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Check if COMPLETE status can be asserted
|
|
174
|
+
*
|
|
175
|
+
* This is the fail-closed check:
|
|
176
|
+
* - Returns true only if at least one successful LLM call has evidence
|
|
177
|
+
* - Returns false if no evidence exists or all calls failed
|
|
178
|
+
*/
|
|
179
|
+
canAssertComplete() {
|
|
180
|
+
return this.evidenceManager.canAssertComplete();
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Get evidence statistics
|
|
184
|
+
*/
|
|
185
|
+
getEvidenceStats() {
|
|
186
|
+
return this.evidenceManager.getStats();
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Get evidence by call ID
|
|
190
|
+
*/
|
|
191
|
+
getEvidence(callId) {
|
|
192
|
+
return this.evidenceManager.getEvidence(callId);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Verify evidence integrity
|
|
196
|
+
*/
|
|
197
|
+
verifyEvidence(callId) {
|
|
198
|
+
return this.evidenceManager.verifyIntegrity(callId);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* List all evidence
|
|
202
|
+
*/
|
|
203
|
+
listEvidence() {
|
|
204
|
+
return this.evidenceManager.listEvidence();
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Get the underlying evidence manager
|
|
208
|
+
*/
|
|
209
|
+
getEvidenceManager() {
|
|
210
|
+
return this.evidenceManager;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Get the configured provider
|
|
214
|
+
*/
|
|
215
|
+
getProvider() {
|
|
216
|
+
return this.provider;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Get the configured model
|
|
220
|
+
*/
|
|
221
|
+
getModel() {
|
|
222
|
+
return this.model;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Generate unique call ID
|
|
226
|
+
*/
|
|
227
|
+
generateCallId() {
|
|
228
|
+
const timestamp = Date.now().toString(36);
|
|
229
|
+
const random = crypto.randomBytes(4).toString('hex');
|
|
230
|
+
return `llm-${timestamp}-${random}`;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
exports.LLMClientWithEvidence = LLMClientWithEvidence;
|
|
234
|
+
/**
|
|
235
|
+
* Create LLM Client with Evidence (convenience function)
|
|
236
|
+
*
|
|
237
|
+
* @param evidenceDir - Directory to store evidence files
|
|
238
|
+
* @param provider - LLM provider (default: openai)
|
|
239
|
+
* @param model - Model name (optional)
|
|
240
|
+
* @throws APIKeyMissingError if API key is not configured
|
|
241
|
+
*/
|
|
242
|
+
function createLLMClientWithEvidence(evidenceDir, provider = 'openai', model) {
|
|
243
|
+
return LLMClientWithEvidence.fromEnv(evidenceDir, provider, model);
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=llm-client-with-evidence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-client-with-evidence.js","sourceRoot":"","sources":["../../src/mediation/llm-client-with-evidence.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM Client - Real API calls to LLM providers
|
|
3
|
+
*
|
|
4
|
+
* REQUIREMENTS:
|
|
5
|
+
* - NO stubs, mocks, or fixed responses
|
|
6
|
+
* - temperature > 0 required (non-deterministic output)
|
|
7
|
+
* - API key from environment variables only
|
|
8
|
+
* - fail-closed on missing API key
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* LLM Provider types
|
|
12
|
+
*/
|
|
13
|
+
export type LLMProvider = 'openai' | 'anthropic';
|
|
14
|
+
/**
|
|
15
|
+
* LLM Client configuration
|
|
16
|
+
*/
|
|
17
|
+
export interface LLMClientConfig {
|
|
18
|
+
provider: LLMProvider;
|
|
19
|
+
model: string;
|
|
20
|
+
apiKey: string;
|
|
21
|
+
temperature?: number;
|
|
22
|
+
maxTokens?: number;
|
|
23
|
+
baseUrl?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Chat message format
|
|
27
|
+
*/
|
|
28
|
+
export interface ChatMessage {
|
|
29
|
+
role: 'system' | 'user' | 'assistant';
|
|
30
|
+
content: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* LLM response
|
|
34
|
+
*/
|
|
35
|
+
export interface LLMResponse {
|
|
36
|
+
content: string;
|
|
37
|
+
model: string;
|
|
38
|
+
usage?: {
|
|
39
|
+
prompt_tokens: number;
|
|
40
|
+
completion_tokens: number;
|
|
41
|
+
total_tokens: number;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Error thrown when API key is missing (fail-closed)
|
|
46
|
+
*/
|
|
47
|
+
export declare class APIKeyMissingError extends Error {
|
|
48
|
+
constructor(provider: LLMProvider);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Error thrown when LLM API call fails
|
|
52
|
+
*/
|
|
53
|
+
export declare class LLMAPIError extends Error {
|
|
54
|
+
readonly provider: LLMProvider;
|
|
55
|
+
readonly statusCode: number;
|
|
56
|
+
readonly responseBody: string;
|
|
57
|
+
constructor(provider: LLMProvider, statusCode: number, responseBody: string);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Get API key from environment variable
|
|
61
|
+
* @throws APIKeyMissingError if key is not set (fail-closed)
|
|
62
|
+
*/
|
|
63
|
+
export declare function getAPIKeyFromEnv(provider: LLMProvider): string;
|
|
64
|
+
/**
|
|
65
|
+
* LLM Client class - Makes real API calls to LLM providers
|
|
66
|
+
*
|
|
67
|
+
* IMPORTANT: This class NEVER uses stubs or mocks.
|
|
68
|
+
* All calls go to the real LLM API.
|
|
69
|
+
*/
|
|
70
|
+
export declare class LLMClient {
|
|
71
|
+
private readonly config;
|
|
72
|
+
constructor(config: LLMClientConfig);
|
|
73
|
+
/**
|
|
74
|
+
* Create LLM client from environment variables
|
|
75
|
+
* @throws APIKeyMissingError if API key is not set (fail-closed)
|
|
76
|
+
*/
|
|
77
|
+
static fromEnv(provider?: LLMProvider, model?: string, options?: {
|
|
78
|
+
temperature?: number;
|
|
79
|
+
maxTokens?: number;
|
|
80
|
+
}): LLMClient;
|
|
81
|
+
/**
|
|
82
|
+
* Send chat completion request to LLM
|
|
83
|
+
*/
|
|
84
|
+
chat(messages: ChatMessage[]): Promise<LLMResponse>;
|
|
85
|
+
/**
|
|
86
|
+
* OpenAI API call
|
|
87
|
+
*/
|
|
88
|
+
private chatOpenAI;
|
|
89
|
+
/**
|
|
90
|
+
* Anthropic API call
|
|
91
|
+
*/
|
|
92
|
+
private chatAnthropic;
|
|
93
|
+
/**
|
|
94
|
+
* Get the configured model name
|
|
95
|
+
*/
|
|
96
|
+
getModel(): string;
|
|
97
|
+
/**
|
|
98
|
+
* Get the configured provider
|
|
99
|
+
*/
|
|
100
|
+
getProvider(): LLMProvider;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=llm-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-client.d.ts","sourceRoot":"","sources":["../../src/mediation/llm-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,WAAW,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE;QACN,aAAa,EAAE,MAAM,CAAC;QACtB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,QAAQ,EAAE,WAAW;CAIlC;AAED;;GAEG;AACH,qBAAa,WAAY,SAAQ,KAAK;aAElB,QAAQ,EAAE,WAAW;aACrB,UAAU,EAAE,MAAM;aAClB,YAAY,EAAE,MAAM;gBAFpB,QAAQ,EAAE,WAAW,EACrB,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM;CAKvC;AAcD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,WAAW,GAAG,MAAM,CAS9D;AAgBD;;;;;GAKG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;gBAEvC,MAAM,EAAE,eAAe;IAiBnC;;;OAGG;IACH,MAAM,CAAC,OAAO,CACZ,QAAQ,GAAE,WAAsB,EAChC,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GACrD,SAAS;IAWZ;;OAEG;IACG,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IASzD;;OAEG;YACW,UAAU;IAiCxB;;OAEG;YACW,aAAa;IA8C3B;;OAEG;IACH,QAAQ,IAAI,MAAM;IAIlB;;OAEG;IACH,WAAW,IAAI,WAAW;CAG3B"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* LLM Client - Real API calls to LLM providers
|
|
4
|
+
*
|
|
5
|
+
* REQUIREMENTS:
|
|
6
|
+
* - NO stubs, mocks, or fixed responses
|
|
7
|
+
* - temperature > 0 required (non-deterministic output)
|
|
8
|
+
* - API key from environment variables only
|
|
9
|
+
* - fail-closed on missing API key
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.LLMClient = exports.LLMAPIError = exports.APIKeyMissingError = void 0;
|
|
13
|
+
exports.getAPIKeyFromEnv = getAPIKeyFromEnv;
|
|
14
|
+
/**
|
|
15
|
+
* Error thrown when API key is missing (fail-closed)
|
|
16
|
+
*/
|
|
17
|
+
class APIKeyMissingError extends Error {
|
|
18
|
+
constructor(provider) {
|
|
19
|
+
super(`API key not found for provider: ${provider}. Set ${getEnvVarName(provider)} environment variable.`);
|
|
20
|
+
this.name = 'APIKeyMissingError';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.APIKeyMissingError = APIKeyMissingError;
|
|
24
|
+
/**
|
|
25
|
+
* Error thrown when LLM API call fails
|
|
26
|
+
*/
|
|
27
|
+
class LLMAPIError extends Error {
|
|
28
|
+
provider;
|
|
29
|
+
statusCode;
|
|
30
|
+
responseBody;
|
|
31
|
+
constructor(provider, statusCode, responseBody) {
|
|
32
|
+
super(`LLM API error (${provider}): ${statusCode} - ${responseBody}`);
|
|
33
|
+
this.provider = provider;
|
|
34
|
+
this.statusCode = statusCode;
|
|
35
|
+
this.responseBody = responseBody;
|
|
36
|
+
this.name = 'LLMAPIError';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.LLMAPIError = LLMAPIError;
|
|
40
|
+
/**
|
|
41
|
+
* Get environment variable name for API key
|
|
42
|
+
*/
|
|
43
|
+
function getEnvVarName(provider) {
|
|
44
|
+
switch (provider) {
|
|
45
|
+
case 'openai':
|
|
46
|
+
return 'OPENAI_API_KEY';
|
|
47
|
+
case 'anthropic':
|
|
48
|
+
return 'ANTHROPIC_API_KEY';
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Get API key from environment variable
|
|
53
|
+
* @throws APIKeyMissingError if key is not set (fail-closed)
|
|
54
|
+
*/
|
|
55
|
+
function getAPIKeyFromEnv(provider) {
|
|
56
|
+
const envVar = getEnvVarName(provider);
|
|
57
|
+
const apiKey = process.env[envVar];
|
|
58
|
+
if (!apiKey || apiKey.trim() === '') {
|
|
59
|
+
throw new APIKeyMissingError(provider);
|
|
60
|
+
}
|
|
61
|
+
return apiKey.trim();
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Default configuration for providers
|
|
65
|
+
*/
|
|
66
|
+
const DEFAULT_CONFIGS = {
|
|
67
|
+
openai: {
|
|
68
|
+
baseUrl: 'https://api.openai.com/v1',
|
|
69
|
+
defaultModel: 'gpt-4o-mini',
|
|
70
|
+
},
|
|
71
|
+
anthropic: {
|
|
72
|
+
baseUrl: 'https://api.anthropic.com/v1',
|
|
73
|
+
defaultModel: 'claude-3-haiku-20240307',
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* LLM Client class - Makes real API calls to LLM providers
|
|
78
|
+
*
|
|
79
|
+
* IMPORTANT: This class NEVER uses stubs or mocks.
|
|
80
|
+
* All calls go to the real LLM API.
|
|
81
|
+
*/
|
|
82
|
+
class LLMClient {
|
|
83
|
+
config;
|
|
84
|
+
constructor(config) {
|
|
85
|
+
// Validate temperature > 0 (required for non-determinism)
|
|
86
|
+
const temperature = config.temperature ?? 0.7;
|
|
87
|
+
if (temperature <= 0) {
|
|
88
|
+
throw new Error('temperature must be > 0 to ensure non-deterministic output');
|
|
89
|
+
}
|
|
90
|
+
this.config = {
|
|
91
|
+
provider: config.provider,
|
|
92
|
+
model: config.model,
|
|
93
|
+
apiKey: config.apiKey,
|
|
94
|
+
temperature,
|
|
95
|
+
maxTokens: config.maxTokens ?? 1024,
|
|
96
|
+
baseUrl: config.baseUrl ?? DEFAULT_CONFIGS[config.provider].baseUrl,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Create LLM client from environment variables
|
|
101
|
+
* @throws APIKeyMissingError if API key is not set (fail-closed)
|
|
102
|
+
*/
|
|
103
|
+
static fromEnv(provider = 'openai', model, options) {
|
|
104
|
+
const apiKey = getAPIKeyFromEnv(provider);
|
|
105
|
+
return new LLMClient({
|
|
106
|
+
provider,
|
|
107
|
+
model: model ?? DEFAULT_CONFIGS[provider].defaultModel,
|
|
108
|
+
apiKey,
|
|
109
|
+
temperature: options?.temperature ?? 0.7,
|
|
110
|
+
maxTokens: options?.maxTokens ?? 1024,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Send chat completion request to LLM
|
|
115
|
+
*/
|
|
116
|
+
async chat(messages) {
|
|
117
|
+
switch (this.config.provider) {
|
|
118
|
+
case 'openai':
|
|
119
|
+
return this.chatOpenAI(messages);
|
|
120
|
+
case 'anthropic':
|
|
121
|
+
return this.chatAnthropic(messages);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* OpenAI API call
|
|
126
|
+
*/
|
|
127
|
+
async chatOpenAI(messages) {
|
|
128
|
+
const response = await fetch(`${this.config.baseUrl}/chat/completions`, {
|
|
129
|
+
method: 'POST',
|
|
130
|
+
headers: {
|
|
131
|
+
'Content-Type': 'application/json',
|
|
132
|
+
'Authorization': `Bearer ${this.config.apiKey}`,
|
|
133
|
+
},
|
|
134
|
+
body: JSON.stringify({
|
|
135
|
+
model: this.config.model,
|
|
136
|
+
messages,
|
|
137
|
+
temperature: this.config.temperature,
|
|
138
|
+
max_tokens: this.config.maxTokens,
|
|
139
|
+
}),
|
|
140
|
+
});
|
|
141
|
+
if (!response.ok) {
|
|
142
|
+
const body = await response.text();
|
|
143
|
+
throw new LLMAPIError('openai', response.status, body);
|
|
144
|
+
}
|
|
145
|
+
const data = await response.json();
|
|
146
|
+
return {
|
|
147
|
+
content: data.choices[0]?.message?.content ?? '',
|
|
148
|
+
model: data.model,
|
|
149
|
+
usage: data.usage,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Anthropic API call
|
|
154
|
+
*/
|
|
155
|
+
async chatAnthropic(messages) {
|
|
156
|
+
// Extract system message if present
|
|
157
|
+
const systemMessage = messages.find(m => m.role === 'system');
|
|
158
|
+
const conversationMessages = messages.filter(m => m.role !== 'system');
|
|
159
|
+
const response = await fetch(`${this.config.baseUrl}/messages`, {
|
|
160
|
+
method: 'POST',
|
|
161
|
+
headers: {
|
|
162
|
+
'Content-Type': 'application/json',
|
|
163
|
+
'x-api-key': this.config.apiKey,
|
|
164
|
+
'anthropic-version': '2023-06-01',
|
|
165
|
+
},
|
|
166
|
+
body: JSON.stringify({
|
|
167
|
+
model: this.config.model,
|
|
168
|
+
max_tokens: this.config.maxTokens,
|
|
169
|
+
temperature: this.config.temperature,
|
|
170
|
+
system: systemMessage?.content,
|
|
171
|
+
messages: conversationMessages.map(m => ({
|
|
172
|
+
role: m.role,
|
|
173
|
+
content: m.content,
|
|
174
|
+
})),
|
|
175
|
+
}),
|
|
176
|
+
});
|
|
177
|
+
if (!response.ok) {
|
|
178
|
+
const body = await response.text();
|
|
179
|
+
throw new LLMAPIError('anthropic', response.status, body);
|
|
180
|
+
}
|
|
181
|
+
const data = await response.json();
|
|
182
|
+
return {
|
|
183
|
+
content: data.content[0]?.text ?? '',
|
|
184
|
+
model: data.model,
|
|
185
|
+
usage: data.usage ? {
|
|
186
|
+
prompt_tokens: data.usage.input_tokens,
|
|
187
|
+
completion_tokens: data.usage.output_tokens,
|
|
188
|
+
total_tokens: data.usage.input_tokens + data.usage.output_tokens,
|
|
189
|
+
} : undefined,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Get the configured model name
|
|
194
|
+
*/
|
|
195
|
+
getModel() {
|
|
196
|
+
return this.config.model;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Get the configured provider
|
|
200
|
+
*/
|
|
201
|
+
getProvider() {
|
|
202
|
+
return this.config.provider;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
exports.LLMClient = LLMClient;
|
|
206
|
+
//# sourceMappingURL=llm-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-client.js","sourceRoot":"","sources":["../../src/mediation/llm-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLM Evidence Manager
|
|
3
|
+
*
|
|
4
|
+
* Tracks and verifies evidence of real LLM API calls.
|
|
5
|
+
* This is the fail-closed mechanism that ensures:
|
|
6
|
+
* 1. Every LLM call generates a proof file
|
|
7
|
+
* 2. COMPLETE status can only be asserted with evidence
|
|
8
|
+
* 3. Evidence files are tamper-resistant (hash verification)
|
|
9
|
+
*
|
|
10
|
+
* ARCHITECTURAL RULES:
|
|
11
|
+
* - No evidence file = LLM call did not happen
|
|
12
|
+
* - Evidence must exist BEFORE asserting COMPLETE
|
|
13
|
+
* - Failed calls are also recorded (to prove attempt)
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* LLM Evidence structure
|
|
17
|
+
* Records proof of a real LLM API call
|
|
18
|
+
*/
|
|
19
|
+
export interface LLMEvidence {
|
|
20
|
+
/** Unique identifier for this call */
|
|
21
|
+
call_id: string;
|
|
22
|
+
/** LLM provider (openai, anthropic) */
|
|
23
|
+
provider: string;
|
|
24
|
+
/** Model used */
|
|
25
|
+
model: string;
|
|
26
|
+
/** Hash of the request payload */
|
|
27
|
+
request_hash: string;
|
|
28
|
+
/** Hash of the response (null if failed) */
|
|
29
|
+
response_hash: string | null;
|
|
30
|
+
/** ISO timestamp of the call */
|
|
31
|
+
timestamp: string;
|
|
32
|
+
/** Duration in milliseconds */
|
|
33
|
+
duration_ms: number;
|
|
34
|
+
/** Whether the call succeeded */
|
|
35
|
+
success: boolean;
|
|
36
|
+
/** Error message if failed */
|
|
37
|
+
error?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Evidence statistics
|
|
41
|
+
*/
|
|
42
|
+
export interface EvidenceStats {
|
|
43
|
+
total_calls: number;
|
|
44
|
+
successful_calls: number;
|
|
45
|
+
failed_calls: number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* LLM Evidence Manager
|
|
49
|
+
*
|
|
50
|
+
* Manages evidence files for LLM API calls.
|
|
51
|
+
* Uses file-based storage for durability and auditability.
|
|
52
|
+
*/
|
|
53
|
+
export declare class LLMEvidenceManager {
|
|
54
|
+
private readonly evidenceDir;
|
|
55
|
+
private readonly evidenceMap;
|
|
56
|
+
constructor(baseDir: string);
|
|
57
|
+
/**
|
|
58
|
+
* Load existing evidence files from disk
|
|
59
|
+
*/
|
|
60
|
+
private loadExistingEvidence;
|
|
61
|
+
/**
|
|
62
|
+
* Record evidence for an LLM call
|
|
63
|
+
* @returns Path to the evidence file
|
|
64
|
+
*/
|
|
65
|
+
recordEvidence(evidence: LLMEvidence): string;
|
|
66
|
+
/**
|
|
67
|
+
* Check if evidence exists for a call
|
|
68
|
+
*/
|
|
69
|
+
hasEvidence(callId: string): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Get evidence by call ID
|
|
72
|
+
*/
|
|
73
|
+
getEvidence(callId: string): LLMEvidence | null;
|
|
74
|
+
/**
|
|
75
|
+
* List all evidence
|
|
76
|
+
*/
|
|
77
|
+
listEvidence(): LLMEvidence[];
|
|
78
|
+
/**
|
|
79
|
+
* Get evidence statistics
|
|
80
|
+
*/
|
|
81
|
+
getStats(): EvidenceStats;
|
|
82
|
+
/**
|
|
83
|
+
* Check if we can assert COMPLETE status
|
|
84
|
+
* Requires at least one successful LLM call with evidence
|
|
85
|
+
*/
|
|
86
|
+
canAssertComplete(): boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Verify integrity of an evidence file
|
|
89
|
+
* Detects tampering by comparing stored hash with recalculated hash
|
|
90
|
+
*/
|
|
91
|
+
verifyIntegrity(callId: string): boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Get evidence directory path
|
|
94
|
+
*/
|
|
95
|
+
getEvidenceDir(): string;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Create hash for request payload
|
|
99
|
+
*/
|
|
100
|
+
export declare function hashRequest(messages: Array<{
|
|
101
|
+
role: string;
|
|
102
|
+
content: string;
|
|
103
|
+
}>): string;
|
|
104
|
+
/**
|
|
105
|
+
* Create hash for response content
|
|
106
|
+
*/
|
|
107
|
+
export declare function hashResponse(content: string): string;
|
|
108
|
+
//# sourceMappingURL=llm-evidence-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-evidence-manager.d.ts","sourceRoot":"","sources":["../../src/mediation/llm-evidence-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,sCAAsC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,uCAAuC;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,iCAAiC;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAUD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;GAKG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAuC;gBAEvD,OAAO,EAAE,MAAM;IAY3B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAqB5B;;;OAGG;IACH,cAAc,CAAC,QAAQ,EAAE,WAAW,GAAG,MAAM;IAqB7C;;OAEG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAWpC;;OAEG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAuB/C;;OAEG;IACH,YAAY,IAAI,WAAW,EAAE;IAM7B;;OAEG;IACH,QAAQ,IAAI,aAAa;IAYzB;;;OAGG;IACH,iBAAiB,IAAI,OAAO;IAY5B;;;OAGG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAqBxC;;OAEG;IACH,cAAc,IAAI,MAAM;CAGzB;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,MAAM,CAGtF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpD"}
|