sentinelayer-cli 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/package.json +4 -4
- package/src/agents/ai-governance/index.js +12 -0
- package/src/agents/ai-governance/tools/base.js +171 -0
- package/src/agents/ai-governance/tools/eval-regression.js +47 -0
- package/src/agents/ai-governance/tools/hitl-audit.js +81 -0
- package/src/agents/ai-governance/tools/index.js +52 -0
- package/src/agents/ai-governance/tools/prompt-drift.js +42 -0
- package/src/agents/ai-governance/tools/provenance-check.js +69 -0
- package/src/agents/backend/index.js +12 -0
- package/src/agents/backend/tools/base.js +189 -0
- package/src/agents/backend/tools/circuit-breaker-check.js +123 -0
- package/src/agents/backend/tools/idempotency-audit.js +105 -0
- package/src/agents/backend/tools/index.js +87 -0
- package/src/agents/backend/tools/retry-audit.js +132 -0
- package/src/agents/backend/tools/timeout-audit.js +144 -0
- package/src/agents/code-quality/index.js +12 -0
- package/src/agents/code-quality/tools/base.js +159 -0
- package/src/agents/code-quality/tools/complexity-measure.js +197 -0
- package/src/agents/code-quality/tools/coupling-analysis.js +81 -0
- package/src/agents/code-quality/tools/cycle-detect.js +49 -0
- package/src/agents/code-quality/tools/dep-graph.js +196 -0
- package/src/agents/code-quality/tools/index.js +89 -0
- package/src/agents/data-layer/index.js +12 -0
- package/src/agents/data-layer/tools/base.js +181 -0
- package/src/agents/data-layer/tools/index-audit.js +165 -0
- package/src/agents/data-layer/tools/index.js +83 -0
- package/src/agents/data-layer/tools/migration-scan.js +135 -0
- package/src/agents/data-layer/tools/query-explain.js +120 -0
- package/src/agents/data-layer/tools/tenancy-scan.js +166 -0
- package/src/agents/documentation/index.js +12 -0
- package/src/agents/documentation/tools/api-diff.js +91 -0
- package/src/agents/documentation/tools/base.js +151 -0
- package/src/agents/documentation/tools/dead-link-check.js +58 -0
- package/src/agents/documentation/tools/docstring-coverage.js +78 -0
- package/src/agents/documentation/tools/index.js +52 -0
- package/src/agents/documentation/tools/readme-freshness.js +61 -0
- package/src/agents/envelope/fix-cycle.js +45 -0
- package/src/agents/envelope/index.js +31 -0
- package/src/agents/envelope/loop.js +150 -0
- package/src/agents/envelope/pulse.js +18 -0
- package/src/agents/envelope/stream.js +40 -0
- package/src/agents/infrastructure/index.js +12 -0
- package/src/agents/infrastructure/tools/base.js +171 -0
- package/src/agents/infrastructure/tools/checkov-run.js +32 -0
- package/src/agents/infrastructure/tools/drift-detect.js +59 -0
- package/src/agents/infrastructure/tools/iam-least-priv-check.js +78 -0
- package/src/agents/infrastructure/tools/index.js +52 -0
- package/src/agents/infrastructure/tools/tflint-run.js +31 -0
- package/src/agents/jules/loop.js +7 -4
- package/src/agents/jules/swarm/sub-agent.js +5 -1
- package/src/agents/jules/tools/auth-audit.js +10 -1
- package/src/agents/mode.js +113 -0
- package/src/agents/observability/index.js +12 -0
- package/src/agents/observability/tools/alert-audit.js +39 -0
- package/src/agents/observability/tools/base.js +181 -0
- package/src/agents/observability/tools/dashboard-gap.js +42 -0
- package/src/agents/observability/tools/index.js +54 -0
- package/src/agents/observability/tools/log-schema-check.js +74 -0
- package/src/agents/observability/tools/span-coverage.js +74 -0
- package/src/agents/persona-visuals.js +38 -0
- package/src/agents/release/index.js +12 -0
- package/src/agents/release/tools/base.js +181 -0
- package/src/agents/release/tools/changelog-diff.js +86 -0
- package/src/agents/release/tools/feature-flag-audit.js +126 -0
- package/src/agents/release/tools/index.js +61 -0
- package/src/agents/release/tools/rollback-verify.js +129 -0
- package/src/agents/release/tools/semver-check.js +109 -0
- package/src/agents/reliability/index.js +12 -0
- package/src/agents/reliability/tools/backpressure-check.js +129 -0
- package/src/agents/reliability/tools/base.js +181 -0
- package/src/agents/reliability/tools/chaos-probe.js +109 -0
- package/src/agents/reliability/tools/graceful-degradation-check.js +114 -0
- package/src/agents/reliability/tools/health-check-audit.js +111 -0
- package/src/agents/reliability/tools/index.js +87 -0
- package/src/agents/run-persona.js +109 -0
- package/src/agents/security/index.js +12 -0
- package/src/agents/security/tools/authz-audit.js +134 -0
- package/src/agents/security/tools/base.js +190 -0
- package/src/agents/security/tools/crypto-review.js +175 -0
- package/src/agents/security/tools/index.js +97 -0
- package/src/agents/security/tools/sast-scan.js +175 -0
- package/src/agents/security/tools/secrets-scan.js +216 -0
- package/src/agents/supply-chain/index.js +12 -0
- package/src/agents/supply-chain/tools/attestation-check.js +42 -0
- package/src/agents/supply-chain/tools/base.js +151 -0
- package/src/agents/supply-chain/tools/index.js +52 -0
- package/src/agents/supply-chain/tools/lockfile-integrity.js +73 -0
- package/src/agents/supply-chain/tools/package-verify.js +56 -0
- package/src/agents/supply-chain/tools/sbom-diff.js +34 -0
- package/src/agents/testing/index.js +12 -0
- package/src/agents/testing/tools/base.js +202 -0
- package/src/agents/testing/tools/coverage-gap.js +144 -0
- package/src/agents/testing/tools/flake-detect.js +125 -0
- package/src/agents/testing/tools/index.js +85 -0
- package/src/agents/testing/tools/mutation-test.js +143 -0
- package/src/agents/testing/tools/snapshot-diff.js +103 -0
- package/src/auth/gate.js +65 -37
- package/src/cli.js +1 -1
- package/src/commands/chat.js +3 -10
- package/src/commands/legacy-args.js +10 -0
- package/src/commands/omargate.js +36 -2
- package/src/commands/persona.js +46 -1
- package/src/commands/scan.js +3 -10
- package/src/commands/session.js +654 -6
- package/src/commands/spec.js +3 -10
- package/src/coord/events-log.js +141 -0
- package/src/coord/handshake.js +719 -0
- package/src/coord/index.js +35 -0
- package/src/coord/paths.js +84 -0
- package/src/coord/priority.js +62 -0
- package/src/coord/tarjan.js +157 -0
- package/src/cost/tokenizer.js +160 -0
- package/src/cost/tracker.js +61 -0
- package/src/daemon/artifact-lineage.js +362 -0
- package/src/daemon/assignment-ledger.js +117 -0
- package/src/daemon/ast-drift.js +496 -0
- package/src/daemon/ingest-refresh.js +69 -2
- package/src/ingest/engine.js +15 -0
- package/src/ingest/ownership.js +380 -0
- package/src/legacy-cli.js +68 -1
- package/src/orchestrator/kai-chen.js +126 -0
- package/src/review/ai-review.js +3 -10
- package/src/review/compliance-pack.js +389 -0
- package/src/review/investor-dd-config.js +54 -0
- package/src/review/investor-dd-file-loop.js +303 -0
- package/src/review/investor-dd-file-router.js +406 -0
- package/src/review/investor-dd-html-report.js +233 -0
- package/src/review/investor-dd-notification.js +120 -0
- package/src/review/investor-dd-orchestrator.js +405 -0
- package/src/review/investor-dd-persona-runner.js +275 -0
- package/src/review/live-validator.js +253 -0
- package/src/review/omargate-orchestrator.js +90 -2
- package/src/review/persona-prompts.js +244 -56
- package/src/review/reconciliation-rules.js +329 -0
- package/src/review/reproducibility-chain.js +136 -0
- package/src/review/scan-modes.js +102 -3
- package/src/session/agent-registry.js +7 -0
- package/src/session/analytics.js +479 -0
- package/src/session/daemon.js +609 -14
- package/src/session/file-locks.js +666 -0
- package/src/session/paths.js +4 -0
- package/src/session/recap.js +567 -0
- package/src/session/redact.js +82 -0
- package/src/session/runtime-bridge.js +24 -1
- package/src/session/scoring.js +406 -0
- package/src/session/setup-guides.js +304 -0
- package/src/session/store.js +318 -2
- package/src/session/stream.js +9 -1
- package/src/session/sync.js +753 -0
- package/src/session/tasks.js +1054 -0
- package/src/session/templates.js +188 -0
- package/src/swarm/runtime.js +1 -8
package/src/session/store.js
CHANGED
|
@@ -3,7 +3,9 @@ import fsp from "node:fs/promises";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import process from "node:process";
|
|
5
5
|
|
|
6
|
+
import { buildArtifactLineageIndex, verifyArtifactChain } from "../daemon/artifact-lineage.js";
|
|
6
7
|
import { collectCodebaseIngest } from "../ingest/engine.js";
|
|
8
|
+
import { computeSessionAnalytics } from "./analytics.js";
|
|
7
9
|
import { resolveSessionPaths, resolveSessionsRoot } from "./paths.js";
|
|
8
10
|
import { appendToStream } from "./stream.js";
|
|
9
11
|
|
|
@@ -30,6 +32,17 @@ function normalizePositiveInteger(value, fallbackValue) {
|
|
|
30
32
|
return Math.floor(normalized);
|
|
31
33
|
}
|
|
32
34
|
|
|
35
|
+
function normalizeNonNegativeInteger(value, fallbackValue = 0) {
|
|
36
|
+
if (value === undefined || value === null || normalizeString(value) === "") {
|
|
37
|
+
return fallbackValue;
|
|
38
|
+
}
|
|
39
|
+
const normalized = Number(value);
|
|
40
|
+
if (!Number.isFinite(normalized) || normalized < 0) {
|
|
41
|
+
return fallbackValue;
|
|
42
|
+
}
|
|
43
|
+
return Math.floor(normalized);
|
|
44
|
+
}
|
|
45
|
+
|
|
33
46
|
function normalizeIsoTimestamp(value, fallbackIso = new Date().toISOString()) {
|
|
34
47
|
const normalized = normalizeString(value);
|
|
35
48
|
if (!normalized) {
|
|
@@ -109,6 +122,90 @@ function normalizeCodebaseContext(ingest = {}) {
|
|
|
109
122
|
};
|
|
110
123
|
}
|
|
111
124
|
|
|
125
|
+
function normalizeStringList(values = []) {
|
|
126
|
+
if (!Array.isArray(values)) {
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
return Array.from(
|
|
130
|
+
new Set(
|
|
131
|
+
values
|
|
132
|
+
.map((value) => normalizeString(value))
|
|
133
|
+
.filter(Boolean)
|
|
134
|
+
)
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function toPosixPath(value = "") {
|
|
139
|
+
return String(value || "").replace(/\\/g, "/");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function toRelativePosix(baseDir, absolutePath) {
|
|
143
|
+
if (!absolutePath) {
|
|
144
|
+
return "";
|
|
145
|
+
}
|
|
146
|
+
return toPosixPath(path.relative(baseDir, absolutePath));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function normalizeDateKeyFromCloseoutPath(closeoutPath = "", fallbackIso = new Date().toISOString()) {
|
|
150
|
+
const normalized = toPosixPath(closeoutPath);
|
|
151
|
+
const match = /\/observability\/(\d{4}-\d{2}-\d{2})\//.exec(`/${normalized}`);
|
|
152
|
+
if (match) {
|
|
153
|
+
return match[1];
|
|
154
|
+
}
|
|
155
|
+
return normalizeIsoTimestamp(fallbackIso).slice(0, 10);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function normalizeSharedResources(raw = {}, { nowIso = new Date().toISOString() } = {}) {
|
|
159
|
+
const source = raw && typeof raw === "object" ? raw : {};
|
|
160
|
+
return {
|
|
161
|
+
provisionedIdentityIds: normalizeStringList(source.provisionedIdentityIds),
|
|
162
|
+
provisioningTags: normalizeStringList(source.provisioningTags),
|
|
163
|
+
provisionCount: normalizeNonNegativeInteger(source.provisionCount, 0),
|
|
164
|
+
lastProvisionedAt: source.lastProvisionedAt
|
|
165
|
+
? normalizeIsoTimestamp(source.lastProvisionedAt, nowIso)
|
|
166
|
+
: null,
|
|
167
|
+
updatedAt: normalizeIsoTimestamp(source.updatedAt, nowIso),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function normalizeTemplateAgent(raw = {}) {
|
|
172
|
+
const source = raw && typeof raw === "object" ? raw : {};
|
|
173
|
+
return {
|
|
174
|
+
role: normalizeString(source.role) || "agent",
|
|
175
|
+
instructions: normalizeString(source.instructions) || "Follow session guidance.",
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function normalizeSessionTemplate(raw = null) {
|
|
180
|
+
if (!raw || typeof raw !== "object") {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
const source = raw;
|
|
184
|
+
const id = normalizeString(source.id || source.name);
|
|
185
|
+
if (!id) {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
const suggestedAgents = Array.isArray(source.suggestedAgents)
|
|
189
|
+
? source.suggestedAgents.map((agent) => normalizeTemplateAgent(agent))
|
|
190
|
+
: [];
|
|
191
|
+
const ttlHours = normalizePositiveInteger(source.ttlHours, 1);
|
|
192
|
+
const normalizedAutoProvision =
|
|
193
|
+
source.autoProvisionEmails === undefined || source.autoProvisionEmails === null
|
|
194
|
+
? null
|
|
195
|
+
: normalizePositiveInteger(source.autoProvisionEmails, 1);
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
id,
|
|
199
|
+
version: normalizeString(source.version) || "1.0.0",
|
|
200
|
+
registryVersion: normalizeString(source.registryVersion) || "1.0.0",
|
|
201
|
+
description: normalizeString(source.description),
|
|
202
|
+
daemonModel: normalizeString(source.daemonModel),
|
|
203
|
+
ttlHours,
|
|
204
|
+
autoProvisionEmails: normalizedAutoProvision,
|
|
205
|
+
suggestedAgents,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
112
209
|
async function collectSessionCodebaseContext(targetPath) {
|
|
113
210
|
const cachedIngestPath = path.join(targetPath, ".sentinelayer", "CODEBASE_INGEST.json");
|
|
114
211
|
const cachedIngest = await readJsonFile(cachedIngestPath, { allowMissing: true });
|
|
@@ -119,6 +216,102 @@ async function collectSessionCodebaseContext(targetPath) {
|
|
|
119
216
|
return normalizeCodebaseContext(ingest);
|
|
120
217
|
}
|
|
121
218
|
|
|
219
|
+
async function buildArchiveSidecars(
|
|
220
|
+
sessionId,
|
|
221
|
+
{
|
|
222
|
+
targetPath,
|
|
223
|
+
outputDir = "",
|
|
224
|
+
env,
|
|
225
|
+
homeDir,
|
|
226
|
+
nowIso = new Date().toISOString(),
|
|
227
|
+
} = {}
|
|
228
|
+
) {
|
|
229
|
+
const normalizedSessionId = normalizeString(sessionId);
|
|
230
|
+
const normalizedTargetPath = path.resolve(String(targetPath || "."));
|
|
231
|
+
const normalizedNow = normalizeIsoTimestamp(nowIso, new Date().toISOString());
|
|
232
|
+
|
|
233
|
+
const analytics = await computeSessionAnalytics(normalizedSessionId, {
|
|
234
|
+
targetPath: normalizedTargetPath,
|
|
235
|
+
outputDir,
|
|
236
|
+
env,
|
|
237
|
+
homeDir,
|
|
238
|
+
nowIso: normalizedNow,
|
|
239
|
+
});
|
|
240
|
+
const lineage = await buildArtifactLineageIndex({
|
|
241
|
+
targetPath: normalizedTargetPath,
|
|
242
|
+
outputDir,
|
|
243
|
+
env,
|
|
244
|
+
homeDir,
|
|
245
|
+
nowIso: normalizedNow,
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const sessionWorkItems = Array.isArray(lineage.workItems)
|
|
249
|
+
? lineage.workItems.filter(
|
|
250
|
+
(item) => normalizeString(item?.links?.sessionId) === normalizedSessionId
|
|
251
|
+
)
|
|
252
|
+
: [];
|
|
253
|
+
const verification = [];
|
|
254
|
+
|
|
255
|
+
for (const workItem of sessionWorkItems) {
|
|
256
|
+
const workItemId = normalizeString(workItem.workItemId);
|
|
257
|
+
if (!workItemId) {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
const closeoutPath = normalizeString(workItem?.artifacts?.closeoutPath);
|
|
261
|
+
const dateKey = normalizeDateKeyFromCloseoutPath(closeoutPath, normalizedNow);
|
|
262
|
+
const chain = await verifyArtifactChain({
|
|
263
|
+
workItemId,
|
|
264
|
+
date: dateKey,
|
|
265
|
+
targetPath: normalizedTargetPath,
|
|
266
|
+
outputDir,
|
|
267
|
+
env,
|
|
268
|
+
homeDir,
|
|
269
|
+
});
|
|
270
|
+
verification.push({
|
|
271
|
+
workItemId,
|
|
272
|
+
date: chain.date,
|
|
273
|
+
closeoutPath: closeoutPath || null,
|
|
274
|
+
closeoutAnchorSha256: normalizeString(workItem?.artifacts?.closeoutAnchorSha256) || null,
|
|
275
|
+
valid: chain.valid,
|
|
276
|
+
mismatchCount: Array.isArray(chain.mismatches) ? chain.mismatches.length : 0,
|
|
277
|
+
mismatches: Array.isArray(chain.mismatches) ? chain.mismatches : [],
|
|
278
|
+
});
|
|
279
|
+
if (!chain.valid) {
|
|
280
|
+
throw new Error(
|
|
281
|
+
`Artifact chain verification failed for work item '${workItemId}' (${dateKey}).`
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const analyticsSidecar = {
|
|
287
|
+
schemaVersion: "1.0.0",
|
|
288
|
+
generatedAt: normalizedNow,
|
|
289
|
+
sessionId: normalizedSessionId,
|
|
290
|
+
metrics: analytics,
|
|
291
|
+
};
|
|
292
|
+
const artifactChainSidecar = {
|
|
293
|
+
schemaVersion: "1.0.0",
|
|
294
|
+
generatedAt: normalizedNow,
|
|
295
|
+
sessionId: normalizedSessionId,
|
|
296
|
+
lineageRunId: normalizeString(lineage.lineageRunId) || null,
|
|
297
|
+
lineageIndexPath: toRelativePosix(
|
|
298
|
+
normalizedTargetPath,
|
|
299
|
+
normalizeString(lineage.indexPath)
|
|
300
|
+
) || null,
|
|
301
|
+
summary: {
|
|
302
|
+
totalWorkItemsIndexed: Number(lineage?.summary?.totalWorkItemsIndexed || 0),
|
|
303
|
+
sessionWorkItems: sessionWorkItems.length,
|
|
304
|
+
verifiedWorkItems: verification.filter((item) => item.valid).length,
|
|
305
|
+
},
|
|
306
|
+
workItems: verification,
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
return {
|
|
310
|
+
analyticsSidecar,
|
|
311
|
+
artifactChainSidecar,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
122
315
|
function normalizeSessionStatus(value) {
|
|
123
316
|
const normalized = normalizeString(value).toLowerCase();
|
|
124
317
|
if (normalized === SESSION_STATUS_EXPIRED) return SESSION_STATUS_EXPIRED;
|
|
@@ -147,6 +340,8 @@ function normalizeMetadata(raw = {}, { sessionId, targetPath, nowIso } = {}) {
|
|
|
147
340
|
s3Path: normalizeString(raw.s3Path) || null,
|
|
148
341
|
archiveStatus: normalizeString(raw.archiveStatus) || "pending",
|
|
149
342
|
codebaseContext: normalizeCodebaseContext(raw.codebaseContext || {}),
|
|
343
|
+
sharedResources: normalizeSharedResources(raw.sharedResources || {}, { nowIso }),
|
|
344
|
+
template: normalizeSessionTemplate(raw.template || null),
|
|
150
345
|
};
|
|
151
346
|
}
|
|
152
347
|
|
|
@@ -176,6 +371,8 @@ function buildSessionPayload(metadata, paths, nowIso = new Date().toISOString())
|
|
|
176
371
|
archivedAt: metadata.archivedAt,
|
|
177
372
|
s3Path: metadata.s3Path,
|
|
178
373
|
codebaseContext: metadata.codebaseContext,
|
|
374
|
+
sharedResources: metadata.sharedResources,
|
|
375
|
+
template: metadata.template,
|
|
179
376
|
};
|
|
180
377
|
}
|
|
181
378
|
|
|
@@ -208,6 +405,7 @@ async function saveMetadata(metadata, paths) {
|
|
|
208
405
|
export async function createSession({
|
|
209
406
|
targetPath = process.cwd(),
|
|
210
407
|
ttlSeconds = DEFAULT_TTL_SECONDS,
|
|
408
|
+
template = null,
|
|
211
409
|
} = {}) {
|
|
212
410
|
const resolvedTargetPath = path.resolve(String(targetPath || "."));
|
|
213
411
|
const normalizedTtlSeconds = normalizePositiveInteger(ttlSeconds, DEFAULT_TTL_SECONDS);
|
|
@@ -234,6 +432,8 @@ export async function createSession({
|
|
|
234
432
|
s3Path: null,
|
|
235
433
|
archiveStatus: "pending",
|
|
236
434
|
codebaseContext,
|
|
435
|
+
sharedResources: normalizeSharedResources({}, { nowIso }),
|
|
436
|
+
template: normalizeSessionTemplate(template),
|
|
237
437
|
},
|
|
238
438
|
{
|
|
239
439
|
sessionId,
|
|
@@ -349,7 +549,14 @@ export async function expireSession(sessionId, { targetPath = process.cwd() } =
|
|
|
349
549
|
|
|
350
550
|
export async function archiveSession(
|
|
351
551
|
sessionId,
|
|
352
|
-
{
|
|
552
|
+
{
|
|
553
|
+
s3Bucket,
|
|
554
|
+
s3Prefix = "",
|
|
555
|
+
targetPath = process.cwd(),
|
|
556
|
+
outputDir = "",
|
|
557
|
+
env,
|
|
558
|
+
homeDir,
|
|
559
|
+
} = {}
|
|
353
560
|
) {
|
|
354
561
|
const loaded = await loadMetadata(sessionId, { targetPath });
|
|
355
562
|
if (!loaded) {
|
|
@@ -363,6 +570,13 @@ export async function archiveSession(
|
|
|
363
570
|
const prefixSegment = normalizedPrefix ? `${normalizedPrefix}/` : "";
|
|
364
571
|
const s3Path = `s3://${normalizedBucket}/${prefixSegment}sessions/${loaded.paths.sessionId}/`;
|
|
365
572
|
const nowIso = new Date().toISOString();
|
|
573
|
+
const sidecars = await buildArchiveSidecars(loaded.paths.sessionId, {
|
|
574
|
+
targetPath: loaded.targetPath,
|
|
575
|
+
outputDir,
|
|
576
|
+
env,
|
|
577
|
+
homeDir,
|
|
578
|
+
nowIso,
|
|
579
|
+
});
|
|
366
580
|
|
|
367
581
|
loaded.metadata.status = SESSION_STATUS_ARCHIVED;
|
|
368
582
|
loaded.metadata.archivedAt = nowIso;
|
|
@@ -371,13 +585,115 @@ export async function archiveSession(
|
|
|
371
585
|
loaded.metadata.s3Path = s3Path;
|
|
372
586
|
const saved = await saveMetadata(loaded.metadata, loaded.paths);
|
|
373
587
|
|
|
588
|
+
await Promise.all([
|
|
589
|
+
writeJsonFile(path.join(loaded.paths.sessionDir, "analytics.json"), sidecars.analyticsSidecar),
|
|
590
|
+
writeJsonFile(
|
|
591
|
+
path.join(loaded.paths.sessionDir, "artifact-chain.json"),
|
|
592
|
+
sidecars.artifactChainSidecar
|
|
593
|
+
),
|
|
594
|
+
]);
|
|
374
595
|
await writeJsonFile(path.join(loaded.paths.sessionDir, "archive-manifest.json"), {
|
|
375
596
|
sessionId: loaded.paths.sessionId,
|
|
376
597
|
archivedAt: nowIso,
|
|
377
598
|
s3Path,
|
|
378
|
-
files: [
|
|
599
|
+
files: [
|
|
600
|
+
"metadata.json",
|
|
601
|
+
"stream.ndjson",
|
|
602
|
+
"stream.1.ndjson",
|
|
603
|
+
"agents/",
|
|
604
|
+
"analytics.json",
|
|
605
|
+
"artifact-chain.json",
|
|
606
|
+
],
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
return buildSessionPayload(saved, loaded.paths, nowIso);
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// Emit analytics.json + artifact-chain.json for a live session without archiving.
|
|
613
|
+
// Callers should invoke this on a timer (spec §PR 10 line 1451: "S3 archive
|
|
614
|
+
// carries analytics.json sidecar" + line 1452-1453: closeout.json observability
|
|
615
|
+
// invariant — mid-flight observability requires the sidecar on disk too).
|
|
616
|
+
// Safe to call frequently; the payload is idempotent per (sessionId, nowIso).
|
|
617
|
+
export async function persistSessionSidecarsSnapshot(
|
|
618
|
+
sessionId,
|
|
619
|
+
{
|
|
620
|
+
targetPath = process.cwd(),
|
|
621
|
+
outputDir = "",
|
|
622
|
+
env = process.env,
|
|
623
|
+
homeDir,
|
|
624
|
+
nowIso = new Date().toISOString(),
|
|
625
|
+
} = {}
|
|
626
|
+
) {
|
|
627
|
+
const loaded = await loadMetadata(sessionId, { targetPath });
|
|
628
|
+
if (!loaded) {
|
|
629
|
+
throw new Error(`Session '${sessionId}' was not found.`);
|
|
630
|
+
}
|
|
631
|
+
const sidecars = await buildArchiveSidecars(loaded.paths.sessionId, {
|
|
632
|
+
targetPath: loaded.targetPath,
|
|
633
|
+
outputDir,
|
|
634
|
+
env,
|
|
635
|
+
homeDir,
|
|
636
|
+
nowIso,
|
|
637
|
+
});
|
|
638
|
+
await Promise.all([
|
|
639
|
+
writeJsonFile(path.join(loaded.paths.sessionDir, "analytics.json"), sidecars.analyticsSidecar),
|
|
640
|
+
writeJsonFile(
|
|
641
|
+
path.join(loaded.paths.sessionDir, "artifact-chain.json"),
|
|
642
|
+
sidecars.artifactChainSidecar
|
|
643
|
+
),
|
|
644
|
+
]);
|
|
645
|
+
return {
|
|
646
|
+
sessionId: loaded.paths.sessionId,
|
|
647
|
+
analyticsSidecar: sidecars.analyticsSidecar,
|
|
648
|
+
artifactChainSidecar: sidecars.artifactChainSidecar,
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
export async function recordSessionProvisionedIdentities(
|
|
653
|
+
sessionId,
|
|
654
|
+
{ targetPath = process.cwd(), identityIds = [], tags = [] } = {}
|
|
655
|
+
) {
|
|
656
|
+
const loaded = await loadMetadata(sessionId, { targetPath });
|
|
657
|
+
if (!loaded) {
|
|
658
|
+
throw new Error(`Session '${sessionId}' was not found.`);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
const nowIso = new Date().toISOString();
|
|
662
|
+
const normalizedIdentityIds = normalizeStringList(identityIds);
|
|
663
|
+
if (normalizedIdentityIds.length === 0) {
|
|
664
|
+
return buildSessionPayload(loaded.metadata, loaded.paths, nowIso);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
const existingSharedResources = normalizeSharedResources(loaded.metadata.sharedResources || {}, {
|
|
668
|
+
nowIso,
|
|
379
669
|
});
|
|
670
|
+
const mergedIdentityIds = normalizeStringList([
|
|
671
|
+
...existingSharedResources.provisionedIdentityIds,
|
|
672
|
+
...normalizedIdentityIds,
|
|
673
|
+
]);
|
|
674
|
+
const mergedTags = normalizeStringList([
|
|
675
|
+
...existingSharedResources.provisioningTags,
|
|
676
|
+
...normalizeStringList(tags),
|
|
677
|
+
]);
|
|
678
|
+
|
|
679
|
+
loaded.metadata.sharedResources = normalizeSharedResources(
|
|
680
|
+
{
|
|
681
|
+
...existingSharedResources,
|
|
682
|
+
provisionedIdentityIds: mergedIdentityIds,
|
|
683
|
+
provisioningTags: mergedTags,
|
|
684
|
+
provisionCount:
|
|
685
|
+
normalizeNonNegativeInteger(existingSharedResources.provisionCount, 0) +
|
|
686
|
+
normalizedIdentityIds.length,
|
|
687
|
+
lastProvisionedAt: nowIso,
|
|
688
|
+
updatedAt: nowIso,
|
|
689
|
+
},
|
|
690
|
+
{ nowIso }
|
|
691
|
+
);
|
|
692
|
+
loaded.metadata.updatedAt = nowIso;
|
|
693
|
+
loaded.metadata.lastInteractionAt = nowIso;
|
|
694
|
+
loaded.metadata.status = SESSION_STATUS_ACTIVE;
|
|
380
695
|
|
|
696
|
+
const saved = await saveMetadata(loaded.metadata, loaded.paths);
|
|
381
697
|
return buildSessionPayload(saved, loaded.paths, nowIso);
|
|
382
698
|
}
|
|
383
699
|
|
package/src/session/stream.js
CHANGED
|
@@ -4,6 +4,8 @@ import { setTimeout as sleep } from "node:timers/promises";
|
|
|
4
4
|
|
|
5
5
|
import { createAgentEvent, normalizeAgentEvent } from "../events/schema.js";
|
|
6
6
|
import { resolveSessionPaths } from "./paths.js";
|
|
7
|
+
import { redactEventPayload } from "./redact.js";
|
|
8
|
+
import { syncSessionEventToApi } from "./sync.js";
|
|
7
9
|
|
|
8
10
|
const DEFAULT_POLL_MS = 500;
|
|
9
11
|
const DEFAULT_LOCK_TIMEOUT_MS = 10_000;
|
|
@@ -232,7 +234,8 @@ export async function appendToStream(
|
|
|
232
234
|
throw new Error(`Session '${paths.sessionId}' is expired and does not accept new events.`);
|
|
233
235
|
}
|
|
234
236
|
|
|
235
|
-
const
|
|
237
|
+
const rawEvent = materializeCanonicalEvent(paths.sessionId, event);
|
|
238
|
+
const canonicalEvent = redactEventPayload(rawEvent);
|
|
236
239
|
const nowIso = new Date().toISOString();
|
|
237
240
|
const normalizedMaxEvents = normalizePositiveInteger(maxEvents, DEFAULT_MAX_STREAM_EVENTS);
|
|
238
241
|
|
|
@@ -249,6 +252,11 @@ export async function appendToStream(
|
|
|
249
252
|
await releaseLock(paths.lockPath);
|
|
250
253
|
}
|
|
251
254
|
|
|
255
|
+
// Best-effort dashboard sync. Never block local stream durability on API state.
|
|
256
|
+
void syncSessionEventToApi(paths.sessionId, canonicalEvent, {
|
|
257
|
+
targetPath,
|
|
258
|
+
}).catch(() => {});
|
|
259
|
+
|
|
252
260
|
return canonicalEvent;
|
|
253
261
|
}
|
|
254
262
|
|