@awak-app/simy-cli 0.7.4 → 0.7.5

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.
Files changed (35) hide show
  1. package/README.md +47 -0
  2. package/package.json +2 -1
  3. package/src/cli-contract.js +1 -1
  4. package/src/codex-plugins/simy/hooks/consistency-handoff.mjs +2 -2
  5. package/src/codex-plugins/simy/hooks/delivery-loop.mjs +144 -59
  6. package/src/codex-plugins/simy/hooks/perspective-memory.mjs +115 -0
  7. package/src/codex-plugins/simy/hooks/purpose-cycle.mjs +43 -12
  8. package/src/codex-plugins/simy/hooks/purpose-evidence.mjs +31 -8
  9. package/src/codex-plugins/simy/hooks/purpose-search.mjs +1 -1
  10. package/src/codex-plugins/simy/hooks/state-limits.mjs +2 -0
  11. package/src/codex-plugins/simy/skills/delivery-loop/SKILL.md +23 -1
  12. package/src/codex-plugins/simy/skills/sqm-loop/SKILL.md +2 -0
  13. package/src/delivery-hooks/checkpoint-diagnostic.js +1 -0
  14. package/src/delivery-hooks/command.js +10 -2
  15. package/src/delivery-hooks/consistency-monitor.js +40 -16
  16. package/src/delivery-hooks/desktop.js +2 -2
  17. package/src/delivery-hooks/local-runtime.js +2 -2
  18. package/src/delivery-hooks/supervisor.js +7 -2
  19. package/src/desktop-host.js +7 -2
  20. package/src/session-supervision/codex-app-server.js +78 -35
  21. package/src/session-supervision/consistency-auditor.js +16 -8
  22. package/src/session-supervision/index.js +22 -1
  23. package/src/session-supervision/native-delegation.js +41 -0
  24. package/src/session-supervision/purpose-instruction.js +4 -2
  25. package/src/session-supervision/purpose-recovery.js +48 -16
  26. package/src/session-supervision/purpose-verification.js +32 -7
  27. package/src/session-supervision/redelegate-native.js +48 -0
  28. package/src/sqm/claude-plugin.js +2 -0
  29. package/src/sqm/command.js +10 -3
  30. package/src/sqm/hypothesis-knowledge.js +2 -1
  31. package/src/sqm/index.js +11 -5
  32. package/src/sqm/local-tests.js +155 -0
  33. package/src/sqm/proof.js +14 -3
  34. package/src/sqm/report.js +8 -3
  35. package/src/sqm/review-worker.js +16 -2
package/src/sqm/proof.js CHANGED
@@ -42,7 +42,8 @@ export function buildProof({ organizationId, repository, bundle, minCheck, evalu
42
42
  const enforcingFindings = findings.filter((finding) => finding.enforcement === "blocking");
43
43
  const statuses = enforcingModules.flatMap((module) => module.scenarios.map((scenario) => scenario.status));
44
44
  const failedModules = enforcingModules.filter((module) => module.scenarios.some((scenario) => scenario.status === "failed"));
45
- const hasFailure = enforcingFindings.length > 0 || failedModules.length > 0;
45
+ const localTests = evaluation.localTests;
46
+ const hasFailure = enforcingFindings.length > 0 || failedModules.length > 0 || Boolean(localTests && localTests.status !== "passed");
46
47
  const advisoryFindingCount = findings.length - enforcingFindings.length;
47
48
  const requiresHuman = statuses.includes("requires_human");
48
49
  const requiresPostDeploy = statuses.includes("requires_post_deploy");
@@ -68,14 +69,20 @@ export function buildProof({ organizationId, repository, bundle, minCheck, evalu
68
69
  status: minCheck.status,
69
70
  preliminary_finding_count: minCheck.preliminary_findings.length,
70
71
  },
71
- executions: evaluation.modules,
72
+ executions: [...evaluation.modules, ...(localTests ? [{
73
+ kind: 'local_test_profile', source: 'cli_catalog_repository_tests',
74
+ module_id: `LOCAL-TESTS:${localTests.profile}`, module_version: String(localTests.version),
75
+ enforcement: 'blocking', rule_results: [], scenarios: [], state_models: [], stressors: [],
76
+ local_tests: localTests,
77
+ }] : [])],
72
78
  findings,
73
79
  outcome: {
74
80
  status,
75
81
  reasons: [
76
82
  ...(hasFailure ? ["one_or_more_blocking_checks_failed"] : []),
77
83
  ...(advisoryFindingCount > 0 ? ["advisory_findings_observed"] : []),
78
- ...(evaluation.modules.length === 0 ? ["no_applicable_rules"] : []),
84
+ ...(evaluation.modules.length === 0 ? [localTests ? "no_applicable_cloud_rules" : "no_applicable_rules"] : []),
85
+ ...(localTests ? ["local_tests_executed"] : []),
79
86
  ...(bundle.modules?.length === 0 ? ["no_system_knowledge"] : []),
80
87
  ...(semanticResults.length ? ['semantic_assessments_advisory'] : []),
81
88
  ...(semanticResults.some(rule => rule.assessment.outcome !== 'scored') ? ['semantic_assessments_inconclusive'] : []),
@@ -141,3 +148,7 @@ export function verifySignedEvidence(proof, evidence) {
141
148
  function taggedDigest(value) {
142
149
  return `sha256:${createHash("sha256").update(canonicalize(value), "utf8").digest("hex")}`;
143
150
  }
151
+
152
+ export function localTestsFromProof(proof) {
153
+ return proof?.executions?.find(row => row.kind === 'local_test_profile')?.local_tests || null;
154
+ }
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."];
@@ -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;