sentinelayer-cli 0.39.2 → 0.40.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/package.json +1 -1
- package/src/agents/ai-governance/tools/base.js +19 -166
- package/src/agents/backend/tools/base.js +33 -186
- package/src/agents/code-quality/tools/base.js +27 -154
- package/src/agents/data-layer/tools/base.js +35 -180
- package/src/agents/documentation/tools/base.js +18 -146
- package/src/agents/infrastructure/tools/base.js +17 -164
- package/src/agents/observability/tools/base.js +35 -180
- package/src/agents/performance/tools/base.js +35 -205
- package/src/agents/release/tools/base.js +35 -180
- package/src/agents/reliability/tools/base.js +35 -180
- package/src/agents/security/tools/base.js +33 -187
- package/src/agents/shared-tools/domain-base.js +246 -0
- package/src/agents/supply-chain/tools/base.js +15 -144
- package/src/agents/testing/tools/base.js +33 -184
- package/src/cli.js +5 -0
- package/src/commands/mcp.js +1 -1
- package/src/commands/ring-owner.js +58 -0
- package/src/commands/session.js +92 -31
- package/src/legacy-cli.js +2 -2
- package/src/mcp/smoke.js +9 -8
- package/src/pocket/ring-owner.js +97 -0
- package/src/review/local-review.js +9 -5
- package/src/review/omargate-orchestrator.js +111 -14
- package/src/review/spec-binding.js +16 -1
- package/src/scan/generator.js +1 -1
- package/src/session/checkpoints.js +194 -0
|
@@ -3,6 +3,7 @@ import process from "node:process";
|
|
|
3
3
|
|
|
4
4
|
import { SentinelayerApiError, requestJson, requestJsonMutation } from "../auth/http.js";
|
|
5
5
|
import { resolveActiveAuthSession } from "../auth/service.js";
|
|
6
|
+
import { pollSessionEventsBefore } from "./sync.js";
|
|
6
7
|
|
|
7
8
|
const DEFAULT_API_BASE_URL = "https://api.sentinelayer.com";
|
|
8
9
|
const DEFAULT_CHECKPOINT_LIMIT = 100;
|
|
@@ -11,6 +12,10 @@ const DEFAULT_MIN_EVENTS = 20;
|
|
|
11
12
|
const DEFAULT_MAX_EVENTS = 80;
|
|
12
13
|
const DEFAULT_BATCH_MAX_CHECKPOINTS = 5;
|
|
13
14
|
const MAX_BATCH_MAX_CHECKPOINTS = 50;
|
|
15
|
+
const DEFAULT_RESTORE_CONTEXT_EVENTS = 3;
|
|
16
|
+
const DEFAULT_RESTORE_MAX_EVENTS = 120;
|
|
17
|
+
const MAX_RESTORE_CONTEXT_EVENTS = 50;
|
|
18
|
+
const MAX_RESTORE_EVENTS = 200;
|
|
14
19
|
const DEFAULT_CREATED_BY_AGENT_ID = "senti";
|
|
15
20
|
|
|
16
21
|
function normalizeString(value) {
|
|
@@ -33,6 +38,17 @@ function parsePositiveInteger(value, field, fallbackValue = null) {
|
|
|
33
38
|
return Math.floor(parsed);
|
|
34
39
|
}
|
|
35
40
|
|
|
41
|
+
function parseNonNegativeInteger(value, field, fallbackValue = 0) {
|
|
42
|
+
if (value === undefined || value === null || normalizeString(value) === "") {
|
|
43
|
+
return Math.max(0, Math.floor(Number(fallbackValue) || 0));
|
|
44
|
+
}
|
|
45
|
+
const parsed = Number(value);
|
|
46
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
47
|
+
throw new Error(`${field} must be a non-negative integer.`);
|
|
48
|
+
}
|
|
49
|
+
return Math.floor(parsed);
|
|
50
|
+
}
|
|
51
|
+
|
|
36
52
|
function normalizeLimit(value) {
|
|
37
53
|
const parsed = parsePositiveInteger(value, "limit", DEFAULT_CHECKPOINT_LIMIT);
|
|
38
54
|
return Math.max(1, Math.min(MAX_CHECKPOINT_LIMIT, parsed));
|
|
@@ -47,6 +63,16 @@ function normalizeBatchMaxCheckpoints(value) {
|
|
|
47
63
|
return Math.max(1, Math.min(MAX_BATCH_MAX_CHECKPOINTS, parsed));
|
|
48
64
|
}
|
|
49
65
|
|
|
66
|
+
function normalizeRestoreMaxEvents(value) {
|
|
67
|
+
const parsed = parsePositiveInteger(value, "max-events", DEFAULT_RESTORE_MAX_EVENTS);
|
|
68
|
+
return Math.max(1, Math.min(MAX_RESTORE_EVENTS, parsed));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function normalizeRestoreContextEvents(value) {
|
|
72
|
+
const parsed = parseNonNegativeInteger(value, "context-events", DEFAULT_RESTORE_CONTEXT_EVENTS);
|
|
73
|
+
return Math.max(0, Math.min(MAX_RESTORE_CONTEXT_EVENTS, parsed));
|
|
74
|
+
}
|
|
75
|
+
|
|
50
76
|
function stableHash(value) {
|
|
51
77
|
return crypto
|
|
52
78
|
.createHash("sha256")
|
|
@@ -148,6 +174,20 @@ function normalizeTokenRange({ tokenStart, tokenEnd } = {}) {
|
|
|
148
174
|
return { start, end };
|
|
149
175
|
}
|
|
150
176
|
|
|
177
|
+
function checkpointIdOf(checkpoint = {}) {
|
|
178
|
+
return normalizeString(checkpoint.checkpointId || checkpoint.checkpoint_id);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function checkpointSequenceValue(checkpoint = {}, camelKey, snakeKey) {
|
|
182
|
+
const parsed = Number(checkpoint[camelKey] ?? checkpoint[snakeKey]);
|
|
183
|
+
return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : 0;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function eventSequenceValue(event = {}) {
|
|
187
|
+
const parsed = Number(event.sequenceId ?? event.sequence_id ?? event.sequence);
|
|
188
|
+
return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : 0;
|
|
189
|
+
}
|
|
190
|
+
|
|
151
191
|
export function buildManualCheckpointPayload(sessionId, {
|
|
152
192
|
checkpointId = "",
|
|
153
193
|
startSequence,
|
|
@@ -235,6 +275,99 @@ export function buildGenerateCheckpointPayload(sessionId, {
|
|
|
235
275
|
};
|
|
236
276
|
}
|
|
237
277
|
|
|
278
|
+
export function findCheckpointById(checkpoints = [], checkpointId = "") {
|
|
279
|
+
const normalizedCheckpointId = normalizeString(checkpointId);
|
|
280
|
+
if (!normalizedCheckpointId) return null;
|
|
281
|
+
return Array.isArray(checkpoints)
|
|
282
|
+
? checkpoints.find((checkpoint) => checkpointIdOf(checkpoint) === normalizedCheckpointId) || null
|
|
283
|
+
: null;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export function buildCheckpointRestoreWindow(checkpoint = {}, {
|
|
287
|
+
contextEvents = DEFAULT_RESTORE_CONTEXT_EVENTS,
|
|
288
|
+
maxEvents = DEFAULT_RESTORE_MAX_EVENTS,
|
|
289
|
+
} = {}) {
|
|
290
|
+
const startSequence = checkpointSequenceValue(checkpoint, "startSequence", "start_sequence");
|
|
291
|
+
const endSequence = checkpointSequenceValue(checkpoint, "endSequence", "end_sequence");
|
|
292
|
+
if (startSequence <= 0) {
|
|
293
|
+
throw new Error("checkpoint startSequence is required for restore.");
|
|
294
|
+
}
|
|
295
|
+
if (endSequence <= 0) {
|
|
296
|
+
throw new Error("checkpoint endSequence is required for restore.");
|
|
297
|
+
}
|
|
298
|
+
if (startSequence > endSequence) {
|
|
299
|
+
throw new Error("checkpoint startSequence must be less than or equal to endSequence.");
|
|
300
|
+
}
|
|
301
|
+
const normalizedContext = normalizeRestoreContextEvents(contextEvents);
|
|
302
|
+
const normalizedMaxEvents = normalizeRestoreMaxEvents(maxEvents);
|
|
303
|
+
const sourceEventCountExpected = endSequence - startSequence + 1;
|
|
304
|
+
const requestedEventCount = sourceEventCountExpected + normalizedContext * 2;
|
|
305
|
+
const limit = Math.min(normalizedMaxEvents, requestedEventCount, MAX_RESTORE_EVENTS);
|
|
306
|
+
return {
|
|
307
|
+
checkpointId: checkpointIdOf(checkpoint) || null,
|
|
308
|
+
startSequence,
|
|
309
|
+
endSequence,
|
|
310
|
+
contextEvents: normalizedContext,
|
|
311
|
+
maxEvents: normalizedMaxEvents,
|
|
312
|
+
limit,
|
|
313
|
+
beforeSequence: endSequence + normalizedContext + 1,
|
|
314
|
+
lowerBoundSequence: Math.max(1, startSequence - normalizedContext),
|
|
315
|
+
sourceEventCountExpected,
|
|
316
|
+
truncatedByLimit: limit < requestedEventCount,
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function classifyCheckpointRestoreEvents(events = [], window = {}) {
|
|
321
|
+
const normalizedEvents = Array.isArray(events) ? events : [];
|
|
322
|
+
const startSequence = Number(window.startSequence || 0);
|
|
323
|
+
const endSequence = Number(window.endSequence || 0);
|
|
324
|
+
const sourceEventCountExpected = Number(window.sourceEventCountExpected || 0);
|
|
325
|
+
const beforeContextEvents = [];
|
|
326
|
+
const sourceEvents = [];
|
|
327
|
+
const afterContextEvents = [];
|
|
328
|
+
const sourceSequenceIds = new Set();
|
|
329
|
+
|
|
330
|
+
for (const event of normalizedEvents) {
|
|
331
|
+
const sequence = eventSequenceValue(event);
|
|
332
|
+
if (sequence <= 0) continue;
|
|
333
|
+
if (sequence < startSequence) {
|
|
334
|
+
beforeContextEvents.push(event);
|
|
335
|
+
} else if (sequence > endSequence) {
|
|
336
|
+
afterContextEvents.push(event);
|
|
337
|
+
} else {
|
|
338
|
+
sourceEvents.push(event);
|
|
339
|
+
sourceSequenceIds.add(sequence);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const missingStart = startSequence > 0 && !sourceSequenceIds.has(startSequence);
|
|
344
|
+
const missingEnd = endSequence > 0 && !sourceSequenceIds.has(endSequence);
|
|
345
|
+
const missingSourceEvents = Math.max(0, sourceEventCountExpected - sourceSequenceIds.size);
|
|
346
|
+
return {
|
|
347
|
+
events: normalizedEvents,
|
|
348
|
+
beforeContextEvents,
|
|
349
|
+
sourceEvents,
|
|
350
|
+
afterContextEvents,
|
|
351
|
+
eventCount: normalizedEvents.length,
|
|
352
|
+
beforeContextCount: beforeContextEvents.length,
|
|
353
|
+
sourceEventCount: sourceEvents.length,
|
|
354
|
+
afterContextCount: afterContextEvents.length,
|
|
355
|
+
missingStart,
|
|
356
|
+
missingEnd,
|
|
357
|
+
missingSourceEvents,
|
|
358
|
+
completeSourceRange:
|
|
359
|
+
sourceEventCountExpected > 0 &&
|
|
360
|
+
!missingStart &&
|
|
361
|
+
!missingEnd &&
|
|
362
|
+
missingSourceEvents === 0,
|
|
363
|
+
partial:
|
|
364
|
+
Boolean(window.truncatedByLimit) ||
|
|
365
|
+
missingStart ||
|
|
366
|
+
missingEnd ||
|
|
367
|
+
missingSourceEvents > 0,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
238
371
|
export function normalizeCheckpointGenerationResult(payload = {}) {
|
|
239
372
|
const source = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
240
373
|
const checkpoint =
|
|
@@ -308,6 +441,63 @@ export async function listSessionCheckpoints(sessionId, {
|
|
|
308
441
|
};
|
|
309
442
|
}
|
|
310
443
|
|
|
444
|
+
export async function showSessionCheckpoint(sessionId, checkpointId, {
|
|
445
|
+
targetPath = process.cwd(),
|
|
446
|
+
contextEvents = DEFAULT_RESTORE_CONTEXT_EVENTS,
|
|
447
|
+
maxEvents = DEFAULT_RESTORE_MAX_EVENTS,
|
|
448
|
+
resolveAuthSession = resolveActiveAuthSession,
|
|
449
|
+
request = requestJson,
|
|
450
|
+
fetchEventsBefore = pollSessionEventsBefore,
|
|
451
|
+
} = {}) {
|
|
452
|
+
const normalizedSessionId = normalizeString(sessionId);
|
|
453
|
+
if (!normalizedSessionId) {
|
|
454
|
+
throw new Error("session id is required.");
|
|
455
|
+
}
|
|
456
|
+
const normalizedCheckpointId = normalizeString(checkpointId);
|
|
457
|
+
if (!normalizedCheckpointId) {
|
|
458
|
+
throw new Error("checkpoint id is required.");
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const listed = await listSessionCheckpoints(normalizedSessionId, {
|
|
462
|
+
targetPath,
|
|
463
|
+
limit: MAX_CHECKPOINT_LIMIT,
|
|
464
|
+
resolveAuthSession,
|
|
465
|
+
request,
|
|
466
|
+
});
|
|
467
|
+
const checkpoint = findCheckpointById(listed.checkpoints, normalizedCheckpointId);
|
|
468
|
+
if (!checkpoint) {
|
|
469
|
+
throw new Error(`Checkpoint '${normalizedCheckpointId}' was not found in session ${normalizedSessionId}.`);
|
|
470
|
+
}
|
|
471
|
+
const restoreWindow = buildCheckpointRestoreWindow(checkpoint, {
|
|
472
|
+
contextEvents,
|
|
473
|
+
maxEvents,
|
|
474
|
+
});
|
|
475
|
+
const remote = await fetchEventsBefore(normalizedSessionId, {
|
|
476
|
+
targetPath,
|
|
477
|
+
beforeSequence: restoreWindow.beforeSequence,
|
|
478
|
+
limit: restoreWindow.limit,
|
|
479
|
+
forceCircuitProbe: true,
|
|
480
|
+
resolveAuthSession,
|
|
481
|
+
});
|
|
482
|
+
const classified = classifyCheckpointRestoreEvents(remote?.events || [], restoreWindow);
|
|
483
|
+
return {
|
|
484
|
+
ok: Boolean(remote?.ok),
|
|
485
|
+
reason: normalizeString(remote?.reason),
|
|
486
|
+
sessionId: normalizedSessionId,
|
|
487
|
+
checkpointId: normalizedCheckpointId,
|
|
488
|
+
apiUrl: listed.apiUrl,
|
|
489
|
+
checkpoint,
|
|
490
|
+
window: {
|
|
491
|
+
...restoreWindow,
|
|
492
|
+
...classified,
|
|
493
|
+
remoteOk: Boolean(remote?.ok),
|
|
494
|
+
remoteReason: normalizeString(remote?.reason),
|
|
495
|
+
remoteBeforeSequence: remote?.beforeSequence || null,
|
|
496
|
+
remoteCursor: remote?.cursor || null,
|
|
497
|
+
},
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
|
|
311
501
|
export async function createSessionCheckpoint(sessionId, options = {}) {
|
|
312
502
|
const normalizedSessionId = normalizeString(sessionId);
|
|
313
503
|
const {
|
|
@@ -446,5 +636,9 @@ export {
|
|
|
446
636
|
DEFAULT_BATCH_MAX_CHECKPOINTS,
|
|
447
637
|
DEFAULT_MAX_EVENTS,
|
|
448
638
|
DEFAULT_MIN_EVENTS,
|
|
639
|
+
DEFAULT_RESTORE_CONTEXT_EVENTS,
|
|
640
|
+
DEFAULT_RESTORE_MAX_EVENTS,
|
|
449
641
|
MAX_BATCH_MAX_CHECKPOINTS,
|
|
642
|
+
MAX_RESTORE_CONTEXT_EVENTS,
|
|
643
|
+
MAX_RESTORE_EVENTS,
|
|
450
644
|
};
|