@awak-app/simy-cli 0.7.4 → 0.7.6
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 +51 -0
- package/package.json +2 -1
- package/src/cli-contract.js +1 -1
- package/src/codex-plugins/simy/hooks/consistency-handoff.mjs +2 -2
- package/src/codex-plugins/simy/hooks/delivery-loop.mjs +144 -59
- package/src/codex-plugins/simy/hooks/perspective-memory.mjs +115 -0
- package/src/codex-plugins/simy/hooks/purpose-cycle.mjs +43 -12
- package/src/codex-plugins/simy/hooks/purpose-evidence.mjs +31 -8
- package/src/codex-plugins/simy/hooks/purpose-search.mjs +2 -1
- package/src/codex-plugins/simy/hooks/state-limits.mjs +2 -0
- package/src/codex-plugins/simy/skills/delivery-loop/SKILL.md +23 -1
- package/src/codex-plugins/simy/skills/sqm-loop/SKILL.md +2 -0
- package/src/delivery-hooks/checkpoint-diagnostic.js +1 -0
- package/src/delivery-hooks/command.js +10 -2
- package/src/delivery-hooks/consistency-monitor.js +71 -22
- package/src/delivery-hooks/desktop.js +2 -2
- package/src/delivery-hooks/local-runtime.js +2 -2
- package/src/delivery-hooks/supervisor.js +15 -4
- package/src/desktop-host.js +7 -2
- package/src/session-supervision/codex-app-server.js +84 -36
- package/src/session-supervision/consistency-auditor.js +87 -34
- package/src/session-supervision/index.js +22 -1
- package/src/session-supervision/native-delegation.js +86 -0
- package/src/session-supervision/purpose-instruction.js +4 -2
- package/src/session-supervision/purpose-recovery.js +48 -16
- package/src/session-supervision/purpose-verification.js +95 -10
- package/src/session-supervision/redelegate-native.js +48 -0
- package/src/sqm/claude-plugin.js +2 -0
- package/src/sqm/command.js +10 -3
- package/src/sqm/hypothesis-knowledge.js +2 -1
- package/src/sqm/index.js +11 -5
- package/src/sqm/local-tests.js +155 -0
- package/src/sqm/proof.js +14 -3
- package/src/sqm/report.js +8 -3
- package/src/sqm/review-worker.js +16 -2
package/src/sqm/report.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { localTestsFromProof } from './proof.js';
|
|
2
|
+
|
|
1
3
|
// Presentation only: retain the signed proof and enforcement outcome unchanged.
|
|
2
4
|
export function formatSqmCoverage(proof) {
|
|
3
|
-
const modules = proof.executions;
|
|
5
|
+
const modules = proof.executions.filter(row => row.kind !== 'local_test_profile');
|
|
6
|
+
const local = localTestsFromProof(proof);
|
|
4
7
|
const rules = modules.flatMap(module => module.rule_results);
|
|
5
8
|
const scenarios = modules.flatMap(module => module.scenarios);
|
|
6
9
|
const blocking = modules.filter(module => module.enforcement === "blocking").length;
|
|
@@ -13,10 +16,11 @@ export function formatSqmCoverage(proof) {
|
|
|
13
16
|
`SQM scope: ${modules.length} module(s) · ${rules.length} rule(s) · ${scenarios.length} scenario evaluation(s)`,
|
|
14
17
|
`Enforcement: ${blocking} blocking module(s) · ${advisory} advisory module(s)`,
|
|
15
18
|
];
|
|
19
|
+
if (local) lines.push(`Local tests: ${local.executions.length}/${local.planned_suite_count ?? local.executions.length} suite(s) · ${local.executions.reduce((n, r) => n + (r.counts?.tests || 0), 0)} actual tests · ${local.status}. Cloud rules and repository test execution are separate.`);
|
|
16
20
|
if (scenarios.length) lines.push(`Scenario evaluations: ${[...statusCounts].map(([status, count]) => `${status}=${count}`).join(" · ")}`);
|
|
17
21
|
if (proof.outcome.reasons.length) lines.push(`Outcome reasons: ${proof.outcome.reasons.join(", ")}`);
|
|
18
22
|
lines.push(...sqmCoverageWarnings({ moduleCount: modules.length, scenarioCount: scenarios.length,
|
|
19
|
-
noSystemKnowledge: proof.outcome.reasons.includes("no_system_knowledge"), outcomeStatus: proof.outcome.status }));
|
|
23
|
+
noSystemKnowledge: proof.outcome.reasons.includes("no_system_knowledge"), outcomeStatus: proof.outcome.status, localTests: local }));
|
|
20
24
|
if (modules.length && !blocking) lines.push("Advisory-only scope: findings do not block this outcome.");
|
|
21
25
|
const semantic = rules.filter(rule => rule.assessment).map(rule => rule.assessment);
|
|
22
26
|
if (semantic.length) lines.push(`Semantic assessments: ${semantic.filter(result => result.executed).length} executed · ${semantic.filter(result => result.outcome === 'scored').length} scored · ${semantic.filter(result => result.outcome !== 'scored').length} inconclusive. Local model judgments are advisory and do not establish independent acceptance.`);
|
|
@@ -24,7 +28,8 @@ export function formatSqmCoverage(proof) {
|
|
|
24
28
|
return lines.join("\n");
|
|
25
29
|
}
|
|
26
30
|
|
|
27
|
-
export function sqmCoverageWarnings({ moduleCount, scenarioCount, noSystemKnowledge, outcomeStatus = "passed" }) {
|
|
31
|
+
export function sqmCoverageWarnings({ moduleCount, scenarioCount, noSystemKnowledge, outcomeStatus = "passed", localTests }) {
|
|
32
|
+
if (localTests && !moduleCount) return ["Warning: no applicable Cloud rules were evaluated. Local regression test results are reported separately; they do not establish deployed acceptance."];
|
|
28
33
|
if (noSystemKnowledge) return [`Warning: the selected system/repository's downloaded SQM knowledge bundle has no published knowledge. ${outcomeStatus === "passed" ? "This is a pass with 0 evaluated cases" : "This result has 0 evaluated cases"}; it does not establish feature or historical incident coverage.`];
|
|
29
34
|
if (!moduleCount) return ["Warning: no applicable rules matched this change (0 evaluated cases). This result does not establish feature or historical incident coverage; it does not mean the system has no past knowledge."];
|
|
30
35
|
if (!scenarioCount) return ["Warning: No scenario evaluations: rule results alone do not establish scenario coverage."];
|
package/src/sqm/review-worker.js
CHANGED
|
@@ -442,6 +442,8 @@ async function runSingleProviderReview(
|
|
|
442
442
|
subscriptionOnly = false,
|
|
443
443
|
authStatusProbe,
|
|
444
444
|
beforeAutomaticRetry = async () => {},
|
|
445
|
+
onUsage = () => {},
|
|
446
|
+
onContext = () => {},
|
|
445
447
|
} = {},
|
|
446
448
|
) {
|
|
447
449
|
assertProvider(provider);
|
|
@@ -508,7 +510,7 @@ async function runSingleProviderReview(
|
|
|
508
510
|
],
|
|
509
511
|
prompt,
|
|
510
512
|
{ spawnImpl, executionTimeoutMs: remaining, stallTimeoutMs: provider === "codex" && readOnlyAnalysis && allowAutomaticRetry && !stallRetried && remaining > codexAttemptTimeoutMs ? codexAttemptTimeoutMs : null, label, cwd: workDir, environment, provider, signal, readOnlyAnalysis, model, retryAttempt,
|
|
511
|
-
terminationGraceMs: Number.isInteger(terminationGraceMs) ? Math.max(1, Math.min(terminationGraceMs, 5_000)) : 1_000 },
|
|
513
|
+
terminationGraceMs: Number.isInteger(terminationGraceMs) ? Math.max(1, Math.min(terminationGraceMs, 5_000)) : 1_000, onUsage, onContext },
|
|
512
514
|
);
|
|
513
515
|
selectedModel = model;
|
|
514
516
|
break;
|
|
@@ -580,7 +582,7 @@ function codexAnalysisArguments(system) {
|
|
|
580
582
|
'-c', `developer_instructions=${JSON.stringify(system)}`];
|
|
581
583
|
}
|
|
582
584
|
|
|
583
|
-
function spawnReview(command, args, prompt, { spawnImpl, executionTimeoutMs, stallTimeoutMs, cwd, environment, provider, signal, readOnlyAnalysis, terminationGraceMs, model, retryAttempt }) {
|
|
585
|
+
function spawnReview(command, args, prompt, { spawnImpl, executionTimeoutMs, stallTimeoutMs, cwd, environment, provider, signal, readOnlyAnalysis, terminationGraceMs, model, retryAttempt, onUsage, onContext }) {
|
|
584
586
|
return new Promise((resolve, reject) => {
|
|
585
587
|
if (signal?.aborted) return reject(providerFailure("analysis_cancelled"));
|
|
586
588
|
let child;
|
|
@@ -591,6 +593,7 @@ function spawnReview(command, args, prompt, { spawnImpl, executionTimeoutMs, sta
|
|
|
591
593
|
} catch { reject(providerFailure("provider_spawn_failed", { model })); return; }
|
|
592
594
|
let stdout = "", stderrPending = "", outputBytes = 0, phase = "before_turn";
|
|
593
595
|
let settled = false, terminalError = null, killTimer = null;
|
|
596
|
+
let reportedTokens = -1, reportedContext = null;
|
|
594
597
|
let diagnostic = safeProviderDiagnostic({ model, retryAttempt });
|
|
595
598
|
const halt = error => {
|
|
596
599
|
if (settled || terminalError) return;
|
|
@@ -653,6 +656,17 @@ function spawnReview(command, args, prompt, { spawnImpl, executionTimeoutMs, sta
|
|
|
653
656
|
for (const line of stdout.split("\n").filter(Boolean)) {
|
|
654
657
|
let event;
|
|
655
658
|
try { event = JSON.parse(line); } catch { continue; }
|
|
659
|
+
try {
|
|
660
|
+
if (event.type === 'thread.started' && typeof event.thread_id === 'string' && event.thread_id !== reportedContext) {
|
|
661
|
+
reportedContext = event.thread_id; onContext(reportedContext);
|
|
662
|
+
}
|
|
663
|
+
if (event.type === 'turn.completed') {
|
|
664
|
+
const input = event.usage?.input_tokens, output = event.usage?.output_tokens, tokens = input + output;
|
|
665
|
+
if ([input, output, tokens].every(value => Number.isSafeInteger(value) && value >= 0) && tokens > reportedTokens) {
|
|
666
|
+
reportedTokens = tokens; onUsage({ source: 'codex_exec', tokens, observedAt: Date.now() });
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
} catch { halt(providerFailure('provider_execution_error')); return; }
|
|
656
670
|
if (event.type === "turn.started") observed = observed === "before_turn" ? "turn_started" : observed;
|
|
657
671
|
else if (observed !== "before_turn" && ["turn.completed", "item.completed"].includes(event.type)) observed = "turn_output";
|
|
658
672
|
phase = observed;
|