codebot-ai 1.7.0 → 1.9.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/dist/agent.d.ts +18 -0
- package/dist/agent.js +135 -6
- package/dist/audit.d.ts +1 -1
- package/dist/capabilities.d.ts +48 -0
- package/dist/capabilities.js +187 -0
- package/dist/cli.js +167 -4
- package/dist/history.d.ts +7 -3
- package/dist/history.js +55 -8
- package/dist/index.d.ts +12 -0
- package/dist/index.js +20 -1
- package/dist/integrity.d.ts +35 -0
- package/dist/integrity.js +135 -0
- package/dist/metrics.d.ts +60 -0
- package/dist/metrics.js +296 -0
- package/dist/policy.d.ts +9 -0
- package/dist/policy.js +32 -6
- package/dist/providers/anthropic.d.ts +1 -0
- package/dist/providers/anthropic.js +4 -0
- package/dist/providers/openai.d.ts +1 -0
- package/dist/providers/openai.js +4 -0
- package/dist/replay.d.ts +55 -0
- package/dist/replay.js +196 -0
- package/dist/risk.d.ts +52 -0
- package/dist/risk.js +367 -0
- package/dist/sarif.d.ts +82 -0
- package/dist/sarif.js +176 -0
- package/dist/tools/batch-edit.d.ts +3 -0
- package/dist/tools/batch-edit.js +12 -0
- package/dist/tools/edit.d.ts +3 -0
- package/dist/tools/edit.js +11 -0
- package/dist/tools/git.d.ts +5 -0
- package/dist/tools/git.js +31 -0
- package/dist/tools/index.d.ts +2 -1
- package/dist/tools/index.js +6 -6
- package/dist/tools/write.d.ts +3 -0
- package/dist/tools/write.js +11 -0
- package/dist/types.d.ts +5 -0
- package/package.json +1 -1
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MetricsCollector for CodeBot v1.9.0
|
|
3
|
+
*
|
|
4
|
+
* Structured telemetry: counters + histograms.
|
|
5
|
+
* Persists to ~/.codebot/telemetry/metrics-YYYY-MM-DD.jsonl
|
|
6
|
+
* Optional OTLP HTTP export when OTEL_EXPORTER_OTLP_ENDPOINT is set.
|
|
7
|
+
*
|
|
8
|
+
* Pattern: fail-safe, session-scoped, never throws.
|
|
9
|
+
* Follows TokenTracker conventions from src/telemetry.ts.
|
|
10
|
+
*/
|
|
11
|
+
export interface CounterValue {
|
|
12
|
+
name: string;
|
|
13
|
+
labels: Record<string, string>;
|
|
14
|
+
value: number;
|
|
15
|
+
}
|
|
16
|
+
export interface HistogramValue {
|
|
17
|
+
name: string;
|
|
18
|
+
labels: Record<string, string>;
|
|
19
|
+
count: number;
|
|
20
|
+
sum: number;
|
|
21
|
+
min: number;
|
|
22
|
+
max: number;
|
|
23
|
+
buckets: number[];
|
|
24
|
+
}
|
|
25
|
+
export interface MetricsSnapshot {
|
|
26
|
+
sessionId: string;
|
|
27
|
+
timestamp: string;
|
|
28
|
+
counters: CounterValue[];
|
|
29
|
+
histograms: HistogramValue[];
|
|
30
|
+
}
|
|
31
|
+
export declare class MetricsCollector {
|
|
32
|
+
private sessionId;
|
|
33
|
+
private counters;
|
|
34
|
+
private histograms;
|
|
35
|
+
constructor(sessionId?: string);
|
|
36
|
+
getSessionId(): string;
|
|
37
|
+
/** Increment a counter by delta (default 1) */
|
|
38
|
+
increment(name: string, labels?: Record<string, string>, delta?: number): void;
|
|
39
|
+
/** Record a histogram observation */
|
|
40
|
+
observe(name: string, value: number, labels?: Record<string, string>): void;
|
|
41
|
+
/** Read a counter value */
|
|
42
|
+
getCounter(name: string, labels?: Record<string, string>): number;
|
|
43
|
+
/** Read a histogram summary */
|
|
44
|
+
getHistogram(name: string, labels?: Record<string, string>): HistogramValue | null;
|
|
45
|
+
/** Full session snapshot */
|
|
46
|
+
snapshot(): MetricsSnapshot;
|
|
47
|
+
/** Persist snapshot to ~/.codebot/telemetry/metrics-YYYY-MM-DD.jsonl */
|
|
48
|
+
save(sessionId?: string): void;
|
|
49
|
+
/** Human-readable per-tool breakdown */
|
|
50
|
+
formatSummary(): string;
|
|
51
|
+
/**
|
|
52
|
+
* Export snapshot in OTLP JSON format via HTTP POST.
|
|
53
|
+
* Only fires when OTEL_EXPORTER_OTLP_ENDPOINT is set.
|
|
54
|
+
* Fails silently — never blocks or crashes.
|
|
55
|
+
*/
|
|
56
|
+
exportOtel(snap?: MetricsSnapshot): void;
|
|
57
|
+
/** Build OTLP-compatible JSON payload */
|
|
58
|
+
private buildOtlpPayload;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=metrics.d.ts.map
|
package/dist/metrics.js
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MetricsCollector for CodeBot v1.9.0
|
|
4
|
+
*
|
|
5
|
+
* Structured telemetry: counters + histograms.
|
|
6
|
+
* Persists to ~/.codebot/telemetry/metrics-YYYY-MM-DD.jsonl
|
|
7
|
+
* Optional OTLP HTTP export when OTEL_EXPORTER_OTLP_ENDPOINT is set.
|
|
8
|
+
*
|
|
9
|
+
* Pattern: fail-safe, session-scoped, never throws.
|
|
10
|
+
* Follows TokenTracker conventions from src/telemetry.ts.
|
|
11
|
+
*/
|
|
12
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
15
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
16
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
17
|
+
}
|
|
18
|
+
Object.defineProperty(o, k2, desc);
|
|
19
|
+
}) : (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
o[k2] = m[k];
|
|
22
|
+
}));
|
|
23
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
24
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
25
|
+
}) : function(o, v) {
|
|
26
|
+
o["default"] = v;
|
|
27
|
+
});
|
|
28
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
29
|
+
var ownKeys = function(o) {
|
|
30
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
31
|
+
var ar = [];
|
|
32
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
33
|
+
return ar;
|
|
34
|
+
};
|
|
35
|
+
return ownKeys(o);
|
|
36
|
+
};
|
|
37
|
+
return function (mod) {
|
|
38
|
+
if (mod && mod.__esModule) return mod;
|
|
39
|
+
var result = {};
|
|
40
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
41
|
+
__setModuleDefault(result, mod);
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
})();
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
exports.MetricsCollector = void 0;
|
|
47
|
+
const fs = __importStar(require("fs"));
|
|
48
|
+
const path = __importStar(require("path"));
|
|
49
|
+
const os = __importStar(require("os"));
|
|
50
|
+
const http = __importStar(require("http"));
|
|
51
|
+
const https = __importStar(require("https"));
|
|
52
|
+
// ── Helpers ──
|
|
53
|
+
/** Encode a metric key: name|label1=val1|label2=val2 (sorted labels) */
|
|
54
|
+
function encodeKey(name, labels) {
|
|
55
|
+
if (!labels || Object.keys(labels).length === 0)
|
|
56
|
+
return name;
|
|
57
|
+
const sorted = Object.entries(labels)
|
|
58
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
59
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
60
|
+
.join('|');
|
|
61
|
+
return `${name}|${sorted}`;
|
|
62
|
+
}
|
|
63
|
+
/** Decode a metric key back to name + labels */
|
|
64
|
+
function decodeKey(key) {
|
|
65
|
+
const parts = key.split('|');
|
|
66
|
+
const name = parts[0];
|
|
67
|
+
const labels = {};
|
|
68
|
+
for (let i = 1; i < parts.length; i++) {
|
|
69
|
+
const eq = parts[i].indexOf('=');
|
|
70
|
+
if (eq > 0) {
|
|
71
|
+
labels[parts[i].substring(0, eq)] = parts[i].substring(eq + 1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return { name, labels };
|
|
75
|
+
}
|
|
76
|
+
/** Compute percentile from sorted array */
|
|
77
|
+
function percentile(sorted, p) {
|
|
78
|
+
if (sorted.length === 0)
|
|
79
|
+
return 0;
|
|
80
|
+
const idx = Math.ceil((p / 100) * sorted.length) - 1;
|
|
81
|
+
return sorted[Math.max(0, idx)];
|
|
82
|
+
}
|
|
83
|
+
// ── Collector ──
|
|
84
|
+
const MAX_BUCKETS = 1000; // cap stored observations to prevent memory bloat
|
|
85
|
+
class MetricsCollector {
|
|
86
|
+
sessionId;
|
|
87
|
+
counters = new Map();
|
|
88
|
+
histograms = new Map();
|
|
89
|
+
constructor(sessionId) {
|
|
90
|
+
this.sessionId = sessionId || `${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
|
|
91
|
+
}
|
|
92
|
+
getSessionId() {
|
|
93
|
+
return this.sessionId;
|
|
94
|
+
}
|
|
95
|
+
/** Increment a counter by delta (default 1) */
|
|
96
|
+
increment(name, labels, delta = 1) {
|
|
97
|
+
const key = encodeKey(name, labels);
|
|
98
|
+
this.counters.set(key, (this.counters.get(key) || 0) + delta);
|
|
99
|
+
}
|
|
100
|
+
/** Record a histogram observation */
|
|
101
|
+
observe(name, value, labels) {
|
|
102
|
+
const key = encodeKey(name, labels);
|
|
103
|
+
const existing = this.histograms.get(key);
|
|
104
|
+
if (existing) {
|
|
105
|
+
existing.count++;
|
|
106
|
+
existing.sum += value;
|
|
107
|
+
existing.min = Math.min(existing.min, value);
|
|
108
|
+
existing.max = Math.max(existing.max, value);
|
|
109
|
+
if (existing.buckets.length < MAX_BUCKETS) {
|
|
110
|
+
existing.buckets.push(value);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
this.histograms.set(key, {
|
|
115
|
+
count: 1,
|
|
116
|
+
sum: value,
|
|
117
|
+
min: value,
|
|
118
|
+
max: value,
|
|
119
|
+
buckets: [value],
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/** Read a counter value */
|
|
124
|
+
getCounter(name, labels) {
|
|
125
|
+
return this.counters.get(encodeKey(name, labels)) || 0;
|
|
126
|
+
}
|
|
127
|
+
/** Read a histogram summary */
|
|
128
|
+
getHistogram(name, labels) {
|
|
129
|
+
const key = encodeKey(name, labels);
|
|
130
|
+
const h = this.histograms.get(key);
|
|
131
|
+
if (!h)
|
|
132
|
+
return null;
|
|
133
|
+
const { name: n, labels: l } = decodeKey(key);
|
|
134
|
+
return { name: n, labels: l, ...h };
|
|
135
|
+
}
|
|
136
|
+
/** Full session snapshot */
|
|
137
|
+
snapshot() {
|
|
138
|
+
const counters = [];
|
|
139
|
+
for (const [key, value] of this.counters) {
|
|
140
|
+
const { name, labels } = decodeKey(key);
|
|
141
|
+
counters.push({ name, labels, value });
|
|
142
|
+
}
|
|
143
|
+
const histograms = [];
|
|
144
|
+
for (const [key, h] of this.histograms) {
|
|
145
|
+
const { name, labels } = decodeKey(key);
|
|
146
|
+
histograms.push({ name, labels, ...h });
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
sessionId: this.sessionId,
|
|
150
|
+
timestamp: new Date().toISOString(),
|
|
151
|
+
counters,
|
|
152
|
+
histograms,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/** Persist snapshot to ~/.codebot/telemetry/metrics-YYYY-MM-DD.jsonl */
|
|
156
|
+
save(sessionId) {
|
|
157
|
+
try {
|
|
158
|
+
const telemetryDir = path.join(os.homedir(), '.codebot', 'telemetry');
|
|
159
|
+
fs.mkdirSync(telemetryDir, { recursive: true });
|
|
160
|
+
const snap = this.snapshot();
|
|
161
|
+
if (sessionId)
|
|
162
|
+
snap.sessionId = sessionId;
|
|
163
|
+
const date = new Date().toISOString().split('T')[0];
|
|
164
|
+
const filePath = path.join(telemetryDir, `metrics-${date}.jsonl`);
|
|
165
|
+
fs.appendFileSync(filePath, JSON.stringify(snap) + '\n', 'utf-8');
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
// Telemetry failures are non-fatal
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/** Human-readable per-tool breakdown */
|
|
172
|
+
formatSummary() {
|
|
173
|
+
const lines = ['Metrics Summary', '─'.repeat(50)];
|
|
174
|
+
// Counters
|
|
175
|
+
const counterEntries = Array.from(this.counters.entries())
|
|
176
|
+
.sort(([a], [b]) => a.localeCompare(b));
|
|
177
|
+
if (counterEntries.length > 0) {
|
|
178
|
+
lines.push('Counters:');
|
|
179
|
+
for (const [key, value] of counterEntries) {
|
|
180
|
+
const { name, labels } = decodeKey(key);
|
|
181
|
+
const labelStr = Object.keys(labels).length > 0
|
|
182
|
+
? ` {${Object.entries(labels).map(([k, v]) => `${k}="${v}"`).join(', ')}}`
|
|
183
|
+
: '';
|
|
184
|
+
lines.push(` ${name}${labelStr}: ${value}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// Histograms — per-tool breakdown
|
|
188
|
+
const histEntries = Array.from(this.histograms.entries())
|
|
189
|
+
.sort(([a], [b]) => a.localeCompare(b));
|
|
190
|
+
if (histEntries.length > 0) {
|
|
191
|
+
lines.push('Histograms:');
|
|
192
|
+
for (const [key, h] of histEntries) {
|
|
193
|
+
const { name, labels } = decodeKey(key);
|
|
194
|
+
const labelStr = Object.keys(labels).length > 0
|
|
195
|
+
? ` {${Object.entries(labels).map(([k, v]) => `${k}="${v}"`).join(', ')}}`
|
|
196
|
+
: '';
|
|
197
|
+
const sorted = [...h.buckets].sort((a, b) => a - b);
|
|
198
|
+
const avg = h.count > 0 ? (h.sum / h.count).toFixed(3) : '0';
|
|
199
|
+
const p50 = percentile(sorted, 50).toFixed(3);
|
|
200
|
+
const p95 = percentile(sorted, 95).toFixed(3);
|
|
201
|
+
const p99 = percentile(sorted, 99).toFixed(3);
|
|
202
|
+
lines.push(` ${name}${labelStr}: count=${h.count} avg=${avg} p50=${p50} p95=${p95} p99=${p99} min=${h.min.toFixed(3)} max=${h.max.toFixed(3)}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (counterEntries.length === 0 && histEntries.length === 0) {
|
|
206
|
+
lines.push(' (no metrics recorded)');
|
|
207
|
+
}
|
|
208
|
+
return lines.join('\n');
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Export snapshot in OTLP JSON format via HTTP POST.
|
|
212
|
+
* Only fires when OTEL_EXPORTER_OTLP_ENDPOINT is set.
|
|
213
|
+
* Fails silently — never blocks or crashes.
|
|
214
|
+
*/
|
|
215
|
+
exportOtel(snap) {
|
|
216
|
+
try {
|
|
217
|
+
const endpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
|
|
218
|
+
if (!endpoint)
|
|
219
|
+
return;
|
|
220
|
+
const data = snap || this.snapshot();
|
|
221
|
+
const payload = this.buildOtlpPayload(data);
|
|
222
|
+
const url = new URL('/v1/metrics', endpoint);
|
|
223
|
+
const body = JSON.stringify(payload);
|
|
224
|
+
const mod = url.protocol === 'https:' ? https : http;
|
|
225
|
+
const req = mod.request(url, {
|
|
226
|
+
method: 'POST',
|
|
227
|
+
headers: {
|
|
228
|
+
'Content-Type': 'application/json',
|
|
229
|
+
'Content-Length': Buffer.byteLength(body),
|
|
230
|
+
},
|
|
231
|
+
timeout: 5000,
|
|
232
|
+
});
|
|
233
|
+
req.on('error', () => { });
|
|
234
|
+
req.on('timeout', () => req.destroy());
|
|
235
|
+
req.write(body);
|
|
236
|
+
req.end();
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
// OTLP export failures are non-fatal
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/** Build OTLP-compatible JSON payload */
|
|
243
|
+
buildOtlpPayload(snap) {
|
|
244
|
+
const metrics = [];
|
|
245
|
+
for (const counter of snap.counters) {
|
|
246
|
+
metrics.push({
|
|
247
|
+
name: counter.name,
|
|
248
|
+
sum: {
|
|
249
|
+
dataPoints: [{
|
|
250
|
+
asInt: counter.value,
|
|
251
|
+
attributes: Object.entries(counter.labels).map(([k, v]) => ({
|
|
252
|
+
key: k, value: { stringValue: v },
|
|
253
|
+
})),
|
|
254
|
+
timeUnixNano: Date.now() * 1_000_000,
|
|
255
|
+
}],
|
|
256
|
+
isMonotonic: true,
|
|
257
|
+
aggregationTemporality: 2, // CUMULATIVE
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
for (const hist of snap.histograms) {
|
|
262
|
+
metrics.push({
|
|
263
|
+
name: hist.name,
|
|
264
|
+
histogram: {
|
|
265
|
+
dataPoints: [{
|
|
266
|
+
count: hist.count,
|
|
267
|
+
sum: hist.sum,
|
|
268
|
+
min: hist.min,
|
|
269
|
+
max: hist.max,
|
|
270
|
+
attributes: Object.entries(hist.labels).map(([k, v]) => ({
|
|
271
|
+
key: k, value: { stringValue: v },
|
|
272
|
+
})),
|
|
273
|
+
timeUnixNano: Date.now() * 1_000_000,
|
|
274
|
+
}],
|
|
275
|
+
aggregationTemporality: 2,
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
resourceMetrics: [{
|
|
281
|
+
resource: {
|
|
282
|
+
attributes: [
|
|
283
|
+
{ key: 'service.name', value: { stringValue: 'codebot' } },
|
|
284
|
+
{ key: 'session.id', value: { stringValue: snap.sessionId } },
|
|
285
|
+
],
|
|
286
|
+
},
|
|
287
|
+
scopeMetrics: [{
|
|
288
|
+
scope: { name: 'codebot-metrics', version: '1.9.0' },
|
|
289
|
+
metrics,
|
|
290
|
+
}],
|
|
291
|
+
}],
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
exports.MetricsCollector = MetricsCollector;
|
|
296
|
+
//# sourceMappingURL=metrics.js.map
|
package/dist/policy.d.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Policy files: .codebot/policy.json (project) + ~/.codebot/policy.json (global)
|
|
6
6
|
* Project policy overrides global policy where specified.
|
|
7
7
|
*/
|
|
8
|
+
import { ToolCapabilities } from './capabilities';
|
|
8
9
|
export interface PolicyExecution {
|
|
9
10
|
sandbox?: 'docker' | 'host' | 'auto';
|
|
10
11
|
network?: boolean;
|
|
@@ -24,6 +25,7 @@ export interface PolicyTools {
|
|
|
24
25
|
enabled?: string[];
|
|
25
26
|
disabled?: string[];
|
|
26
27
|
permissions?: PolicyToolPermission;
|
|
28
|
+
capabilities?: Record<string, ToolCapabilities>;
|
|
27
29
|
}
|
|
28
30
|
export interface PolicySecrets {
|
|
29
31
|
block_on_detect?: boolean;
|
|
@@ -108,6 +110,13 @@ export declare class PolicyEnforcer {
|
|
|
108
110
|
getMaxFileSizeBytes(): number;
|
|
109
111
|
/** Get cost limit in USD (0 = no limit). */
|
|
110
112
|
getCostLimitUsd(): number;
|
|
113
|
+
/** Get capability restrictions for a tool. undefined = unrestricted. */
|
|
114
|
+
getToolCapabilities(toolName: string): ToolCapabilities | undefined;
|
|
115
|
+
/** Check a specific capability for a tool. Returns { allowed, reason }. */
|
|
116
|
+
checkCapability(toolName: string, capabilityType: keyof ToolCapabilities, value: string | number): {
|
|
117
|
+
allowed: boolean;
|
|
118
|
+
reason?: string;
|
|
119
|
+
};
|
|
111
120
|
/**
|
|
112
121
|
* Simple glob-like pattern matching:
|
|
113
122
|
* - `*` matches any single path component
|
package/dist/policy.js
CHANGED
|
@@ -46,12 +46,13 @@ exports.generateDefaultPolicyFile = generateDefaultPolicyFile;
|
|
|
46
46
|
const fs = __importStar(require("fs"));
|
|
47
47
|
const path = __importStar(require("path"));
|
|
48
48
|
const os = __importStar(require("os"));
|
|
49
|
+
const capabilities_1 = require("./capabilities");
|
|
49
50
|
// ── Default Policy ──
|
|
50
51
|
exports.DEFAULT_POLICY = {
|
|
51
52
|
version: '1.0',
|
|
52
53
|
execution: {
|
|
53
54
|
sandbox: 'auto',
|
|
54
|
-
network:
|
|
55
|
+
network: false, // safe default: no network in sandbox
|
|
55
56
|
timeout_seconds: 120,
|
|
56
57
|
max_memory_mb: 512,
|
|
57
58
|
},
|
|
@@ -67,12 +68,12 @@ exports.DEFAULT_POLICY = {
|
|
|
67
68
|
permissions: {},
|
|
68
69
|
},
|
|
69
70
|
secrets: {
|
|
70
|
-
block_on_detect:
|
|
71
|
+
block_on_detect: true, // safe default: block writes containing secrets
|
|
71
72
|
scan_on_write: true,
|
|
72
73
|
allowed_patterns: [],
|
|
73
74
|
},
|
|
74
75
|
git: {
|
|
75
|
-
always_branch:
|
|
76
|
+
always_branch: true, // safe default: auto-branch on first write
|
|
76
77
|
branch_prefix: 'codebot/',
|
|
77
78
|
require_tests_before_commit: false,
|
|
78
79
|
never_push_main: true,
|
|
@@ -327,6 +328,19 @@ class PolicyEnforcer {
|
|
|
327
328
|
getCostLimitUsd() {
|
|
328
329
|
return this.policy.limits?.cost_limit_usd || 0;
|
|
329
330
|
}
|
|
331
|
+
// ── Capabilities (v1.8.0) ──
|
|
332
|
+
/** Get capability restrictions for a tool. undefined = unrestricted. */
|
|
333
|
+
getToolCapabilities(toolName) {
|
|
334
|
+
return this.policy.tools?.capabilities?.[toolName];
|
|
335
|
+
}
|
|
336
|
+
/** Check a specific capability for a tool. Returns { allowed, reason }. */
|
|
337
|
+
checkCapability(toolName, capabilityType, value) {
|
|
338
|
+
const caps = this.policy.tools?.capabilities;
|
|
339
|
+
if (!caps)
|
|
340
|
+
return { allowed: true };
|
|
341
|
+
const checker = new capabilities_1.CapabilityChecker(caps, this.projectRoot);
|
|
342
|
+
return checker.checkCapability(toolName, capabilityType, value);
|
|
343
|
+
}
|
|
330
344
|
// ── Helpers ──
|
|
331
345
|
/**
|
|
332
346
|
* Simple glob-like pattern matching:
|
|
@@ -374,7 +388,7 @@ function generateDefaultPolicyFile() {
|
|
|
374
388
|
version: '1.0',
|
|
375
389
|
execution: {
|
|
376
390
|
sandbox: 'auto',
|
|
377
|
-
network:
|
|
391
|
+
network: false,
|
|
378
392
|
timeout_seconds: 120,
|
|
379
393
|
max_memory_mb: 512,
|
|
380
394
|
},
|
|
@@ -392,13 +406,25 @@ function generateDefaultPolicyFile() {
|
|
|
392
406
|
write_file: 'prompt',
|
|
393
407
|
edit_file: 'prompt',
|
|
394
408
|
},
|
|
409
|
+
capabilities: {
|
|
410
|
+
execute: {
|
|
411
|
+
shell_commands: [
|
|
412
|
+
'npm', 'npx', 'node', 'git', 'tsc', 'eslint', 'prettier',
|
|
413
|
+
'jest', 'vitest', 'pytest', 'make', 'cargo', 'go', 'python',
|
|
414
|
+
'python3', 'ruby', 'php', 'java', 'javac', 'mvn', 'gradle',
|
|
415
|
+
'docker', 'ls', 'cat', 'head', 'tail', 'wc', 'sort', 'uniq',
|
|
416
|
+
'find', 'which', 'echo', 'pwd', 'env', 'date',
|
|
417
|
+
],
|
|
418
|
+
max_output_kb: 500,
|
|
419
|
+
},
|
|
420
|
+
},
|
|
395
421
|
},
|
|
396
422
|
secrets: {
|
|
397
|
-
block_on_detect:
|
|
423
|
+
block_on_detect: true,
|
|
398
424
|
scan_on_write: true,
|
|
399
425
|
},
|
|
400
426
|
git: {
|
|
401
|
-
always_branch:
|
|
427
|
+
always_branch: true,
|
|
402
428
|
branch_prefix: 'codebot/',
|
|
403
429
|
require_tests_before_commit: false,
|
|
404
430
|
never_push_main: true,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { LLMProvider, Message, ToolSchema, StreamEvent, ProviderConfig } from '../types';
|
|
2
2
|
export declare class AnthropicProvider implements LLMProvider {
|
|
3
3
|
name: string;
|
|
4
|
+
temperature?: number;
|
|
4
5
|
private config;
|
|
5
6
|
constructor(config: ProviderConfig);
|
|
6
7
|
chat(messages: Message[], tools?: ToolSchema[]): AsyncGenerator<StreamEvent>;
|
|
@@ -4,6 +4,7 @@ exports.AnthropicProvider = void 0;
|
|
|
4
4
|
const retry_1 = require("../retry");
|
|
5
5
|
class AnthropicProvider {
|
|
6
6
|
name;
|
|
7
|
+
temperature;
|
|
7
8
|
config;
|
|
8
9
|
constructor(config) {
|
|
9
10
|
this.config = config;
|
|
@@ -22,6 +23,9 @@ class AnthropicProvider {
|
|
|
22
23
|
max_tokens: 8192,
|
|
23
24
|
stream: true,
|
|
24
25
|
};
|
|
26
|
+
if (this.temperature !== undefined) {
|
|
27
|
+
body.temperature = this.temperature;
|
|
28
|
+
}
|
|
25
29
|
if (systemPrompt) {
|
|
26
30
|
body.system = systemPrompt;
|
|
27
31
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { LLMProvider, Message, ToolSchema, StreamEvent, ProviderConfig } from '../types';
|
|
2
2
|
export declare class OpenAIProvider implements LLMProvider {
|
|
3
3
|
name: string;
|
|
4
|
+
temperature?: number;
|
|
4
5
|
private config;
|
|
5
6
|
private supportsTools;
|
|
6
7
|
constructor(config: ProviderConfig);
|
package/dist/providers/openai.js
CHANGED
|
@@ -5,6 +5,7 @@ const registry_1 = require("./registry");
|
|
|
5
5
|
const retry_1 = require("../retry");
|
|
6
6
|
class OpenAIProvider {
|
|
7
7
|
name;
|
|
8
|
+
temperature;
|
|
8
9
|
config;
|
|
9
10
|
supportsTools;
|
|
10
11
|
constructor(config) {
|
|
@@ -25,6 +26,9 @@ class OpenAIProvider {
|
|
|
25
26
|
messages: messages.map(m => this.formatMessage(m)),
|
|
26
27
|
stream: true,
|
|
27
28
|
};
|
|
29
|
+
if (this.temperature !== undefined) {
|
|
30
|
+
body.temperature = this.temperature;
|
|
31
|
+
}
|
|
28
32
|
if (tools?.length && this.supportsTools) {
|
|
29
33
|
body.tools = tools;
|
|
30
34
|
}
|
package/dist/replay.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Replay Engine for CodeBot v1.8.0
|
|
3
|
+
*
|
|
4
|
+
* Replays saved sessions by feeding recorded assistant responses
|
|
5
|
+
* instead of calling the LLM. Tool calls are re-executed and outputs
|
|
6
|
+
* compared against recorded results to detect environment divergences.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* codebot --replay <session-id>
|
|
10
|
+
* codebot --replay (replays latest session)
|
|
11
|
+
*/
|
|
12
|
+
import { Message, LLMProvider, ToolSchema, StreamEvent } from './types';
|
|
13
|
+
/**
|
|
14
|
+
* Mock LLM provider that feeds recorded assistant messages.
|
|
15
|
+
* Used during replay to bypass actual LLM calls.
|
|
16
|
+
*/
|
|
17
|
+
export declare class ReplayProvider implements LLMProvider {
|
|
18
|
+
name: string;
|
|
19
|
+
private assistantMessages;
|
|
20
|
+
private callIndex;
|
|
21
|
+
constructor(assistantMessages: Message[]);
|
|
22
|
+
chat(_messages: Message[], _tools?: ToolSchema[]): AsyncGenerator<StreamEvent>;
|
|
23
|
+
}
|
|
24
|
+
export interface SessionReplayData {
|
|
25
|
+
messages: Message[];
|
|
26
|
+
assistantMessages: Message[];
|
|
27
|
+
userMessages: Message[];
|
|
28
|
+
toolResults: Map<string, string>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Load a session from disk and prepare it for replay.
|
|
32
|
+
* Returns null if session doesn't exist or is empty.
|
|
33
|
+
*/
|
|
34
|
+
export declare function loadSessionForReplay(sessionId: string): SessionReplayData | null;
|
|
35
|
+
export interface ReplayDivergence {
|
|
36
|
+
toolCallId: string;
|
|
37
|
+
toolName: string;
|
|
38
|
+
type: 'output_mismatch';
|
|
39
|
+
diff: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Compare recorded vs actual tool output.
|
|
43
|
+
* Returns null if identical, or a diff description.
|
|
44
|
+
*/
|
|
45
|
+
export declare function compareOutputs(recorded: string, actual: string): string | null;
|
|
46
|
+
/**
|
|
47
|
+
* List sessions available for replay.
|
|
48
|
+
*/
|
|
49
|
+
export declare function listReplayableSessions(limit?: number): Array<{
|
|
50
|
+
id: string;
|
|
51
|
+
preview: string;
|
|
52
|
+
messageCount: number;
|
|
53
|
+
date: string;
|
|
54
|
+
}>;
|
|
55
|
+
//# sourceMappingURL=replay.d.ts.map
|