sentinelayer-cli 0.46.5 → 0.48.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/README.md +47 -3
- package/package.json +12 -7
- package/skills/omar-gate-setup/SKILL.md +103 -0
- package/skills/senti-agent-identity/SKILL.md +80 -0
- package/skills/senti-judge/SKILL.md +139 -0
- package/skills/senti-room-notifications/SKILL.md +73 -0
- package/skills/senti-room-notifications/listen-wrapper.cjs +150 -0
- package/skills/senti-room-notifications/package.json +1 -0
- package/skills/senti-room-notifications/test-classifier.js +109 -0
- package/skills/senti-room-notifications/test-receipt.js +54 -0
- package/skills/sentinelayer-cli/SKILL.md +222 -0
- package/src/agents/devtestbot/runner.js +22 -11
- package/src/agents/devtestbot/tool.js +24 -11
- package/src/agents/jules/fix-cycle.js +7 -1
- package/src/agents/jules/tools/runtime-audit.js +17 -12
- package/src/agents/shared-tools/dispatch-core.js +10 -5
- package/src/ai/aidenid.js +4 -2
- package/src/ai/domain-target-store.js +11 -5
- package/src/ai/identity-store.js +11 -5
- package/src/ai/site-store.js +8 -4
- package/src/audit/orchestrator.js +21 -14
- package/src/audit/package.js +6 -4
- package/src/audit/persona-loop.js +7 -0
- package/src/audit/replay.js +4 -2
- package/src/auth/gate.js +32 -1
- package/src/auth/session-store.js +37 -0
- package/src/board/evidence-resolvers.js +3 -4
- package/src/cli.js +23 -3
- package/src/commands/ai/identity-lifecycle.js +30 -26
- package/src/commands/ai/provision-governance.js +32 -24
- package/src/commands/ai/shared.js +6 -4
- package/src/commands/audit.js +14 -12
- package/src/commands/board.js +2 -2
- package/src/commands/chat.js +8 -6
- package/src/commands/judge.js +125 -0
- package/src/commands/legacy-args.js +1 -0
- package/src/commands/mcp.js +45 -52
- package/src/commands/omargate.js +2 -1
- package/src/commands/plugin.js +11 -3
- package/src/commands/review.js +9 -4
- package/src/commands/scan.js +7 -7
- package/src/commands/session.js +516 -30
- package/src/commands/skills.js +45 -0
- package/src/commands/watch.js +9 -7
- package/src/config/output-filesystem.js +156 -0
- package/src/config/schema.js +4 -2
- package/src/config/service.js +40 -4
- package/src/cost/history.js +15 -9
- package/src/daemon/artifact-lineage.js +15 -12
- package/src/daemon/assignment-ledger.js +31 -25
- package/src/daemon/ast-drift.js +7 -3
- package/src/daemon/budget-governor.js +26 -20
- package/src/daemon/error-worker.js +28 -22
- package/src/daemon/fix-cycle.js +5 -4
- package/src/daemon/hybrid-mapper.js +19 -16
- package/src/daemon/ingest-refresh.js +4 -0
- package/src/daemon/jira-lifecycle.js +21 -17
- package/src/daemon/operator-control.js +18 -15
- package/src/daemon/reliability-lane.js +18 -15
- package/src/daemon/scope-engine.js +12 -8
- package/src/daemon/watchdog.js +15 -12
- package/src/engram/build-state.js +24 -3
- package/src/engram/capability-index.js +367 -0
- package/src/engram/compile-graph.js +1063 -0
- package/src/engram/contracts/action-receipt.js +82 -0
- package/src/engram/contracts/capability-index.js +259 -0
- package/src/engram/contracts/capability.js +377 -0
- package/src/engram/contracts/content-hash.js +8 -0
- package/src/engram/contracts/evidence.js +258 -0
- package/src/engram/contracts/graph.js +594 -0
- package/src/engram/contracts/identity.js +8 -0
- package/src/engram/contracts/index.js +149 -0
- package/src/engram/contracts/media-asset.js +85 -0
- package/src/engram/contracts/money.js +249 -0
- package/src/engram/contracts/source-snapshot.js +57 -0
- package/src/engram/contracts/task-context-pack.js +114 -0
- package/src/engram/contracts/temporal-segment.js +72 -0
- package/src/engram/contracts/versions.js +96 -0
- package/src/engram/extract-html.js +692 -0
- package/src/engram/graph-store.js +877 -0
- package/src/engram/index.js +98 -14
- package/src/engram/ingest-site.js +132 -0
- package/src/engram/live-state.js +1180 -0
- package/src/engram/namespace.js +34 -1
- package/src/engram/retrieve-site.js +753 -0
- package/src/engram/safe-reader.js +597 -0
- package/src/engram/source-intake.js +251 -0
- package/src/engram/store.js +227 -9
- package/src/ingest/engine.js +21 -28
- package/src/judge/verdict.js +305 -0
- package/src/legacy-cli.js +96 -19
- package/src/mcp/bridge-inputs.js +25 -4
- package/src/mcp/cli-command-tools.js +70 -0
- package/src/mcp/registry.js +17 -13
- package/src/mcp/session-stdio-server.js +297 -19
- package/src/memory/blackboard.js +5 -4
- package/src/net/gh-api-path.js +35 -9
- package/src/net/test-egress-guard.js +16 -4
- package/src/plugin/manifest.js +12 -8
- package/src/review/ai-review.js +110 -10
- package/src/review/investor-dd-devtestbot.js +17 -6
- package/src/review/investor-dd-orchestrator.js +21 -13
- package/src/review/local-review.js +26 -16
- package/src/review/omargate-cache.js +5 -5
- package/src/review/omargate-orchestrator.js +89 -17
- package/src/review/replay.js +9 -4
- package/src/review/report.js +22 -13
- package/src/review/typesafe-severity.js +201 -0
- package/src/session/admission-auth.js +34 -7
- package/src/session/admission.js +110 -15
- package/src/session/attachment-summary.js +77 -0
- package/src/session/edit-summary.js +19 -0
- package/src/session/invitations.js +65 -1
- package/src/session/mentions.js +17 -9
- package/src/session/message-edits.js +5 -4
- package/src/session/preview.js +10 -2
- package/src/session/recap.js +3 -2
- package/src/session/room-files.js +519 -0
- package/src/session/route-classes.js +22 -8
- package/src/session/setup-guides.js +4 -0
- package/src/session/standup.js +192 -0
- package/src/session/sync.js +229 -10
- package/src/session/tickets.js +41 -0
- package/src/session/transcript.js +2 -1
- package/src/session/wake/resolve-target.js +7 -3
- package/src/skills/listener-runtime.cjs +92 -0
- package/src/skills/service.js +207 -0
- package/src/swarm/factory.js +5 -5
- package/src/swarm/pentest.js +9 -9
- package/src/swarm/report.js +4 -4
- package/src/swarm/runtime.js +15 -12
- package/src/swarm/scenario-dsl.js +7 -5
- package/src/telemetry/ledger.js +16 -7
package/src/daemon/ast-drift.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
// ast-parser-layer.js so we don't spawn Python for the JS/TS majority.
|
|
13
13
|
|
|
14
14
|
import fsp from "node:fs/promises";
|
|
15
|
+
import { requireOutputFileSystem } from "../config/output-filesystem.js";
|
|
15
16
|
import path from "node:path";
|
|
16
17
|
import process from "node:process";
|
|
17
18
|
|
|
@@ -452,24 +453,27 @@ export async function detectAstDrift({
|
|
|
452
453
|
}
|
|
453
454
|
|
|
454
455
|
export async function writeAstSnapshot({
|
|
456
|
+
outputFs,
|
|
455
457
|
rootPath = process.cwd(),
|
|
456
458
|
snapshotPath = "",
|
|
457
459
|
snapshot,
|
|
458
460
|
} = {}) {
|
|
461
|
+
requireOutputFileSystem(outputFs);
|
|
459
462
|
if (!snapshot || typeof snapshot !== "object") {
|
|
460
463
|
throw new Error("snapshot is required.");
|
|
461
464
|
}
|
|
462
465
|
const resolvedSnapshotPath = snapshotPath
|
|
463
466
|
? path.resolve(rootPath, snapshotPath)
|
|
464
467
|
: path.join(rootPath, DEFAULT_AST_SNAPSHOT_RELATIVE_PATH);
|
|
465
|
-
await
|
|
468
|
+
await outputFs.assertFilePath(resolvedSnapshotPath);
|
|
469
|
+
await outputFs.mkdir(path.dirname(resolvedSnapshotPath), { recursive: true });
|
|
466
470
|
const tmpPath = `${resolvedSnapshotPath}.${process.pid}.${Date.now()}.tmp`;
|
|
467
|
-
await
|
|
471
|
+
await outputFs.writeFile(
|
|
468
472
|
tmpPath,
|
|
469
473
|
`${JSON.stringify(snapshot, null, 2)}\n`,
|
|
470
474
|
"utf-8"
|
|
471
475
|
);
|
|
472
|
-
await
|
|
476
|
+
await outputFs.rename(tmpPath, resolvedSnapshotPath);
|
|
473
477
|
return resolvedSnapshotPath;
|
|
474
478
|
}
|
|
475
479
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fsp from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
|
+
import { bindOutputFileSystem, requireOutputFileSystem } from "../config/output-filesystem.js";
|
|
4
5
|
import { resolveAssignmentLedgerStorage } from "./assignment-ledger.js";
|
|
5
6
|
import { resolveErrorDaemonStorage } from "./error-worker.js";
|
|
6
7
|
|
|
@@ -179,14 +180,16 @@ async function loadJsonFile(filePath, defaultFactory) {
|
|
|
179
180
|
}
|
|
180
181
|
}
|
|
181
182
|
|
|
182
|
-
async function writeJsonFile(filePath, payload = {}) {
|
|
183
|
-
|
|
184
|
-
await
|
|
183
|
+
async function writeJsonFile(filePath, payload = {}, outputFs) {
|
|
184
|
+
requireOutputFileSystem(outputFs);
|
|
185
|
+
await outputFs.mkdir(path.dirname(filePath), { recursive: true });
|
|
186
|
+
await outputFs.writeFile(filePath, `${JSON.stringify(payload, null, 2)}\n`, "utf-8");
|
|
185
187
|
}
|
|
186
188
|
|
|
187
|
-
async function appendEvent(filePath, payload = {}) {
|
|
188
|
-
|
|
189
|
-
await
|
|
189
|
+
async function appendEvent(filePath, payload = {}, outputFs) {
|
|
190
|
+
requireOutputFileSystem(outputFs);
|
|
191
|
+
await outputFs.mkdir(path.dirname(filePath), { recursive: true });
|
|
192
|
+
await outputFs.appendFile(filePath, `${JSON.stringify(payload)}\n`, "utf-8");
|
|
190
193
|
}
|
|
191
194
|
|
|
192
195
|
async function loadQueue(queuePath, nowIso = new Date().toISOString()) {
|
|
@@ -202,12 +205,13 @@ async function loadQueue(queuePath, nowIso = new Date().toISOString()) {
|
|
|
202
205
|
};
|
|
203
206
|
}
|
|
204
207
|
|
|
205
|
-
async function writeQueue(queuePath, queue = {}, nowIso = new Date().toISOString()) {
|
|
208
|
+
async function writeQueue(queuePath, queue = {}, nowIso = new Date().toISOString(), outputFs) {
|
|
209
|
+
requireOutputFileSystem(outputFs);
|
|
206
210
|
await writeJsonFile(queuePath, {
|
|
207
211
|
schemaVersion: QUEUE_SCHEMA_VERSION,
|
|
208
212
|
generatedAt: normalizeIsoTimestamp(nowIso, nowIso),
|
|
209
213
|
items: Array.isArray(queue.items) ? queue.items : [],
|
|
210
|
-
});
|
|
214
|
+
}, outputFs);
|
|
211
215
|
}
|
|
212
216
|
|
|
213
217
|
async function loadAssignmentLedger(ledgerPath, nowIso = new Date().toISOString()) {
|
|
@@ -218,12 +222,13 @@ async function loadAssignmentLedger(ledgerPath, nowIso = new Date().toISOString(
|
|
|
218
222
|
}));
|
|
219
223
|
}
|
|
220
224
|
|
|
221
|
-
async function writeAssignmentLedger(ledgerPath, ledger = {}, nowIso = new Date().toISOString()) {
|
|
225
|
+
async function writeAssignmentLedger(ledgerPath, ledger = {}, nowIso = new Date().toISOString(), outputFs) {
|
|
226
|
+
requireOutputFileSystem(outputFs);
|
|
222
227
|
await writeJsonFile(ledgerPath, {
|
|
223
228
|
...ledger,
|
|
224
229
|
generatedAt: normalizeIsoTimestamp(nowIso, nowIso),
|
|
225
230
|
assignments: Array.isArray(ledger.assignments) ? ledger.assignments : [],
|
|
226
|
-
});
|
|
231
|
+
}, outputFs);
|
|
227
232
|
}
|
|
228
233
|
|
|
229
234
|
export function evaluateDaemonBudget({
|
|
@@ -371,12 +376,12 @@ export async function resolveBudgetGovernorStorage({
|
|
|
371
376
|
env,
|
|
372
377
|
homeDir,
|
|
373
378
|
});
|
|
374
|
-
return {
|
|
379
|
+
return bindOutputFileSystem({
|
|
375
380
|
...daemonStorage,
|
|
376
381
|
budgetStatePath: path.join(daemonStorage.baseDir, "budget-state.json"),
|
|
377
382
|
budgetEventsPath: path.join(daemonStorage.baseDir, "budget-events.ndjson"),
|
|
378
383
|
budgetRunsDir: path.join(daemonStorage.baseDir, "budget-runs"),
|
|
379
|
-
};
|
|
384
|
+
}, daemonStorage.outputFs);
|
|
380
385
|
}
|
|
381
386
|
|
|
382
387
|
async function loadBudgetState(filePath, nowIso = new Date().toISOString()) {
|
|
@@ -392,7 +397,8 @@ async function loadBudgetState(filePath, nowIso = new Date().toISOString()) {
|
|
|
392
397
|
};
|
|
393
398
|
}
|
|
394
399
|
|
|
395
|
-
async function writeBudgetState(filePath, state = {}, nowIso = new Date().toISOString()) {
|
|
400
|
+
async function writeBudgetState(filePath, state = {}, nowIso = new Date().toISOString(), outputFs) {
|
|
401
|
+
requireOutputFileSystem(outputFs);
|
|
396
402
|
const normalized = {
|
|
397
403
|
schemaVersion: BUDGET_STATE_SCHEMA_VERSION,
|
|
398
404
|
generatedAt: normalizeIsoTimestamp(nowIso, nowIso),
|
|
@@ -402,7 +408,7 @@ async function writeBudgetState(filePath, state = {}, nowIso = new Date().toISOS
|
|
|
402
408
|
.filter((record) => record.workItemId)
|
|
403
409
|
: [],
|
|
404
410
|
};
|
|
405
|
-
await writeJsonFile(filePath, normalized);
|
|
411
|
+
await writeJsonFile(filePath, normalized, outputFs);
|
|
406
412
|
return normalized;
|
|
407
413
|
}
|
|
408
414
|
|
|
@@ -529,9 +535,9 @@ export async function applyDaemonBudgetCheck({
|
|
|
529
535
|
}
|
|
530
536
|
|
|
531
537
|
const [savedState] = await Promise.all([
|
|
532
|
-
writeBudgetState(storage.budgetStatePath, budgetState, normalizedNow),
|
|
533
|
-
writeQueue(storage.queuePath, queue, normalizedNow),
|
|
534
|
-
writeAssignmentLedger(assignmentStorage.ledgerPath, assignmentLedger, normalizedNow),
|
|
538
|
+
writeBudgetState(storage.budgetStatePath, budgetState, normalizedNow, storage.outputFs),
|
|
539
|
+
writeQueue(storage.queuePath, queue, normalizedNow, storage.outputFs),
|
|
540
|
+
writeAssignmentLedger(assignmentStorage.ledgerPath, assignmentLedger, normalizedNow, assignmentStorage.outputFs),
|
|
535
541
|
]);
|
|
536
542
|
|
|
537
543
|
await appendEvent(storage.budgetEventsPath, {
|
|
@@ -543,9 +549,9 @@ export async function applyDaemonBudgetCheck({
|
|
|
543
549
|
warningCodes: evaluation.warnings.map((item) => item.code),
|
|
544
550
|
stopCodes: evaluation.stopReasons.map((item) => item.code),
|
|
545
551
|
quarantineUntil: evaluation.quarantineUntil,
|
|
546
|
-
});
|
|
552
|
+
}, storage.outputFs);
|
|
547
553
|
|
|
548
|
-
await
|
|
554
|
+
await storage.outputFs.mkdir(storage.budgetRunsDir, { recursive: true });
|
|
549
555
|
const runId = `budget-check-${normalizedNow.replace(/[:.]/g, "-")}-${String(
|
|
550
556
|
budgetState.records.length
|
|
551
557
|
).padStart(4, "0")}`;
|
|
@@ -562,7 +568,7 @@ export async function applyDaemonBudgetCheck({
|
|
|
562
568
|
usage: evaluation.usage,
|
|
563
569
|
quarantineStartedAt: evaluation.quarantineStartedAt,
|
|
564
570
|
quarantineUntil: evaluation.quarantineUntil,
|
|
565
|
-
});
|
|
571
|
+
}, storage.outputFs);
|
|
566
572
|
|
|
567
573
|
return {
|
|
568
574
|
...storage,
|
|
@@ -2,7 +2,8 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
2
2
|
import fsp from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { bindOutputFileSystem, requireOutputFileSystem } from "../config/output-filesystem.js";
|
|
6
|
+
import { resolveOutputContext } from "../config/service.js";
|
|
6
7
|
import { createAgentEvent } from "../events/schema.js";
|
|
7
8
|
import { appendToStream } from "../session/stream.js";
|
|
8
9
|
|
|
@@ -290,7 +291,8 @@ async function loadQueueFile(queuePath, nowIso = new Date().toISOString()) {
|
|
|
290
291
|
}
|
|
291
292
|
}
|
|
292
293
|
|
|
293
|
-
async function writeQueueFile(queuePath, queue = {}, nowIso = new Date().toISOString()) {
|
|
294
|
+
async function writeQueueFile(queuePath, queue = {}, nowIso = new Date().toISOString(), outputFs) {
|
|
295
|
+
requireOutputFileSystem(outputFs);
|
|
294
296
|
const normalized = {
|
|
295
297
|
schemaVersion: QUEUE_SCHEMA_VERSION,
|
|
296
298
|
generatedAt: normalizeIsoTimestamp(nowIso, nowIso),
|
|
@@ -300,8 +302,8 @@ async function writeQueueFile(queuePath, queue = {}, nowIso = new Date().toISOSt
|
|
|
300
302
|
.filter((item) => normalizeString(item.fingerprint))
|
|
301
303
|
: [],
|
|
302
304
|
};
|
|
303
|
-
await
|
|
304
|
-
await
|
|
305
|
+
await outputFs.mkdir(path.dirname(queuePath), { recursive: true });
|
|
306
|
+
await outputFs.writeFile(queuePath, `${JSON.stringify(normalized, null, 2)}\n`, "utf-8");
|
|
305
307
|
return normalized;
|
|
306
308
|
}
|
|
307
309
|
|
|
@@ -327,7 +329,8 @@ async function loadWorkerStateFile(statePath, nowIso = new Date().toISOString())
|
|
|
327
329
|
}
|
|
328
330
|
}
|
|
329
331
|
|
|
330
|
-
async function writeWorkerStateFile(statePath, state = {}, nowIso = new Date().toISOString()) {
|
|
332
|
+
async function writeWorkerStateFile(statePath, state = {}, nowIso = new Date().toISOString(), outputFs) {
|
|
333
|
+
requireOutputFileSystem(outputFs);
|
|
331
334
|
const normalized = {
|
|
332
335
|
schemaVersion: STATE_SCHEMA_VERSION,
|
|
333
336
|
updatedAt: normalizeIsoTimestamp(nowIso, nowIso),
|
|
@@ -338,8 +341,8 @@ async function writeWorkerStateFile(statePath, state = {}, nowIso = new Date().t
|
|
|
338
341
|
totalQueuedItems: normalizeNonNegativeInteger(state.totalQueuedItems, 0),
|
|
339
342
|
totalDedupeHits: normalizeNonNegativeInteger(state.totalDedupeHits, 0),
|
|
340
343
|
};
|
|
341
|
-
await
|
|
342
|
-
await
|
|
344
|
+
await outputFs.mkdir(path.dirname(statePath), { recursive: true });
|
|
345
|
+
await outputFs.writeFile(statePath, `${JSON.stringify(normalized, null, 2)}\n`, "utf-8");
|
|
343
346
|
return normalized;
|
|
344
347
|
}
|
|
345
348
|
|
|
@@ -427,15 +430,16 @@ export async function resolveErrorDaemonStorage({
|
|
|
427
430
|
env,
|
|
428
431
|
homeDir,
|
|
429
432
|
} = {}) {
|
|
430
|
-
const outputRoot = await
|
|
433
|
+
const { outputRoot, outputFs } = await resolveOutputContext({
|
|
431
434
|
cwd: path.resolve(String(targetPath || ".")),
|
|
432
435
|
outputDirOverride: outputDir,
|
|
433
436
|
env,
|
|
434
437
|
homeDir,
|
|
435
438
|
});
|
|
439
|
+
requireOutputFileSystem(outputFs);
|
|
436
440
|
const observabilityRoot = path.join(outputRoot, "observability");
|
|
437
441
|
const baseDir = path.join(observabilityRoot, "error-daemon");
|
|
438
|
-
return {
|
|
442
|
+
return bindOutputFileSystem({
|
|
439
443
|
outputRoot,
|
|
440
444
|
observabilityRoot,
|
|
441
445
|
baseDir,
|
|
@@ -445,7 +449,7 @@ export async function resolveErrorDaemonStorage({
|
|
|
445
449
|
intakeDir: path.join(baseDir, "intake"),
|
|
446
450
|
runsDir: path.join(baseDir, "runs"),
|
|
447
451
|
sweepsDir: path.join(baseDir, "sweeps"),
|
|
448
|
-
};
|
|
452
|
+
}, outputFs);
|
|
449
453
|
}
|
|
450
454
|
|
|
451
455
|
export async function appendAdminErrorEvent({
|
|
@@ -464,15 +468,15 @@ export async function appendAdminErrorEvent({
|
|
|
464
468
|
homeDir,
|
|
465
469
|
});
|
|
466
470
|
|
|
467
|
-
await
|
|
468
|
-
await
|
|
471
|
+
await storage.outputFs.mkdir(storage.baseDir, { recursive: true });
|
|
472
|
+
await storage.outputFs.appendFile(storage.streamPath, `${JSON.stringify(normalizedEvent)}\n`, "utf-8");
|
|
469
473
|
|
|
470
|
-
await
|
|
474
|
+
await storage.outputFs.mkdir(storage.intakeDir, { recursive: true });
|
|
471
475
|
const intakePath = path.join(
|
|
472
476
|
storage.intakeDir,
|
|
473
477
|
`intake-${stableTimestampForFile(new Date(normalizedEvent.recordedAt))}-${normalizedEvent.eventId.slice(0, 8)}.json`
|
|
474
478
|
);
|
|
475
|
-
await
|
|
479
|
+
await storage.outputFs.writeFile(
|
|
476
480
|
intakePath,
|
|
477
481
|
`${JSON.stringify(
|
|
478
482
|
{
|
|
@@ -506,9 +510,10 @@ async function writeWorkItemIntakeArtifact(
|
|
|
506
510
|
nowIso = new Date().toISOString(),
|
|
507
511
|
} = {}
|
|
508
512
|
) {
|
|
513
|
+
requireOutputFileSystem(storage.outputFs);
|
|
509
514
|
const normalizedNow = normalizeIsoTimestamp(nowIso, new Date().toISOString());
|
|
510
515
|
const intakePath = resolveWorkItemIntakePath(storage, queueItem, normalizedNow);
|
|
511
|
-
await
|
|
516
|
+
await storage.outputFs.mkdir(path.dirname(intakePath), { recursive: true });
|
|
512
517
|
const normalizedRequestIds = normalizeRequestIds(queueItem.requestIds || []);
|
|
513
518
|
const payload = {
|
|
514
519
|
schemaVersion: "1.0.0",
|
|
@@ -537,7 +542,7 @@ async function writeWorkItemIntakeArtifact(
|
|
|
537
542
|
status: queueItem.status,
|
|
538
543
|
message: queueItem.message,
|
|
539
544
|
};
|
|
540
|
-
await
|
|
545
|
+
await storage.outputFs.writeFile(intakePath, `${JSON.stringify(payload, null, 2)}\n`, "utf-8");
|
|
541
546
|
return {
|
|
542
547
|
intakePath,
|
|
543
548
|
payload,
|
|
@@ -696,7 +701,7 @@ export async function runErrorDaemonWorker({
|
|
|
696
701
|
}
|
|
697
702
|
|
|
698
703
|
queue.generatedAt = normalizedNow;
|
|
699
|
-
const savedQueue = await writeQueueFile(storage.queuePath, queue, normalizedNow);
|
|
704
|
+
const savedQueue = await writeQueueFile(storage.queuePath, queue, normalizedNow, storage.outputFs);
|
|
700
705
|
|
|
701
706
|
const savedState = await writeWorkerStateFile(
|
|
702
707
|
storage.statePath,
|
|
@@ -710,7 +715,8 @@ export async function runErrorDaemonWorker({
|
|
|
710
715
|
totalQueuedItems: savedQueue.items.length,
|
|
711
716
|
totalDedupeHits: state.totalDedupeHits + dedupedCount,
|
|
712
717
|
},
|
|
713
|
-
normalizedNow
|
|
718
|
+
normalizedNow,
|
|
719
|
+
storage.outputFs
|
|
714
720
|
);
|
|
715
721
|
|
|
716
722
|
const intakeArtifacts = [];
|
|
@@ -759,10 +765,10 @@ export async function runErrorDaemonWorker({
|
|
|
759
765
|
}
|
|
760
766
|
}
|
|
761
767
|
|
|
762
|
-
await
|
|
768
|
+
await storage.outputFs.mkdir(storage.runsDir, { recursive: true });
|
|
763
769
|
const runId = `error-daemon-run-${stableTimestampForFile(new Date(normalizedNow))}-${randomUUID().slice(0, 8)}`;
|
|
764
770
|
const runPath = path.join(storage.runsDir, `${runId}.json`);
|
|
765
|
-
await
|
|
771
|
+
await storage.outputFs.writeFile(
|
|
766
772
|
runPath,
|
|
767
773
|
`${JSON.stringify(
|
|
768
774
|
{
|
|
@@ -893,9 +899,9 @@ export async function scheduledErrorSweep({
|
|
|
893
899
|
})),
|
|
894
900
|
};
|
|
895
901
|
|
|
896
|
-
await
|
|
902
|
+
await storage.outputFs.mkdir(storage.sweepsDir, { recursive: true });
|
|
897
903
|
const sweepPath = path.join(storage.sweepsDir, `${runId}.json`);
|
|
898
|
-
await
|
|
904
|
+
await storage.outputFs.writeFile(sweepPath, `${JSON.stringify(digest, null, 2)}\n`, "utf-8");
|
|
899
905
|
|
|
900
906
|
let sweepEvent = null;
|
|
901
907
|
if (normalizedSessionId) {
|
package/src/daemon/fix-cycle.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import
|
|
3
|
+
import { requireOutputFileSystem } from "../config/output-filesystem.js";
|
|
4
4
|
import { startJiraLifecycle, commentJiraIssue, transitionJiraIssue } from "./jira-lifecycle.js";
|
|
5
5
|
import { claimAssignment, heartbeatAssignment, releaseAssignment } from "./assignment-ledger.js";
|
|
6
6
|
import { createAgentEvent } from "../events/schema.js";
|
|
@@ -23,7 +23,8 @@ const HEARTBEAT_INTERVAL_MS = 300000;
|
|
|
23
23
|
const OMAR_POLL_INTERVAL_MS = 15000;
|
|
24
24
|
const OMAR_POLL_MAX_ATTEMPTS = 40; // 10 minutes max wait
|
|
25
25
|
|
|
26
|
-
export async function runFixCycle({ workItemId, workItem, rootPath, scopeMap, findings, onEvent, agentIdentity }) {
|
|
26
|
+
export async function runFixCycle({ workItemId, workItem, rootPath, scopeMap, findings, onEvent, agentIdentity, outputFs }) {
|
|
27
|
+
requireOutputFileSystem(outputFs);
|
|
27
28
|
const agentDef = agentIdentity || { id: "unknown", persona: "Unknown Agent", color: "white", avatar: "", signature: "" };
|
|
28
29
|
const emit = (ev, pl) => {
|
|
29
30
|
if (onEvent) onEvent(createAgentEvent({
|
|
@@ -40,7 +41,7 @@ export async function runFixCycle({ workItemId, workItem, rootPath, scopeMap, fi
|
|
|
40
41
|
};
|
|
41
42
|
|
|
42
43
|
const artDir = path.join(rootPath, ".sentinelayer", "observability", "fixes", workItemId);
|
|
43
|
-
await
|
|
44
|
+
await outputFs.mkdir(artDir, { recursive: true });
|
|
44
45
|
|
|
45
46
|
let jiraKey = null;
|
|
46
47
|
let prNumber = null;
|
|
@@ -202,7 +203,7 @@ export async function runFixCycle({ workItemId, workItem, rootPath, scopeMap, fi
|
|
|
202
203
|
findingsAddressed: findings?.length || 0,
|
|
203
204
|
signature: agentDef.signature,
|
|
204
205
|
};
|
|
205
|
-
await
|
|
206
|
+
await outputFs.writeFile(
|
|
206
207
|
path.join(artDir, "fix-result.json"),
|
|
207
208
|
JSON.stringify(result, null, 2),
|
|
208
209
|
);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fsp from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
|
+
import { bindOutputFileSystem, requireOutputFileSystem } from "../config/output-filesystem.js";
|
|
4
5
|
import { parseAstModuleSpecifiers } from "./ast-parser-layer.js";
|
|
5
6
|
import { buildCallgraphOverlay } from "./callgraph-overlay.js";
|
|
6
7
|
import { collectCodebaseIngest } from "../ingest/engine.js";
|
|
@@ -110,14 +111,16 @@ async function readJsonFile(filePath, defaultFactory) {
|
|
|
110
111
|
}
|
|
111
112
|
}
|
|
112
113
|
|
|
113
|
-
async function writeJsonFile(filePath, payload = {}) {
|
|
114
|
-
|
|
115
|
-
await
|
|
114
|
+
async function writeJsonFile(filePath, payload = {}, outputFs) {
|
|
115
|
+
requireOutputFileSystem(outputFs);
|
|
116
|
+
await outputFs.mkdir(path.dirname(filePath), { recursive: true });
|
|
117
|
+
await outputFs.writeFile(filePath, `${JSON.stringify(payload, null, 2)}\n`, "utf-8");
|
|
116
118
|
}
|
|
117
119
|
|
|
118
|
-
async function appendJsonLine(filePath, payload = {}) {
|
|
119
|
-
|
|
120
|
-
await
|
|
120
|
+
async function appendJsonLine(filePath, payload = {}, outputFs) {
|
|
121
|
+
requireOutputFileSystem(outputFs);
|
|
122
|
+
await outputFs.mkdir(path.dirname(filePath), { recursive: true });
|
|
123
|
+
await outputFs.appendFile(filePath, `${JSON.stringify(payload)}\n`, "utf-8");
|
|
121
124
|
}
|
|
122
125
|
|
|
123
126
|
function tokenizeWorkItem(queueItem = {}) {
|
|
@@ -364,7 +367,7 @@ export async function resolveHybridMappingStorage({
|
|
|
364
367
|
homeDir,
|
|
365
368
|
});
|
|
366
369
|
const mappingDir = path.join(daemonStorage.baseDir, "mapping");
|
|
367
|
-
return {
|
|
370
|
+
return bindOutputFileSystem({
|
|
368
371
|
...daemonStorage,
|
|
369
372
|
mappingDir,
|
|
370
373
|
mapIndexPath: path.join(mappingDir, "hybrid-map-index.json"),
|
|
@@ -373,7 +376,7 @@ export async function resolveHybridMappingStorage({
|
|
|
373
376
|
handoffIndexPath: path.join(mappingDir, "hybrid-handoff-index.json"),
|
|
374
377
|
handoffEventsPath: path.join(mappingDir, "hybrid-handoff-events.ndjson"),
|
|
375
378
|
handoffRunsDir: path.join(mappingDir, "handoffs"),
|
|
376
|
-
};
|
|
379
|
+
}, daemonStorage.outputFs);
|
|
377
380
|
}
|
|
378
381
|
|
|
379
382
|
export async function buildHybridScopeMap({
|
|
@@ -577,8 +580,8 @@ export async function buildHybridScopeMap({
|
|
|
577
580
|
},
|
|
578
581
|
};
|
|
579
582
|
|
|
580
|
-
await
|
|
581
|
-
await writeJsonFile(runPath, runPayload);
|
|
583
|
+
await storage.outputFs.mkdir(storage.mapRunsDir, { recursive: true });
|
|
584
|
+
await writeJsonFile(runPath, runPayload, storage.outputFs);
|
|
582
585
|
|
|
583
586
|
const rawIndex = await readJsonFile(storage.mapIndexPath, () => createInitialMapIndex(normalizedNow));
|
|
584
587
|
const index = normalizeMapIndex(rawIndex, normalizedNow);
|
|
@@ -600,7 +603,7 @@ export async function buildHybridScopeMap({
|
|
|
600
603
|
...index.maps.filter((entry) => entry.runId !== runId),
|
|
601
604
|
].slice(0, 2000);
|
|
602
605
|
await Promise.all([
|
|
603
|
-
writeJsonFile(storage.mapIndexPath, index),
|
|
606
|
+
writeJsonFile(storage.mapIndexPath, index, storage.outputFs),
|
|
604
607
|
appendJsonLine(storage.mapEventsPath, {
|
|
605
608
|
timestamp: normalizedNow,
|
|
606
609
|
eventType: "hybrid_scope_map",
|
|
@@ -618,7 +621,7 @@ export async function buildHybridScopeMap({
|
|
|
618
621
|
callGraphParsedFileCount: callGraphOverlay.summary.parsedFileCount,
|
|
619
622
|
callGraphFallbackParsedFileCount: callGraphOverlay.summary.fallbackParsedFileCount,
|
|
620
623
|
callGraphParseErrorCount: callGraphOverlay.summary.parseErrorCount,
|
|
621
|
-
}),
|
|
624
|
+
}, storage.outputFs),
|
|
622
625
|
]);
|
|
623
626
|
|
|
624
627
|
return {
|
|
@@ -791,8 +794,8 @@ export async function buildHybridHandoffPackage({
|
|
|
791
794
|
},
|
|
792
795
|
files: handoffFiles,
|
|
793
796
|
};
|
|
794
|
-
await
|
|
795
|
-
await writeJsonFile(handoffPath, payload);
|
|
797
|
+
await storage.outputFs.mkdir(storage.handoffRunsDir, { recursive: true });
|
|
798
|
+
await writeJsonFile(handoffPath, payload, storage.outputFs);
|
|
796
799
|
|
|
797
800
|
const rawIndex = await readJsonFile(storage.handoffIndexPath, () => createInitialHandoffIndex(normalizedNow));
|
|
798
801
|
const index = normalizeHandoffIndex(rawIndex, normalizedNow);
|
|
@@ -812,7 +815,7 @@ export async function buildHybridHandoffPackage({
|
|
|
812
815
|
].slice(0, 2000);
|
|
813
816
|
|
|
814
817
|
await Promise.all([
|
|
815
|
-
writeJsonFile(storage.handoffIndexPath, index),
|
|
818
|
+
writeJsonFile(storage.handoffIndexPath, index, storage.outputFs),
|
|
816
819
|
appendJsonLine(storage.handoffEventsPath, {
|
|
817
820
|
timestamp: normalizedNow,
|
|
818
821
|
eventType: "hybrid_handoff_package",
|
|
@@ -822,7 +825,7 @@ export async function buildHybridHandoffPackage({
|
|
|
822
825
|
assignee: normalizedAssignee,
|
|
823
826
|
scopedFileCount: handoffFiles.length,
|
|
824
827
|
estimatedInputTokens: budgets.estimatedInputTokens,
|
|
825
|
-
}),
|
|
828
|
+
}, storage.outputFs),
|
|
826
829
|
]);
|
|
827
830
|
|
|
828
831
|
return {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { resolveOutputContext } from "../config/service.js";
|
|
3
4
|
import { collectCodebaseIngest, generateCodebaseIngest } from "../ingest/engine.js";
|
|
4
5
|
import { createAgentEvent } from "../events/schema.js";
|
|
5
6
|
import {
|
|
@@ -74,6 +75,7 @@ export function detectIngestDrift(targetPath) {
|
|
|
74
75
|
* @returns {Promise<{ refreshed: boolean, reason: string, durationMs: number }>}
|
|
75
76
|
*/
|
|
76
77
|
export async function refreshIngestIfNeeded(targetPath, options = {}) {
|
|
78
|
+
const { outputFs } = await resolveOutputContext({ cwd: path.resolve(targetPath), outputDirOverride: ".sentinelayer" });
|
|
77
79
|
const maxDurationMs = options.maxDurationMs || DEFAULT_MAX_DURATION_MS;
|
|
78
80
|
const driftMode = options.driftMode === "ast" ? "ast" : "file-count";
|
|
79
81
|
let astOutcome = null;
|
|
@@ -137,6 +139,7 @@ export async function refreshIngestIfNeeded(targetPath, options = {}) {
|
|
|
137
139
|
let ingest;
|
|
138
140
|
try {
|
|
139
141
|
ingest = await generateCodebaseIngest({
|
|
142
|
+
outputFs,
|
|
140
143
|
rootPath: targetPath,
|
|
141
144
|
outputDir: path.join(targetPath, ".sentinelayer"),
|
|
142
145
|
refresh: true,
|
|
@@ -152,6 +155,7 @@ export async function refreshIngestIfNeeded(targetPath, options = {}) {
|
|
|
152
155
|
if (driftMode === "ast" && astOutcome?.currentSnapshot) {
|
|
153
156
|
try {
|
|
154
157
|
await writeAstSnapshot({
|
|
158
|
+
outputFs,
|
|
155
159
|
rootPath: targetPath,
|
|
156
160
|
snapshot: astOutcome.currentSnapshot,
|
|
157
161
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fsp from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
|
+
import { bindOutputFileSystem, requireOutputFileSystem } from "../config/output-filesystem.js";
|
|
4
5
|
import { createAgentEvent } from "../events/schema.js";
|
|
5
6
|
import { appendToStream } from "../session/stream.js";
|
|
6
7
|
import { resolveAssignmentLedgerStorage } from "./assignment-ledger.js";
|
|
@@ -165,14 +166,16 @@ async function loadJsonFile(filePath, defaultFactory) {
|
|
|
165
166
|
}
|
|
166
167
|
}
|
|
167
168
|
|
|
168
|
-
async function writeJsonFile(filePath, payload = {}) {
|
|
169
|
-
|
|
170
|
-
await
|
|
169
|
+
async function writeJsonFile(filePath, payload = {}, outputFs) {
|
|
170
|
+
requireOutputFileSystem(outputFs);
|
|
171
|
+
await outputFs.mkdir(path.dirname(filePath), { recursive: true });
|
|
172
|
+
await outputFs.writeFile(filePath, `${JSON.stringify(payload, null, 2)}\n`, "utf-8");
|
|
171
173
|
}
|
|
172
174
|
|
|
173
|
-
async function appendEvent(filePath, payload = {}) {
|
|
174
|
-
|
|
175
|
-
await
|
|
175
|
+
async function appendEvent(filePath, payload = {}, outputFs) {
|
|
176
|
+
requireOutputFileSystem(outputFs);
|
|
177
|
+
await outputFs.mkdir(path.dirname(filePath), { recursive: true });
|
|
178
|
+
await outputFs.appendFile(filePath, `${JSON.stringify(payload)}\n`, "utf-8");
|
|
176
179
|
}
|
|
177
180
|
|
|
178
181
|
async function loadQueue(queuePath, nowIso = new Date().toISOString()) {
|
|
@@ -200,7 +203,8 @@ async function loadLifecycle(lifecyclePath, nowIso = new Date().toISOString()) {
|
|
|
200
203
|
};
|
|
201
204
|
}
|
|
202
205
|
|
|
203
|
-
async function writeLifecycle(lifecyclePath, lifecycle = {}, nowIso = new Date().toISOString()) {
|
|
206
|
+
async function writeLifecycle(lifecyclePath, lifecycle = {}, nowIso = new Date().toISOString(), outputFs) {
|
|
207
|
+
requireOutputFileSystem(outputFs);
|
|
204
208
|
const normalized = {
|
|
205
209
|
schemaVersion: LIFECYCLE_SCHEMA_VERSION,
|
|
206
210
|
generatedAt: normalizeIsoTimestamp(nowIso, nowIso),
|
|
@@ -211,7 +215,7 @@ async function writeLifecycle(lifecyclePath, lifecycle = {}, nowIso = new Date()
|
|
|
211
215
|
.filter((issue) => issue.workItemId && issue.issueKey)
|
|
212
216
|
: [],
|
|
213
217
|
};
|
|
214
|
-
await writeJsonFile(lifecyclePath, normalized);
|
|
218
|
+
await writeJsonFile(lifecyclePath, normalized, outputFs);
|
|
215
219
|
return normalized;
|
|
216
220
|
}
|
|
217
221
|
|
|
@@ -284,7 +288,7 @@ async function syncIssueKeyToAssignment({
|
|
|
284
288
|
...rawLedger,
|
|
285
289
|
generatedAt: nowIso,
|
|
286
290
|
assignments,
|
|
287
|
-
});
|
|
291
|
+
}, assignmentStorage.outputFs);
|
|
288
292
|
}
|
|
289
293
|
|
|
290
294
|
export async function resolveJiraLifecycleStorage({
|
|
@@ -299,11 +303,11 @@ export async function resolveJiraLifecycleStorage({
|
|
|
299
303
|
env,
|
|
300
304
|
homeDir,
|
|
301
305
|
});
|
|
302
|
-
return {
|
|
306
|
+
return bindOutputFileSystem({
|
|
303
307
|
...daemonStorage,
|
|
304
308
|
lifecyclePath: path.join(daemonStorage.baseDir, "jira-lifecycle.json"),
|
|
305
309
|
eventsPath: path.join(daemonStorage.baseDir, "jira-events.ndjson"),
|
|
306
|
-
};
|
|
310
|
+
}, daemonStorage.outputFs);
|
|
307
311
|
}
|
|
308
312
|
|
|
309
313
|
export async function emitJiraLifecycleEvent(
|
|
@@ -453,7 +457,7 @@ export async function openJiraIssue({
|
|
|
453
457
|
normalizedNow
|
|
454
458
|
);
|
|
455
459
|
lifecycle.issues.push(issue);
|
|
456
|
-
const savedLifecycle = await writeLifecycle(storage.lifecyclePath, lifecycle, normalizedNow);
|
|
460
|
+
const savedLifecycle = await writeLifecycle(storage.lifecyclePath, lifecycle, normalizedNow, storage.outputFs);
|
|
457
461
|
await appendEvent(storage.eventsPath, {
|
|
458
462
|
timestamp: normalizedNow,
|
|
459
463
|
eventType: "create_issue",
|
|
@@ -461,7 +465,7 @@ export async function openJiraIssue({
|
|
|
461
465
|
workItemId: normalizedWorkItemId,
|
|
462
466
|
issueKey: normalizedIssueKey,
|
|
463
467
|
status: "OPEN",
|
|
464
|
-
});
|
|
468
|
+
}, storage.outputFs);
|
|
465
469
|
await syncIssueKeyToAssignment({
|
|
466
470
|
targetPath,
|
|
467
471
|
outputDir,
|
|
@@ -536,7 +540,7 @@ export async function commentJiraIssue({
|
|
|
536
540
|
existing.latestCommentAt = normalizedNow;
|
|
537
541
|
existing.updatedAt = normalizedNow;
|
|
538
542
|
lifecycle.issues[issueIndex] = normalizeIssue(existing, normalizedNow);
|
|
539
|
-
const savedLifecycle = await writeLifecycle(storage.lifecyclePath, lifecycle, normalizedNow);
|
|
543
|
+
const savedLifecycle = await writeLifecycle(storage.lifecyclePath, lifecycle, normalizedNow, storage.outputFs);
|
|
540
544
|
await appendEvent(storage.eventsPath, {
|
|
541
545
|
timestamp: normalizedNow,
|
|
542
546
|
eventType: "comment",
|
|
@@ -545,7 +549,7 @@ export async function commentJiraIssue({
|
|
|
545
549
|
issueKey: existing.issueKey,
|
|
546
550
|
type: comment.type,
|
|
547
551
|
message: comment.message,
|
|
548
|
-
});
|
|
552
|
+
}, storage.outputFs);
|
|
549
553
|
if (normalizedSessionId) {
|
|
550
554
|
await emitJiraLifecycleEvent(normalizedSessionId, {
|
|
551
555
|
phase: comment.type === "plan" ? "plan_comment" : "checkpoint",
|
|
@@ -613,7 +617,7 @@ export async function transitionJiraIssue({
|
|
|
613
617
|
existing.transitions = [...existing.transitions, transition];
|
|
614
618
|
existing.updatedAt = normalizedNow;
|
|
615
619
|
lifecycle.issues[issueIndex] = normalizeIssue(existing, normalizedNow);
|
|
616
|
-
const savedLifecycle = await writeLifecycle(storage.lifecyclePath, lifecycle, normalizedNow);
|
|
620
|
+
const savedLifecycle = await writeLifecycle(storage.lifecyclePath, lifecycle, normalizedNow, storage.outputFs);
|
|
617
621
|
await appendEvent(storage.eventsPath, {
|
|
618
622
|
timestamp: normalizedNow,
|
|
619
623
|
eventType: "transition",
|
|
@@ -623,7 +627,7 @@ export async function transitionJiraIssue({
|
|
|
623
627
|
from: transition.from,
|
|
624
628
|
to: transition.to,
|
|
625
629
|
reason: transition.reason,
|
|
626
|
-
});
|
|
630
|
+
}, storage.outputFs);
|
|
627
631
|
if (normalizedSessionId) {
|
|
628
632
|
await emitJiraLifecycleEvent(normalizedSessionId, {
|
|
629
633
|
phase: resolveTransitionPhase(nextStatus),
|