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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { requireOutputFileSystem } from "../config/output-filesystem.js";
|
|
3
4
|
|
|
4
5
|
import { createMultiProviderApiClient } from "../ai/client.js";
|
|
5
6
|
import { serializeProxyError } from "../ai/proxy.js";
|
|
@@ -376,6 +377,7 @@ export function createIsolatedPersonaContext({
|
|
|
376
377
|
runId = "",
|
|
377
378
|
rootPath = ".",
|
|
378
379
|
artifactDir = "",
|
|
380
|
+
outputFs,
|
|
379
381
|
provider = null,
|
|
380
382
|
budget = {},
|
|
381
383
|
onEvent = null,
|
|
@@ -384,6 +386,7 @@ export function createIsolatedPersonaContext({
|
|
|
384
386
|
isolation = DEFAULT_ISOLATION_MODE,
|
|
385
387
|
eventContext = null,
|
|
386
388
|
} = {}) {
|
|
389
|
+
if (artifactDir) requireOutputFileSystem(outputFs);
|
|
387
390
|
const normalizedAgent = normalizeAuditPersona(agent || {});
|
|
388
391
|
const resolvedRootPath = path.resolve(String(rootPath || "."));
|
|
389
392
|
const resolvedRunId =
|
|
@@ -399,6 +402,7 @@ export function createIsolatedPersonaContext({
|
|
|
399
402
|
budget: loopBudget,
|
|
400
403
|
runId: resolvedRunId,
|
|
401
404
|
artifactDir,
|
|
405
|
+
outputFs,
|
|
402
406
|
onEvent,
|
|
403
407
|
});
|
|
404
408
|
const emitter = (event, payload = {}) => {
|
|
@@ -701,6 +705,7 @@ async function runSinglePersonaAgenticLoop({
|
|
|
701
705
|
sharedContext = null,
|
|
702
706
|
hybridContext = null,
|
|
703
707
|
artifactDir = "",
|
|
708
|
+
outputFs,
|
|
704
709
|
provider = null,
|
|
705
710
|
budget = {},
|
|
706
711
|
maxTurns = null,
|
|
@@ -722,6 +727,7 @@ async function runSinglePersonaAgenticLoop({
|
|
|
722
727
|
runId: requestedRunId,
|
|
723
728
|
rootPath: resolvedRootPath,
|
|
724
729
|
artifactDir,
|
|
730
|
+
outputFs,
|
|
725
731
|
provider,
|
|
726
732
|
budget,
|
|
727
733
|
onEvent,
|
|
@@ -1053,6 +1059,7 @@ function summarizeSwarmStatus(results = []) {
|
|
|
1053
1059
|
}
|
|
1054
1060
|
|
|
1055
1061
|
export async function runPersonaAgenticLoop(options = {}) {
|
|
1062
|
+
if (options.artifactDir) requireOutputFileSystem(options.outputFs);
|
|
1056
1063
|
const {
|
|
1057
1064
|
agent,
|
|
1058
1065
|
rootPath,
|
package/src/audit/replay.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { requireOutputFileSystem } from "../config/output-filesystem.js";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
4
|
function normalizeString(value) {
|
|
@@ -87,15 +87,17 @@ export function compareAuditReports(baseReport = {}, candidateReport = {}) {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
export async function writeAuditComparisonArtifact({
|
|
90
|
+
outputFs,
|
|
90
91
|
baseReport = {},
|
|
91
92
|
candidateReport = {},
|
|
92
93
|
outputDirectory = "",
|
|
93
94
|
} = {}) {
|
|
95
|
+
requireOutputFileSystem(outputFs);
|
|
94
96
|
const resolvedOutputDirectory = path.resolve(String(outputDirectory || "."));
|
|
95
97
|
const comparison = compareAuditReports(baseReport, candidateReport);
|
|
96
98
|
const fileName = `AUDIT_COMPARISON_${comparison.baseRunId}_vs_${comparison.candidateRunId}.json`;
|
|
97
99
|
const outputPath = path.join(resolvedOutputDirectory, fileName);
|
|
98
|
-
await
|
|
100
|
+
await outputFs.writeFile(outputPath, `${JSON.stringify(comparison, null, 2)}\n`, "utf-8");
|
|
99
101
|
return {
|
|
100
102
|
comparison,
|
|
101
103
|
outputPath,
|
package/src/auth/gate.js
CHANGED
|
@@ -17,6 +17,8 @@ import { isAuthenticated } from "./credential-destinations.js";
|
|
|
17
17
|
* - auth login, auth status, auth --help
|
|
18
18
|
* - --help, --version, -h, -v
|
|
19
19
|
* - config (read-only config inspection)
|
|
20
|
+
* - skills install/list (local packaged files only)
|
|
21
|
+
* - judge check (validates a local verdict file; sends nothing)
|
|
20
22
|
*
|
|
21
23
|
* All other commands require a valid session.
|
|
22
24
|
* If not authenticated, prints instructions and exits.
|
|
@@ -31,7 +33,7 @@ const AUTH_BYPASS_COMMANDS = new Set([
|
|
|
31
33
|
"-v",
|
|
32
34
|
]);
|
|
33
35
|
|
|
34
|
-
// Commands that work without auth (
|
|
36
|
+
// Commands that work without auth (local only, no API calls)
|
|
35
37
|
const NO_AUTH_REQUIRED = new Set([
|
|
36
38
|
"config", // local config inspection
|
|
37
39
|
]);
|
|
@@ -331,6 +333,25 @@ function isAuthenticatedSessionValid(session) {
|
|
|
331
333
|
return true;
|
|
332
334
|
}
|
|
333
335
|
|
|
336
|
+
/**
|
|
337
|
+
* A help request a newcomer can make before signing in (`sl session --help`, `sl session say --help`): the help flag
|
|
338
|
+
* is the LAST argument and every argument before it is a plain word (command, subcommand, positional). Then no option
|
|
339
|
+
* can take "--help" as its value (`--message-file --help` stays gated), so Commander prints help and runs nothing.
|
|
340
|
+
* The caller applies it only to Commander-dispatched commands. Tokens are judged exactly as Commander will see them,
|
|
341
|
+
* never trimmed: `" --help"` is message text to Commander, so it must not skip the gate either.
|
|
342
|
+
*
|
|
343
|
+
* @param {string[]} args - CLI arguments (after normalization)
|
|
344
|
+
* @returns {boolean}
|
|
345
|
+
*/
|
|
346
|
+
export function isHelpOnlyInvocation(args = []) {
|
|
347
|
+
const list = Array.isArray(args) ? args : [];
|
|
348
|
+
const last = list[list.length - 1];
|
|
349
|
+
if (last !== "--help" && last !== "-h") {
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
return list.slice(0, -1).every((arg) => typeof arg === "string" && arg !== "" && !arg.startsWith("-"));
|
|
353
|
+
}
|
|
354
|
+
|
|
334
355
|
/**
|
|
335
356
|
* Check if the current command requires authentication.
|
|
336
357
|
* Returns true if auth is required but user is not logged in.
|
|
@@ -349,6 +370,16 @@ export async function checkAuthGate(args) {
|
|
|
349
370
|
return { authenticated: true, session: null, bypassReason: "no_auth_required", failureReason: null };
|
|
350
371
|
}
|
|
351
372
|
|
|
373
|
+
// These two commands only inspect/copy packaged files. Future skills subcommands stay gated.
|
|
374
|
+
if (args[0] === "skills" && (args[1] === "install" || args[1] === "list")) {
|
|
375
|
+
return { authenticated: true, session: null, bypassReason: "local_skills", failureReason: null };
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Validating a verdict file is local; `judge verdict` sends, so it stays gated.
|
|
379
|
+
if (args[0] === "judge" && args[1] === "check") {
|
|
380
|
+
return { authenticated: true, session: null, bypassReason: "local_judge_check", failureReason: null };
|
|
381
|
+
}
|
|
382
|
+
|
|
352
383
|
if (isSessionNoAuthCommand(args)) {
|
|
353
384
|
return { authenticated: true, session: null, bypassReason: "session_no_auth_required", failureReason: null };
|
|
354
385
|
}
|
|
@@ -19,6 +19,19 @@ const SESSION_WARNING_ALLOWED_FIELDS = new Set([
|
|
|
19
19
|
]);
|
|
20
20
|
const emittedSessionWarningKeys = new Set();
|
|
21
21
|
let keytarClientOverrideForTests;
|
|
22
|
+
let sessionOperationTail = Promise.resolve();
|
|
23
|
+
|
|
24
|
+
// A store operation spans metadata, keyring and file publication/cleanup. Reads
|
|
25
|
+
// can migrate plaintext too. Keep that transaction in order within this module;
|
|
26
|
+
// serializing only rename would still permit stale migrations and logout races.
|
|
27
|
+
// One queue also covers path aliases and keyring accounts shared across homes.
|
|
28
|
+
// Other processes still rely on the existing key publication and bounded retries.
|
|
29
|
+
function runSessionOperation(operation) {
|
|
30
|
+
const result = sessionOperationTail.then(operation);
|
|
31
|
+
// A failed operation must not poison the queue or retain returned credentials.
|
|
32
|
+
sessionOperationTail = result.then(() => undefined, () => undefined);
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
22
35
|
|
|
23
36
|
function nowIso() {
|
|
24
37
|
return new Date().toISOString();
|
|
@@ -600,6 +613,11 @@ async function tryDecryptFileToken({ metadata, homeDir }) {
|
|
|
600
613
|
* }>}
|
|
601
614
|
*/
|
|
602
615
|
export async function readStoredSession({ homeDir } = {}) {
|
|
616
|
+
const options = { homeDir: resolveHomeDir(homeDir) };
|
|
617
|
+
return runSessionOperation(() => readStoredSessionUnlocked(options));
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
async function readStoredSessionUnlocked({ homeDir } = {}) {
|
|
603
621
|
const { filePath, metadata } = await readMetadata({ homeDir });
|
|
604
622
|
if (!metadata) {
|
|
605
623
|
return null;
|
|
@@ -711,6 +729,11 @@ export async function readStoredSession({ homeDir } = {}) {
|
|
|
711
729
|
* }>}
|
|
712
730
|
*/
|
|
713
731
|
export async function readStoredSessionMetadata({ homeDir } = {}) {
|
|
732
|
+
const options = { homeDir: resolveHomeDir(homeDir) };
|
|
733
|
+
return runSessionOperation(() => readStoredSessionMetadataUnlocked(options));
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
async function readStoredSessionMetadataUnlocked({ homeDir } = {}) {
|
|
714
737
|
const { filePath, metadata } = await readMetadata({ homeDir });
|
|
715
738
|
if (!metadata) {
|
|
716
739
|
return null;
|
|
@@ -776,6 +799,15 @@ export async function writeStoredSession(
|
|
|
776
799
|
aidenid = null,
|
|
777
800
|
} = {},
|
|
778
801
|
{ homeDir } = {}
|
|
802
|
+
) {
|
|
803
|
+
const session = { apiUrl, token, tokenId, tokenPrefix, tokenExpiresAt, user, aidenid };
|
|
804
|
+
const options = { homeDir: resolveHomeDir(homeDir) };
|
|
805
|
+
return runSessionOperation(() => writeStoredSessionUnlocked(session, options));
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
async function writeStoredSessionUnlocked(
|
|
809
|
+
{ apiUrl, token, tokenId, tokenPrefix, tokenExpiresAt, user, aidenid },
|
|
810
|
+
{ homeDir }
|
|
779
811
|
) {
|
|
780
812
|
const normalizedApiUrl = String(apiUrl || "").trim();
|
|
781
813
|
const normalizedToken = String(token || "").trim();
|
|
@@ -838,6 +870,11 @@ export async function writeStoredSession(
|
|
|
838
870
|
* @returns {Promise<{ filePath: string, hadSession: boolean }>}
|
|
839
871
|
*/
|
|
840
872
|
export async function clearStoredSession({ homeDir } = {}) {
|
|
873
|
+
const options = { homeDir: resolveHomeDir(homeDir) };
|
|
874
|
+
return runSessionOperation(() => clearStoredSessionUnlocked(options));
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
async function clearStoredSessionUnlocked({ homeDir } = {}) {
|
|
841
878
|
const { filePath, metadata } = await readMetadata({ homeDir });
|
|
842
879
|
if (metadata && metadata.storage === "keyring") {
|
|
843
880
|
const keytar = await loadKeytarClient({ allowImplicit: true });
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* refused with an Error, which the gate reports as cannot-verify.
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
|
-
import {
|
|
33
|
+
import { ghCheckRunsApiPath, ghCompareApiPath, ghRepoSlug } from "../net/gh-api-path.js";
|
|
34
34
|
|
|
35
35
|
/** Shape a resolver must return. Anything else is treated as indeterminate. */
|
|
36
36
|
const YES = true;
|
|
@@ -74,8 +74,7 @@ export function createEvidenceResolver({ run, probeDeployedSha } = {}) {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
async function shaOnBranch({ repo, sha, branch }) {
|
|
77
|
-
const
|
|
78
|
-
const res = await run(["api", `repos/${ghRepoSlug(repo)}/compare/${basehead}`, "--jq", ".status"]);
|
|
77
|
+
const res = await run(["api", ghCompareApiPath({ repo, branch, sha }), "--jq", ".status"]);
|
|
79
78
|
if (!res || !res.ok) return UNKNOWN;
|
|
80
79
|
const status = String(res.stdout ?? "").trim();
|
|
81
80
|
// GitHub's compare status, base=branch head=sha:
|
|
@@ -87,7 +86,7 @@ export function createEvidenceResolver({ run, probeDeployedSha } = {}) {
|
|
|
87
86
|
}
|
|
88
87
|
|
|
89
88
|
async function checkConclusive({ repo, ref, check }) {
|
|
90
|
-
const res = await run(["api",
|
|
89
|
+
const res = await run(["api", ghCheckRunsApiPath({ repo, ref }), "--jq", ".check_runs"]);
|
|
91
90
|
if (!res || !res.ok) return UNKNOWN;
|
|
92
91
|
const runs = parseJson(res.stdout);
|
|
93
92
|
if (!Array.isArray(runs)) return UNKNOWN;
|
package/src/cli.js
CHANGED
|
@@ -5,6 +5,16 @@ import { CLI_VERSION, runLegacyCliWithErrorHandling } from "./legacy-cli.js";
|
|
|
5
5
|
import { restrictProgramLookupToPath } from "./program-lookup.js";
|
|
6
6
|
|
|
7
7
|
const COMMAND_REGISTRARS = {
|
|
8
|
+
skills: {
|
|
9
|
+
loader: () => import("./commands/skills.js"),
|
|
10
|
+
exportName: "registerSkillsCommand",
|
|
11
|
+
needsLegacy: false,
|
|
12
|
+
},
|
|
13
|
+
judge: {
|
|
14
|
+
loader: () => import("./commands/judge.js"),
|
|
15
|
+
exportName: "registerJudgeCommand",
|
|
16
|
+
needsLegacy: false,
|
|
17
|
+
},
|
|
8
18
|
init: {
|
|
9
19
|
loader: () => import("./commands/init.js"),
|
|
10
20
|
exportName: "registerInitCommand",
|
|
@@ -262,9 +272,11 @@ export async function runCli(rawArgs = process.argv.slice(2)) {
|
|
|
262
272
|
// Normalize slash commands (/omargate → omargate, /audit → audit, etc.)
|
|
263
273
|
const normalizedArgs = normalizeSlashArgs(rawArgs);
|
|
264
274
|
|
|
265
|
-
// Auth gate — require login for all commands except auth/help/version/config
|
|
266
|
-
|
|
267
|
-
const
|
|
275
|
+
// Auth gate — require login for all commands except auth/help/version/config. Help for a Commander command prints
|
|
276
|
+
// without login too: Commander prints it and runs nothing (a newcomer reads `sl session --help` first).
|
|
277
|
+
const { checkAuthGate, isHelpOnlyInvocation, printAuthRequired } = await import("./auth/gate.js");
|
|
278
|
+
const helpOnly = !shouldBypassCommander(normalizedArgs) && isHelpOnlyInvocation(normalizedArgs);
|
|
279
|
+
const authResult = helpOnly ? { authenticated: true } : await checkAuthGate(normalizedArgs);
|
|
268
280
|
if (!authResult.authenticated) {
|
|
269
281
|
printAuthRequired(authResult.failureReason);
|
|
270
282
|
return;
|
|
@@ -272,6 +284,14 @@ export async function runCli(rawArgs = process.argv.slice(2)) {
|
|
|
272
284
|
|
|
273
285
|
if (shouldBypassCommander(normalizedArgs)) {
|
|
274
286
|
await runLegacyCliWithErrorHandling(normalizedArgs);
|
|
287
|
+
if (["--help", "-h", "help"].includes(normalizedArgs[0])) {
|
|
288
|
+
console.log("\nAgent Skills:");
|
|
289
|
+
console.log(" sl skills install [names...] [--dir <path>] [--force] [--json] Install bundled Senti skills (all, or the named ones)");
|
|
290
|
+
console.log(" sl skills list [--dir <path>] [--json] Show bundled and installed versions/hashes");
|
|
291
|
+
console.log("\nJudge Loop:");
|
|
292
|
+
console.log(" sl judge check --file <verdict.json> [--json] Validate a verdict offline and preview the message");
|
|
293
|
+
console.log(" sl judge verdict <sessionId> --agent <id> --file <verdict.json> Send the verdict to the coder");
|
|
294
|
+
}
|
|
275
295
|
return;
|
|
276
296
|
}
|
|
277
297
|
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
recordProvisionedIdentity,
|
|
23
23
|
updateIdentityStatus,
|
|
24
24
|
} from "../../ai/identity-store.js";
|
|
25
|
-
import { resolveOutputRoot } from "../../config/service.js";
|
|
25
|
+
import { resolveOutputRoot, resolveOutputContext } from "../../config/service.js";
|
|
26
26
|
import {
|
|
27
27
|
delay,
|
|
28
28
|
hasExtractionSignal,
|
|
@@ -145,7 +145,7 @@ legalHold
|
|
|
145
145
|
.action(async (identityId, options, command) => {
|
|
146
146
|
const emitJson = shouldEmitJson(options, command);
|
|
147
147
|
const targetPath = path.resolve(process.cwd(), String(options.path || "."));
|
|
148
|
-
const outputRoot = await
|
|
148
|
+
const { outputRoot, outputFs } = await resolveOutputContext({
|
|
149
149
|
cwd: targetPath,
|
|
150
150
|
outputDirOverride: options.outputDir,
|
|
151
151
|
env: process.env,
|
|
@@ -172,7 +172,7 @@ legalHold
|
|
|
172
172
|
status,
|
|
173
173
|
underHold,
|
|
174
174
|
identity: identityRecord,
|
|
175
|
-
});
|
|
175
|
+
}, outputFs);
|
|
176
176
|
|
|
177
177
|
const payload = {
|
|
178
178
|
command: "ai identity legal-hold status",
|
|
@@ -204,7 +204,7 @@ identity
|
|
|
204
204
|
.action(async (options, command) => {
|
|
205
205
|
const emitJson = shouldEmitJson(options, command);
|
|
206
206
|
const targetPath = path.resolve(process.cwd(), String(options.path || "."));
|
|
207
|
-
const outputRoot = await
|
|
207
|
+
const { outputRoot, outputFs } = await resolveOutputContext({
|
|
208
208
|
cwd: targetPath,
|
|
209
209
|
outputDirOverride: options.outputDir,
|
|
210
210
|
env: process.env,
|
|
@@ -223,7 +223,7 @@ identity
|
|
|
223
223
|
staleCount: staleScan.stale.length,
|
|
224
224
|
totalCount: staleScan.identities.length,
|
|
225
225
|
identities,
|
|
226
|
-
});
|
|
226
|
+
}, outputFs);
|
|
227
227
|
|
|
228
228
|
const payload = {
|
|
229
229
|
command: "ai identity audit",
|
|
@@ -269,7 +269,7 @@ identity
|
|
|
269
269
|
throw new Error("At least one tag is required via --tags.");
|
|
270
270
|
}
|
|
271
271
|
const targetPath = path.resolve(process.cwd(), String(options.path || "."));
|
|
272
|
-
const outputRoot = await
|
|
272
|
+
const { outputRoot, outputFs } = await resolveOutputContext({
|
|
273
273
|
cwd: targetPath,
|
|
274
274
|
outputDirOverride: options.outputDir,
|
|
275
275
|
env: process.env,
|
|
@@ -293,7 +293,7 @@ identity
|
|
|
293
293
|
candidateIdentityIds: candidates.map((item) => item.identityId),
|
|
294
294
|
blockedIdentityIds: blocked.map((item) => item.identityId),
|
|
295
295
|
eligibleIdentityIds: eligible.map((item) => item.identityId),
|
|
296
|
-
});
|
|
296
|
+
}, outputFs);
|
|
297
297
|
|
|
298
298
|
if (!options.execute) {
|
|
299
299
|
const payload = {
|
|
@@ -345,6 +345,7 @@ identity
|
|
|
345
345
|
revokeResponse = revokeExecution.response || null;
|
|
346
346
|
}
|
|
347
347
|
const updated = await updateIdentityStatus({
|
|
348
|
+
outputFs,
|
|
348
349
|
outputRoot,
|
|
349
350
|
identityId: identityRecord.identityId,
|
|
350
351
|
status: "SQUASHED",
|
|
@@ -379,7 +380,7 @@ identity
|
|
|
379
380
|
updated: updates,
|
|
380
381
|
apiCalled: canCallApi,
|
|
381
382
|
credentialsMissing: resolvedCredentials.missing,
|
|
382
|
-
});
|
|
383
|
+
}, outputFs);
|
|
383
384
|
|
|
384
385
|
const payload = {
|
|
385
386
|
command: "ai identity kill-all",
|
|
@@ -426,7 +427,7 @@ identity
|
|
|
426
427
|
.action(async (identityId, options, command) => {
|
|
427
428
|
const emitJson = shouldEmitJson(options, command);
|
|
428
429
|
const targetPath = path.resolve(process.cwd(), String(options.path || "."));
|
|
429
|
-
const outputRoot = await
|
|
430
|
+
const { outputRoot, outputFs } = await resolveOutputContext({
|
|
430
431
|
cwd: targetPath,
|
|
431
432
|
outputDirOverride: options.outputDir,
|
|
432
433
|
env: process.env,
|
|
@@ -441,7 +442,7 @@ identity
|
|
|
441
442
|
apiUrl,
|
|
442
443
|
idempotencyKey,
|
|
443
444
|
identityId,
|
|
444
|
-
});
|
|
445
|
+
}, outputFs);
|
|
445
446
|
|
|
446
447
|
const { registryPath, identity: trackedIdentity } = await getIdentityById({
|
|
447
448
|
outputRoot,
|
|
@@ -472,7 +473,7 @@ identity
|
|
|
472
473
|
credentialsMissing: resolvedCredentials.missing,
|
|
473
474
|
trackedIdentity,
|
|
474
475
|
curlPreview: [
|
|
475
|
-
`curl -X POST ${apiUrl}/v1/identities/${urlPathSegment(identityId, { label: "identityId" })}/
|
|
476
|
+
`curl -X POST ${apiUrl}/v1/identities/${urlPathSegment(identityId, { label: "identityId" })}/squash \\`,
|
|
476
477
|
` -H \"Authorization: Bearer $AIDENID_API_KEY\" \\`,
|
|
477
478
|
` -H \"X-Org-Id: $AIDENID_ORG_ID\" \\`,
|
|
478
479
|
` -H \"X-Project-Id: $AIDENID_PROJECT_ID\" \\`,
|
|
@@ -517,10 +518,11 @@ identity
|
|
|
517
518
|
apiUrl,
|
|
518
519
|
idempotencyKey,
|
|
519
520
|
response: execution.response,
|
|
520
|
-
});
|
|
521
|
+
}, outputFs);
|
|
521
522
|
|
|
522
523
|
const revokedAt = String(execution.response?.revokedAt || "").trim() || new Date().toISOString();
|
|
523
524
|
const updated = await updateIdentityStatus({
|
|
525
|
+
outputFs,
|
|
524
526
|
outputRoot,
|
|
525
527
|
identityId,
|
|
526
528
|
status: String(execution.response?.status || "REVOKED"),
|
|
@@ -576,7 +578,7 @@ identity
|
|
|
576
578
|
.action(async (parentIdentityId, options, command) => {
|
|
577
579
|
const emitJson = shouldEmitJson(options, command);
|
|
578
580
|
const targetPath = path.resolve(process.cwd(), String(options.path || "."));
|
|
579
|
-
const outputRoot = await
|
|
581
|
+
const { outputRoot, outputFs } = await resolveOutputContext({
|
|
580
582
|
cwd: targetPath,
|
|
581
583
|
outputDirOverride: options.outputDir,
|
|
582
584
|
env: process.env,
|
|
@@ -617,7 +619,7 @@ identity
|
|
|
617
619
|
idempotencyKey,
|
|
618
620
|
parentIdentityId,
|
|
619
621
|
payload,
|
|
620
|
-
});
|
|
622
|
+
}, outputFs);
|
|
621
623
|
|
|
622
624
|
const resolvedCredentials = await resolveAidenIdCredentials({
|
|
623
625
|
apiKey: options.apiKey,
|
|
@@ -695,9 +697,10 @@ identity
|
|
|
695
697
|
idempotencyKey,
|
|
696
698
|
parentIdentityId,
|
|
697
699
|
response: execution.response,
|
|
698
|
-
});
|
|
700
|
+
}, outputFs);
|
|
699
701
|
|
|
700
702
|
const registryUpdate = await recordProvisionedIdentity({
|
|
703
|
+
outputFs,
|
|
701
704
|
outputRoot,
|
|
702
705
|
response: execution.response || {},
|
|
703
706
|
context: {
|
|
@@ -758,7 +761,7 @@ identity
|
|
|
758
761
|
.action(async (identityId, options, command) => {
|
|
759
762
|
const emitJson = shouldEmitJson(options, command);
|
|
760
763
|
const targetPath = path.resolve(process.cwd(), String(options.path || "."));
|
|
761
|
-
const outputRoot = await
|
|
764
|
+
const { outputRoot, outputFs } = await resolveOutputContext({
|
|
762
765
|
cwd: targetPath,
|
|
763
766
|
outputDirOverride: options.outputDir,
|
|
764
767
|
env: process.env,
|
|
@@ -796,7 +799,7 @@ identity
|
|
|
796
799
|
generatedAt: new Date().toISOString(),
|
|
797
800
|
identityId,
|
|
798
801
|
response: execution.response,
|
|
799
|
-
});
|
|
802
|
+
}, outputFs);
|
|
800
803
|
|
|
801
804
|
const rows = renderLineageRows({
|
|
802
805
|
nodes: execution.nodes,
|
|
@@ -843,7 +846,7 @@ identity
|
|
|
843
846
|
.action(async (identityId, options, command) => {
|
|
844
847
|
const emitJson = shouldEmitJson(options, command);
|
|
845
848
|
const targetPath = path.resolve(process.cwd(), String(options.path || "."));
|
|
846
|
-
const outputRoot = await
|
|
849
|
+
const { outputRoot, outputFs } = await resolveOutputContext({
|
|
847
850
|
cwd: targetPath,
|
|
848
851
|
outputDirOverride: options.outputDir,
|
|
849
852
|
env: process.env,
|
|
@@ -865,7 +868,7 @@ identity
|
|
|
865
868
|
apiUrl,
|
|
866
869
|
idempotencyKey,
|
|
867
870
|
identityId,
|
|
868
|
-
});
|
|
871
|
+
}, outputFs);
|
|
869
872
|
|
|
870
873
|
const resolvedCredentials = await resolveAidenIdCredentials({
|
|
871
874
|
apiKey: options.apiKey,
|
|
@@ -937,12 +940,13 @@ identity
|
|
|
937
940
|
idempotencyKey,
|
|
938
941
|
identityId,
|
|
939
942
|
response: execution.response,
|
|
940
|
-
});
|
|
943
|
+
}, outputFs);
|
|
941
944
|
|
|
942
945
|
const updateResults = await Promise.all(
|
|
943
946
|
execution.revokedIdentityIds.map(async (revokedIdentityId) => {
|
|
944
947
|
try {
|
|
945
948
|
const updated = await updateIdentityStatus({
|
|
949
|
+
outputFs,
|
|
946
950
|
outputRoot,
|
|
947
951
|
identityId: revokedIdentityId,
|
|
948
952
|
status: "SQUASHED",
|
|
@@ -1003,7 +1007,7 @@ identity
|
|
|
1003
1007
|
.action(async (identityId, options, command) => {
|
|
1004
1008
|
const emitJson = shouldEmitJson(options, command);
|
|
1005
1009
|
const targetPath = path.resolve(process.cwd(), String(options.path || "."));
|
|
1006
|
-
const outputRoot = await
|
|
1010
|
+
const { outputRoot, outputFs } = await resolveOutputContext({
|
|
1007
1011
|
cwd: targetPath,
|
|
1008
1012
|
outputDirOverride: options.outputDir,
|
|
1009
1013
|
env: process.env,
|
|
@@ -1048,7 +1052,7 @@ identity
|
|
|
1048
1052
|
cursor: String(options.cursor || "").trim() || null,
|
|
1049
1053
|
limit,
|
|
1050
1054
|
response: execution.response,
|
|
1051
|
-
});
|
|
1055
|
+
}, outputFs);
|
|
1052
1056
|
|
|
1053
1057
|
const payload = {
|
|
1054
1058
|
command: "ai identity events",
|
|
@@ -1096,7 +1100,7 @@ identity
|
|
|
1096
1100
|
.action(async (identityId, options, command) => {
|
|
1097
1101
|
const emitJson = shouldEmitJson(options, command);
|
|
1098
1102
|
const targetPath = path.resolve(process.cwd(), String(options.path || "."));
|
|
1099
|
-
const outputRoot = await
|
|
1103
|
+
const { outputRoot, outputFs } = await resolveOutputContext({
|
|
1100
1104
|
cwd: targetPath,
|
|
1101
1105
|
outputDirOverride: options.outputDir,
|
|
1102
1106
|
env: process.env,
|
|
@@ -1149,7 +1153,7 @@ identity
|
|
|
1149
1153
|
latestEvent,
|
|
1150
1154
|
extraction: latestExtraction.extraction,
|
|
1151
1155
|
extractionResponse: latestExtraction.response,
|
|
1152
|
-
});
|
|
1156
|
+
}, outputFs);
|
|
1153
1157
|
|
|
1154
1158
|
const payload = {
|
|
1155
1159
|
command: "ai identity latest",
|
|
@@ -1207,7 +1211,7 @@ identity
|
|
|
1207
1211
|
.action(async (identityId, options, command) => {
|
|
1208
1212
|
const emitJson = shouldEmitJson(options, command);
|
|
1209
1213
|
const targetPath = path.resolve(process.cwd(), String(options.path || "."));
|
|
1210
|
-
const outputRoot = await
|
|
1214
|
+
const { outputRoot, outputFs } = await resolveOutputContext({
|
|
1211
1215
|
cwd: targetPath,
|
|
1212
1216
|
outputDirOverride: options.outputDir,
|
|
1213
1217
|
env: process.env,
|
|
@@ -1309,7 +1313,7 @@ identity
|
|
|
1309
1313
|
extraction: lastExtraction,
|
|
1310
1314
|
},
|
|
1311
1315
|
};
|
|
1312
|
-
await writeArtifact(outputPath, artifactPayload);
|
|
1316
|
+
await writeArtifact(outputPath, artifactPayload, outputFs);
|
|
1313
1317
|
|
|
1314
1318
|
if (!success) {
|
|
1315
1319
|
throw new Error(
|