sneakoscope 7.3.0 → 7.6.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.
Files changed (283) hide show
  1. package/README.md +129 -10
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/dist/cli/codex-app-command.js +1 -1
  5. package/dist/cli/command-manifest-lite.js +4 -1
  6. package/dist/cli/command-registry.js +2 -0
  7. package/dist/cli/install-helpers-codex-lb-chain.js +42 -0
  8. package/dist/cli/install-helpers-codex-lb-config.js +114 -9
  9. package/dist/cli/install-helpers-install-support.js +6 -16
  10. package/dist/cli/install-helpers.js +788 -600
  11. package/dist/cli/router.js +30 -2
  12. package/dist/commands/codex-app.js +37 -1
  13. package/dist/commands/codex-lb.js +491 -300
  14. package/dist/config/skills-manifest.json +57 -71
  15. package/dist/core/align/align-route.js +127 -0
  16. package/dist/core/codex-app/codex-app-fast-ui-repair.js +8 -4
  17. package/dist/core/codex-app/codex-app-restart.js +83 -0
  18. package/dist/core/codex-app/codex-app-ui-state-snapshot.js +8 -4
  19. package/dist/core/codex-app/desktop-routing-snapshot.js +1 -1
  20. package/dist/core/codex-app/menubar/constants.js +5 -0
  21. package/dist/core/codex-app/menubar/generation-transaction-journal.js +1 -3
  22. package/dist/core/codex-app/menubar/installer.js +18 -0
  23. package/dist/core/codex-app/multi-provider-router.js +22 -10
  24. package/dist/core/codex-app/openrouter-activate.js +26 -1
  25. package/dist/core/codex-app.js +118 -26
  26. package/dist/core/codex-control/codex-app-server-runtime-env.js +86 -0
  27. package/dist/core/codex-control/codex-app-server-v2-client.js +78 -8
  28. package/dist/core/codex-control/codex-task-runner.js +2 -2
  29. package/dist/core/codex-lb/capability-runner.js +204 -0
  30. package/dist/core/codex-lb/capability-types.js +1 -0
  31. package/dist/core/codex-lb/codex-lb-env.js +9 -13
  32. package/dist/core/codex-lb/codex-lb-setup.js +45 -8
  33. package/dist/core/codex-lb/codex-lb-tool-catalog.js +52 -74
  34. package/dist/core/codex-lb/codex-lb-tool-output-recovery.js +1 -1
  35. package/dist/core/codex-lb/desktop-auth-invariant.js +185 -0
  36. package/dist/core/codex-lb/desktop-bridge/client.js +34 -0
  37. package/dist/core/codex-lb/desktop-bridge/header-policy.js +106 -0
  38. package/dist/core/codex-lb/desktop-bridge/http-forward.js +101 -0
  39. package/dist/core/codex-lb/desktop-bridge/index.js +10 -0
  40. package/dist/core/codex-lb/desktop-bridge/launchd.js +81 -0
  41. package/dist/core/codex-lb/desktop-bridge/location-rewrite.js +27 -0
  42. package/dist/core/codex-lb/desktop-bridge/security.js +274 -0
  43. package/dist/core/codex-lb/desktop-bridge/server.js +179 -0
  44. package/dist/core/codex-lb/desktop-bridge/state.js +119 -0
  45. package/dist/core/codex-lb/desktop-bridge/types.js +16 -0
  46. package/dist/core/codex-lb/desktop-bridge/websocket-forward.js +151 -0
  47. package/dist/core/codex-lb/desktop-center-credentials.js +189 -0
  48. package/dist/core/codex-lb/desktop-controller.js +1410 -0
  49. package/dist/core/codex-lb/desktop-mode.js +32 -0
  50. package/dist/core/codex-lb/desktop-real-evidence.js +168 -0
  51. package/dist/core/codex-lb/desktop-service.js +613 -0
  52. package/dist/core/codex-lb/legacy-migration.js +575 -0
  53. package/dist/core/codex-lb/migration-receipt.js +246 -0
  54. package/dist/core/codex-lb/probes/auxiliary-surfaces-probe.js +60 -0
  55. package/dist/core/codex-lb/probes/bridge-probe.js +75 -0
  56. package/dist/core/codex-lb/probes/catalog-probe.js +128 -0
  57. package/dist/core/codex-lb/probes/computer-use-probe.js +50 -0
  58. package/dist/core/codex-lb/probes/image-generation-probe.js +47 -0
  59. package/dist/core/codex-lb/probes/probe-evidence.js +39 -0
  60. package/dist/core/codex-lb/probes/voice-realtime-probe.js +44 -0
  61. package/dist/core/codex-lb/trusted-deep-evidence.js +403 -0
  62. package/dist/core/codex-native/core-skill-manifest.js +22 -6
  63. package/dist/core/codex-runtime/codex-desktop-config-policy.js +2 -1
  64. package/dist/core/commands/align-command.js +220 -0
  65. package/dist/core/commands/image-ux-review-command.js +280 -68
  66. package/dist/core/commands/naruto-command.js +257 -8
  67. package/dist/core/commands/ppt-command.js +2 -2
  68. package/dist/core/commands/telegram-command.js +402 -34
  69. package/dist/core/commands/triwiki-command.js +11 -1
  70. package/dist/core/commands/triwiki-graph-command.js +160 -0
  71. package/dist/core/commands/wiki-command.js +100 -20
  72. package/dist/core/doctor/skill-legacy-surface.js +1 -3
  73. package/dist/core/errors/message.js +3 -0
  74. package/dist/core/evidence/evidence-freshness.js +14 -0
  75. package/dist/core/feature-fixtures.js +9 -1
  76. package/dist/core/feature-registry.js +5 -3
  77. package/dist/core/fsx.js +7 -3
  78. package/dist/core/hooks-runtime/codex-commit-hooks-selftest.js +82 -0
  79. package/dist/core/hooks-runtime/context-graph-freshness-preflight.js +113 -0
  80. package/dist/core/hooks-runtime/hook-context.js +15 -2
  81. package/dist/core/hooks-runtime/managed-skill-generation-heal.js +46 -0
  82. package/dist/core/hooks-runtime/naruto-terminal-finalization.js +1 -0
  83. package/dist/core/hooks-runtime/official-subagent-lifecycle.js +1 -1
  84. package/dist/core/hooks-runtime/tool-output-quarantine.js +1 -1
  85. package/dist/core/hooks-runtime.js +33 -89
  86. package/dist/core/image-ux-review/callout-extraction.js +6 -0
  87. package/dist/core/image-ux-review/imagegen-adapter.js +64 -23
  88. package/dist/core/image-ux-review/real-callout-extractor.js +1 -0
  89. package/dist/core/image-ux-review.js +230 -12
  90. package/dist/core/imagegen/codex-lb-imagegen-target.js +21 -7
  91. package/dist/core/imagegen/imagegen-capability.js +10 -4
  92. package/dist/core/imagegen/require-imagegen.js +29 -5
  93. package/dist/core/init/skills.js +25 -13
  94. package/dist/core/init.js +1 -1
  95. package/dist/core/install/installed-package-smoke.js +157 -4
  96. package/dist/core/managed-assets/managed-assets-manifest.js +2 -1
  97. package/dist/core/managed-path-safety.js +8 -10
  98. package/dist/core/mcp-config/backup.js +2 -4
  99. package/dist/core/mcp-config/config-reader.js +2 -4
  100. package/dist/core/mcp-config/scope.js +3 -5
  101. package/dist/core/naruto/context-graph-advisor-scope.js +242 -0
  102. package/dist/core/naruto/context-graph-advisor.js +0 -0
  103. package/dist/core/perf/release-latency-slo.js +12 -6
  104. package/dist/core/pipeline-internals/runtime-core.js +78 -0
  105. package/dist/core/pipeline-internals/runtime-gates.js +5 -2
  106. package/dist/core/policy/latest-version-guidance.js +113 -0
  107. package/dist/core/ppt.js +8 -8
  108. package/dist/core/preflight/parallel-preflight-engine.js +1 -1
  109. package/dist/core/proof/auto-finalize.js +98 -6
  110. package/dist/core/proof/route-proof-gate.js +12 -1
  111. package/dist/core/proof/route-proof-policy.js +8 -8
  112. package/dist/core/release/gate-manifest.js +32 -1
  113. package/dist/core/release/npm-stage-contract.js +21 -0
  114. package/dist/core/release/package-size-budget.js +2 -2
  115. package/dist/core/release/release-gate-contract.js +5 -0
  116. package/dist/core/release/release-proof-io.js +1 -3
  117. package/dist/core/release/release-real-contract.js +5 -4
  118. package/dist/core/release/release-stamp-proof.js +1 -3
  119. package/dist/core/release/stage-publish.js +27 -0
  120. package/dist/core/remote/audit-idempotency.js +50 -18
  121. package/dist/core/remote/codex-public-activity.js +283 -0
  122. package/dist/core/remote/index.js +1 -0
  123. package/dist/core/remote/ssh-worker-client.js +4 -1
  124. package/dist/core/remote/worker.js +206 -27
  125. package/dist/core/research/research-adversarial-review.js +1 -3
  126. package/dist/core/research/research-claim-synthesizer.js +1 -3
  127. package/dist/core/research/research-falsification-runner.js +1 -3
  128. package/dist/core/research/research-source-evidence.js +1 -3
  129. package/dist/core/responses-stream.js +65 -19
  130. package/dist/core/routes/constants.js +1 -1
  131. package/dist/core/routes/design-policy.js +7 -2
  132. package/dist/core/routes/dollar-manifest-lite.js +2 -4
  133. package/dist/core/routes/evidence.js +1 -1
  134. package/dist/core/routes.js +30 -10
  135. package/dist/core/safety/command-contract/contracts.js +2 -0
  136. package/dist/core/safety/command-contract/types.js +1 -1
  137. package/dist/core/search/context-graph-seeds.js +205 -0
  138. package/dist/core/search/context.js +0 -0
  139. package/dist/core/search/provider.js +2 -2
  140. package/dist/core/secret-redaction.js +6 -0
  141. package/dist/core/subagents/naruto-help-contract.js +2 -1
  142. package/dist/core/subagents/naruto-host-credentials.js +161 -0
  143. package/dist/core/subagents/official-subagent-preparation.js +4 -1
  144. package/dist/core/subagents/official-subagent-prompt.js +44 -2
  145. package/dist/core/subagents/official-subagent-runner.js +26 -8
  146. package/dist/core/subagents/triwiki-attention.js +135 -123
  147. package/dist/core/telegram/bot-api.js +85 -10
  148. package/dist/core/telegram/center-snapshot.js +718 -0
  149. package/dist/core/telegram/config.js +158 -31
  150. package/dist/core/telegram/hub.js +288 -33
  151. package/dist/core/telegram/index.js +2 -0
  152. package/dist/core/telegram/ingress-spool.js +419 -0
  153. package/dist/core/telegram/ledgers.js +188 -29
  154. package/dist/core/telegram/messages.js +26 -6
  155. package/dist/core/telegram/owner-lock.js +10 -7
  156. package/dist/core/telegram/runtime-projection.js +38 -3
  157. package/dist/core/telegram/runtime.js +298 -63
  158. package/dist/core/telegram/service.js +137 -8
  159. package/dist/core/telegram/setup.js +291 -73
  160. package/dist/core/text/strings.js +3 -0
  161. package/dist/core/triwiki/code-pack.js +12 -120
  162. package/dist/core/triwiki/context-graph/compiler/cache-key.js +277 -0
  163. package/dist/core/triwiki/context-graph/compiler/extract.js +104 -0
  164. package/dist/core/triwiki/context-graph/compiler/freshness.js +108 -0
  165. package/dist/core/triwiki/context-graph/compiler/index.js +237 -0
  166. package/dist/core/triwiki/context-graph/compiler/merge.js +0 -0
  167. package/dist/core/triwiki/context-graph/compiler/serialize.js +115 -0
  168. package/dist/core/triwiki/context-graph/contracts.js +236 -0
  169. package/dist/core/triwiki/context-graph/extractors/code/builders.js +150 -0
  170. package/dist/core/triwiki/context-graph/extractors/code/declarations.js +161 -0
  171. package/dist/core/triwiki/context-graph/extractors/code/index.js +265 -0
  172. package/dist/core/triwiki/context-graph/extractors/code/inventory.js +237 -0
  173. package/dist/core/triwiki/context-graph/extractors/code/module-graph.js +252 -0
  174. package/dist/core/triwiki/context-graph/extractors/code/modules.js +79 -0
  175. package/dist/core/triwiki/context-graph/extractors/code/references.js +184 -0
  176. package/dist/core/triwiki/context-graph/extractors/code/selection.js +100 -0
  177. package/dist/core/triwiki/context-graph/extractors/code/ts-config.js +161 -0
  178. package/dist/core/triwiki/context-graph/extractors/code/types.js +18 -0
  179. package/dist/core/triwiki/context-graph/extractors/evidence/claims.js +289 -0
  180. package/dist/core/triwiki/context-graph/extractors/evidence/index.js +54 -0
  181. package/dist/core/triwiki/context-graph/extractors/evidence/proof-index.js +213 -0
  182. package/dist/core/triwiki/context-graph/extractors/evidence/proofs.js +266 -0
  183. package/dist/core/triwiki/context-graph/extractors/evidence/redaction.js +177 -0
  184. package/dist/core/triwiki/context-graph/extractors/evidence/shared.js +0 -0
  185. package/dist/core/triwiki/context-graph/extractors/evidence/sources.js +154 -0
  186. package/dist/core/triwiki/context-graph/extractors/index.js +7 -0
  187. package/dist/core/triwiki/context-graph/extractors/topology/commands.js +207 -0
  188. package/dist/core/triwiki/context-graph/extractors/topology/gate-edges.js +201 -0
  189. package/dist/core/triwiki/context-graph/extractors/topology/gates.js +182 -0
  190. package/dist/core/triwiki/context-graph/extractors/topology/globs.js +124 -0
  191. package/dist/core/triwiki/context-graph/extractors/topology/index.js +92 -0
  192. package/dist/core/triwiki/context-graph/extractors/topology/routes.js +135 -0
  193. package/dist/core/triwiki/context-graph/extractors/topology/shared.js +215 -0
  194. package/dist/core/triwiki/context-graph/graph-index.js +143 -0
  195. package/dist/core/triwiki/context-graph/ids.js +0 -0
  196. package/dist/core/triwiki/context-graph/lint/index.js +0 -0
  197. package/dist/core/triwiki/context-graph/lint/rules.js +232 -0
  198. package/dist/core/triwiki/context-graph/lint/warnings.js +93 -0
  199. package/dist/core/triwiki/context-graph/paths.js +116 -0
  200. package/dist/core/triwiki/context-graph/profiles.js +78 -0
  201. package/dist/core/triwiki/context-graph/projections/anchors.js +91 -0
  202. package/dist/core/triwiki/context-graph/projections/attention.js +58 -0
  203. package/dist/core/triwiki/context-graph/projections/code-pack-workspace.js +62 -0
  204. package/dist/core/triwiki/context-graph/projections/code-pack.js +144 -0
  205. package/dist/core/triwiki/context-graph/projections/index.js +7 -0
  206. package/dist/core/triwiki/context-graph/projections/module-view.js +140 -0
  207. package/dist/core/triwiki/context-graph/projections/node-summary.js +118 -0
  208. package/dist/core/triwiki/context-graph/projections/pack-contract.js +6 -0
  209. package/dist/core/triwiki/context-graph/query/explain.js +0 -0
  210. package/dist/core/triwiki/context-graph/query/index.js +178 -0
  211. package/dist/core/triwiki/context-graph/query/load.js +116 -0
  212. package/dist/core/triwiki/context-graph/query/pack.js +117 -0
  213. package/dist/core/triwiki/context-graph/query/rank.js +142 -0
  214. package/dist/core/triwiki/context-graph/query/ranking-config.js +64 -0
  215. package/dist/core/triwiki/context-graph/query/seeds.js +241 -0
  216. package/dist/core/triwiki/context-graph/query/snapshot-cache.js +72 -0
  217. package/dist/core/triwiki/context-graph/query/traverse.js +154 -0
  218. package/dist/core/triwiki/context-graph/query-types.js +29 -0
  219. package/dist/core/triwiki/context-graph/store/compile-lock.js +21 -0
  220. package/dist/core/triwiki/context-graph/store/event-log.js +48 -0
  221. package/dist/core/triwiki/context-graph/store/fragment-cache.js +85 -0
  222. package/dist/core/triwiki/context-graph/store/graph-status.js +85 -0
  223. package/dist/core/triwiki/context-graph/store/snapshot-store.js +155 -0
  224. package/dist/core/triwiki/triwiki-proof-bank-index-repair.js +115 -0
  225. package/dist/core/triwiki/triwiki-proof-bank-index-store.js +290 -0
  226. package/dist/core/triwiki/triwiki-proof-bank-index.js +126 -0
  227. package/dist/core/triwiki/triwiki-proof-bank.js +7 -1
  228. package/dist/core/verification/context-graph-affected.js +271 -0
  229. package/dist/core/version.js +1 -1
  230. package/dist/native/sks-menubar/Sources/AlertFactory.swift +11 -3
  231. package/dist/native/sks-menubar/Sources/AppDelegate.swift +9 -0
  232. package/dist/native/sks-menubar/Sources/ControlCenterWindowController.swift +25 -3
  233. package/dist/native/sks-menubar/Sources/ControlKit.swift +19 -8
  234. package/dist/native/sks-menubar/Sources/DiagnosticsViewController.swift +1 -2
  235. package/dist/native/sks-menubar/Sources/MCPServersViewController.swift +45 -17
  236. package/dist/native/sks-menubar/Sources/OverviewSummary.swift +162 -0
  237. package/dist/native/sks-menubar/Sources/OverviewViewController.swift +136 -190
  238. package/dist/native/sks-menubar/Sources/ProcessClient.swift +107 -11
  239. package/dist/native/sks-menubar/Sources/ProvidersMultiProvider.swift +5 -1
  240. package/dist/native/sks-menubar/Sources/ProvidersOpenRouter.swift +16 -4
  241. package/dist/native/sks-menubar/Sources/ProvidersRoleModels.swift +4 -1
  242. package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +131 -0
  243. package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +288 -341
  244. package/dist/native/sks-menubar/Sources/RemoteTelegramCenterState.swift +447 -0
  245. package/dist/native/sks-menubar/Sources/RemoteTelegramRendering.swift +484 -0
  246. package/dist/native/sks-menubar/Sources/RemoteTelegramViewController.swift +364 -115
  247. package/dist/native/sks-menubar/Sources/SettingsViewController.swift +73 -12
  248. package/dist/native/sks-menubar/Sources/SidebarItem.swift +3 -3
  249. package/dist/native/sks-menubar/Sources/StatusItemController.swift +13 -13
  250. package/dist/native/sks-menubar/Sources/TelegramHubSupervisor.swift +469 -0
  251. package/dist/native/sks-menubar/Sources/UpdatesViewController.swift +1 -6
  252. package/dist/native/sks-menubar/Tests/AppRuntimeTestSupport.swift +12 -0
  253. package/dist/native/sks-menubar/Tests/NativeTelegramRegressionRunner.swift +55 -0
  254. package/dist/native/sks-menubar/Tests/ProcessClientTests.swift +33 -0
  255. package/dist/native/sks-menubar/Tests/ProvidersViewControllerTests.swift +142 -0
  256. package/dist/native/sks-menubar/Tests/RemoteTelegramCenterStateTests.swift +56 -0
  257. package/dist/native/sks-menubar/Tests/TelegramHubSupervisorTests.swift +55 -0
  258. package/dist/scripts/codex-lb-catalog-passthrough-check.js +120 -0
  259. package/dist/scripts/codex-lb-desktop-auth-invariant-check.js +169 -0
  260. package/dist/scripts/codex-lb-desktop-bridge-check.js +394 -0
  261. package/dist/scripts/codex-lb-desktop-bridge-latency-check.js +196 -0
  262. package/dist/scripts/codex-lb-desktop-capabilities-check.js +287 -0
  263. package/dist/scripts/codex-lb-desktop-real-evidence-check.js +123 -0
  264. package/dist/scripts/codex-lb-fast-mode-truth-check.js +10 -3
  265. package/dist/scripts/codex-lb-fast-ui-preservation-check.js +81 -40
  266. package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +99 -37
  267. package/dist/scripts/context-graph-check.js +191 -0
  268. package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +1 -1
  269. package/dist/scripts/gate-policy-audit-check.js +1 -1
  270. package/dist/scripts/helper-dedup-check.js +0 -2
  271. package/dist/scripts/installed-package-smoke-check.js +4 -1
  272. package/dist/scripts/latest-version-guidance-check.js +74 -0
  273. package/dist/scripts/release-metadata-check.js +21 -4
  274. package/dist/scripts/release-real-check.js +7 -0
  275. package/dist/scripts/ux-review-imagegen-blackbox-check.js +25 -1
  276. package/dist/scripts/ux-review-run-wires-imagegen-check.js +9 -1
  277. package/package.json +8 -3
  278. package/release-gates.v2.json +176 -1
  279. package/runtime-required-scripts.json +30 -0
  280. package/schemas/triwiki/context-graph.schema.json +119 -0
  281. package/dist/cli/install-helpers-codex-lb-selftest-chain.js +0 -112
  282. package/dist/cli/install-helpers-codex-lb-selftest.js +0 -481
  283. package/dist/core/triwiki/code-index-scanner.js +0 -309
@@ -1,5 +1,6 @@
1
1
  import fsp from 'node:fs/promises';
2
2
  import { parseCodexConfigToml } from '../codex/codex-config-toml.js';
3
+ import { errorCodeOf } from '../errors/message.js';
3
4
  import { publicMcpCommand, redactMcpError, redactMcpUrl, sanitizeMcpArgs } from './redaction.js';
4
5
  import { normalizeApprovalMode } from './secret-policy.js';
5
6
  import { MCP_DEFAULT_STARTUP_TIMEOUT_SEC, MCP_DEFAULT_TOOL_TIMEOUT_SEC, MCP_SERVER_CONFIG_SCHEMA } from './types.js';
@@ -16,7 +17,7 @@ export class McpConfigReadError extends Error {
16
17
  }
17
18
  }
18
19
  export async function readMcpConfigDocument(ref) {
19
- const stat = await fsp.lstat(ref.configPath).catch((error) => errorCode(error) === 'ENOENT' ? null : Promise.reject(error));
20
+ const stat = await fsp.lstat(ref.configPath).catch((error) => errorCodeOf(error) === 'ENOENT' ? null : Promise.reject(error));
20
21
  if (!stat)
21
22
  return { ref, exists: false, text: '', parsed: {}, rawServers: {} };
22
23
  if (stat.isSymbolicLink())
@@ -142,6 +143,3 @@ function positiveNumber(value) {
142
143
  const number = Number(value);
143
144
  return Number.isFinite(number) && number > 0 ? number : null;
144
145
  }
145
- function errorCode(error) {
146
- return error && typeof error === 'object' && 'code' in error ? String(error.code) : '';
147
- }
@@ -1,6 +1,7 @@
1
1
  import fsp from 'node:fs/promises';
2
2
  import os from 'node:os';
3
3
  import path from 'node:path';
4
+ import { errorCodeOf } from '../errors/message.js';
4
5
  export class McpScopeError extends Error {
5
6
  code;
6
7
  constructor(code) {
@@ -41,7 +42,7 @@ export function isPathInside(root, candidate) {
41
42
  return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
42
43
  }
43
44
  async function assertSafeDirectory(directory, boundary, strictNoSymlink) {
44
- const stat = await fsp.lstat(directory).catch((error) => errorCode(error) === 'ENOENT' ? null : Promise.reject(error));
45
+ const stat = await fsp.lstat(directory).catch((error) => errorCodeOf(error) === 'ENOENT' ? null : Promise.reject(error));
45
46
  if (!stat)
46
47
  return;
47
48
  if (strictNoSymlink && stat.isSymbolicLink())
@@ -53,7 +54,7 @@ async function assertSafeDirectory(directory, boundary, strictNoSymlink) {
53
54
  throw new McpScopeError('mcp_codex_home_escape_refused');
54
55
  }
55
56
  async function assertSafeConfig(configPath, boundary) {
56
- const stat = await fsp.lstat(configPath).catch((error) => errorCode(error) === 'ENOENT' ? null : Promise.reject(error));
57
+ const stat = await fsp.lstat(configPath).catch((error) => errorCodeOf(error) === 'ENOENT' ? null : Promise.reject(error));
57
58
  if (!stat)
58
59
  return;
59
60
  if (stat.isSymbolicLink())
@@ -64,6 +65,3 @@ async function assertSafeConfig(configPath, boundary) {
64
65
  if (!resolved || !isPathInside(boundary, resolved))
65
66
  throw new McpScopeError('mcp_config_escape_refused');
66
67
  }
67
- function errorCode(error) {
68
- return error && typeof error === 'object' && 'code' in error ? String(error.code) : '';
69
- }
@@ -0,0 +1,242 @@
1
+ import { incomingEdges, outgoingEdges } from '../triwiki/context-graph/graph-index.js';
2
+ import { contextGraphPathFromId } from '../triwiki/context-graph/ids.js';
3
+ import { isWorkspaceRelativePosixPath } from '../triwiki/context-graph/paths.js';
4
+ import { normalizeNarutoPath } from './naruto-work-item.js';
5
+ export const NARUTO_ADVISOR_CAPS = {
6
+ maxDepth: 2,
7
+ maxNodesPerWalk: 512,
8
+ maxEdgesPerWalk: 4096,
9
+ maxSharedPathsPerPair: 12,
10
+ maxRecommendationsPerSlice: 24,
11
+ maxTaskTokens: 32,
12
+ maxNodesPerLabel: 8
13
+ };
14
+ const DEPENDENCY_EDGE_TYPES = new Set(['imports', 'reexports', 'depends_on', 'references', 'calls', 'routes_to']);
15
+ const IMPACT_EDGE_TYPES = new Set([
16
+ 'imports', 'reexports', 'references', 'calls', 'tests', 'affected_by', 'verified_by', 'gated_by', 'depends_on', 'owns', 'routes_to'
17
+ ]);
18
+ const SEED_EXPANSION_EDGE_TYPES = new Set(['defines', 'contains']);
19
+ function explanationStep(edge) {
20
+ return { edgeId: edge.id, type: edge.type, from: edge.from, to: edge.to, confidence: edge.confidence, path: edge.provenance.path };
21
+ }
22
+ function nodePathOf(node) {
23
+ return node.path ?? contextGraphPathFromId(node.id);
24
+ }
25
+ function isTestNode(node) {
26
+ return node.kind === 'test' || node.metadata.isTest === true;
27
+ }
28
+ function isProtectedGateNode(node) {
29
+ return node.risk === 'protected' || node.metadata.requiredForPublish === true || node.metadata.alwaysOnRelease === true;
30
+ }
31
+ function riskDomainOf(node) {
32
+ if (node.kind === 'risk_domain')
33
+ return node.label || null;
34
+ if (node.kind !== 'gate')
35
+ return null;
36
+ const declared = node.metadata.namespace;
37
+ return (typeof declared === 'string' && declared ? declared : node.label.split(':')[0]) || null;
38
+ }
39
+ export function narutoAdvisorWorkspacePath(value) {
40
+ const normalized = normalizeNarutoPath(value);
41
+ return normalized && isWorkspaceRelativePosixPath(normalized) ? normalized : null;
42
+ }
43
+ export function narutoAdvisorPathList(values) {
44
+ return [...new Set((values ?? []).map(narutoAdvisorWorkspacePath).filter((value) => value !== null))].sort();
45
+ }
46
+ function rootHit(nodeId) {
47
+ return { nodeId, depth: 0, reasonPath: [nodeId], explanation: [] };
48
+ }
49
+ function walkGraph(index, roots, direction, types, maxDepth) {
50
+ const hits = new Map();
51
+ const queue = [];
52
+ for (const root of roots) {
53
+ if (hits.has(root.nodeId) || !index.nodesById.has(root.nodeId))
54
+ continue;
55
+ hits.set(root.nodeId, root);
56
+ queue.push(root);
57
+ }
58
+ let edgesVisited = 0;
59
+ for (let head = 0; head < queue.length; head += 1) {
60
+ const current = queue[head];
61
+ if (!current || current.depth >= maxDepth)
62
+ continue;
63
+ for (const edge of direction === 'out' ? outgoingEdges(index, current.nodeId) : incomingEdges(index, current.nodeId)) {
64
+ edgesVisited += 1;
65
+ if (edgesVisited > NARUTO_ADVISOR_CAPS.maxEdgesPerWalk)
66
+ return { hits, truncated: true };
67
+ if (!types.has(edge.type))
68
+ continue;
69
+ const next = direction === 'out' ? edge.to : edge.from;
70
+ if (hits.has(next) || !index.nodesById.has(next))
71
+ continue;
72
+ if (hits.size >= NARUTO_ADVISOR_CAPS.maxNodesPerWalk)
73
+ return { hits, truncated: true };
74
+ const hit = {
75
+ nodeId: next,
76
+ depth: current.depth + 1,
77
+ reasonPath: [...current.reasonPath, direction === 'out' ? edge.type : `<-${edge.type}`, next],
78
+ explanation: [...current.explanation, explanationStep(edge)]
79
+ };
80
+ hits.set(next, hit);
81
+ queue.push(hit);
82
+ }
83
+ }
84
+ return { hits, truncated: false };
85
+ }
86
+ export function narutoAdvisorProvenance(index, hit) {
87
+ const out = [];
88
+ const seen = new Set();
89
+ for (const step of hit.explanation) {
90
+ const edge = index.edgesById.get(step.edgeId);
91
+ if (!edge)
92
+ continue;
93
+ const key = `${edge.provenance.path}#${edge.provenance.line ?? 0}#${edge.provenance.hash}`;
94
+ if (seen.has(key))
95
+ continue;
96
+ seen.add(key);
97
+ const line = edge.provenance.line;
98
+ out.push({ path: edge.provenance.path, ...(line === undefined ? {} : { line }), hash: edge.provenance.hash });
99
+ }
100
+ if (out.length)
101
+ return out;
102
+ const node = index.nodesById.get(hit.nodeId);
103
+ const nodePath = node ? nodePathOf(node) : null;
104
+ if (node?.contentHash && nodePath)
105
+ out.push({ path: nodePath, hash: node.contentHash });
106
+ return out;
107
+ }
108
+ function resolveSeedNodes(index, paths, labels, unresolved) {
109
+ const out = [];
110
+ const seen = new Set();
111
+ const push = (id) => {
112
+ if (!seen.has(id) && index.nodesById.has(id)) {
113
+ seen.add(id);
114
+ out.push(id);
115
+ }
116
+ };
117
+ const miss = (value) => {
118
+ if (value && !unresolved.includes(value))
119
+ unresolved.push(value);
120
+ };
121
+ for (const raw of paths) {
122
+ const rel = narutoAdvisorWorkspacePath(raw);
123
+ const ids = rel ? index.nodesByPath.get(rel) ?? [] : [];
124
+ if (!ids.length)
125
+ miss(raw);
126
+ for (const id of ids)
127
+ push(id);
128
+ }
129
+ for (const label of labels) {
130
+ const ids = index.nodesByLabel.get(String(label ?? '').toLowerCase()) ?? [];
131
+ if (!ids.length || ids.length > NARUTO_ADVISOR_CAPS.maxNodesPerLabel)
132
+ miss(String(label ?? ''));
133
+ else
134
+ for (const id of ids)
135
+ push(id);
136
+ }
137
+ return out;
138
+ }
139
+ export function narutoAdvisorTaskTokens(text) {
140
+ const tokens = [];
141
+ for (const raw of String(text ?? '').split(/[^\w$./-]+/)) {
142
+ const token = raw.replace(/^[./-]+/, '').replace(/[./-]+$/, '');
143
+ if (token.length < 3 || tokens.includes(token))
144
+ continue;
145
+ if (!/^[A-Za-z_$][\w$]*$/.test(token) && !/^[\w$./-]+\.[A-Za-z]\w*$/.test(token))
146
+ continue;
147
+ tokens.push(token);
148
+ if (tokens.length >= NARUTO_ADVISOR_CAPS.maxTaskTokens)
149
+ break;
150
+ }
151
+ return tokens;
152
+ }
153
+ function expandRoots(index, seeds) {
154
+ const roots = seeds.map(rootHit);
155
+ const seen = new Set(seeds);
156
+ for (const seed of seeds) {
157
+ for (const edge of outgoingEdges(index, seed)) {
158
+ if (!SEED_EXPANSION_EDGE_TYPES.has(edge.type) || seen.has(edge.to))
159
+ continue;
160
+ seen.add(edge.to);
161
+ roots.push({ nodeId: edge.to, depth: 0, reasonPath: [seed, edge.type, edge.to], explanation: [explanationStep(edge)] });
162
+ if (roots.length >= NARUTO_ADVISOR_CAPS.maxNodesPerWalk)
163
+ return roots;
164
+ }
165
+ }
166
+ return roots;
167
+ }
168
+ function closurePaths(index, walk) {
169
+ const out = new Map();
170
+ for (const hit of walk.hits.values()) {
171
+ const node = index.nodesById.get(hit.nodeId);
172
+ if (!node || isTestNode(node))
173
+ continue;
174
+ const nodePath = nodePathOf(node);
175
+ if (nodePath && !out.has(nodePath))
176
+ out.set(nodePath, hit);
177
+ }
178
+ return out;
179
+ }
180
+ function recommendationsFrom(index, sliceId, writeSet, impact) {
181
+ const out = [];
182
+ for (const hit of impact.hits.values()) {
183
+ const node = index.nodesById.get(hit.nodeId);
184
+ if (!node)
185
+ continue;
186
+ const isGate = node.kind === 'gate';
187
+ if (!isGate && !isTestNode(node))
188
+ continue;
189
+ const nodePath = nodePathOf(node);
190
+ if (!nodePath || (!isGate && writeSet.has(nodePath)))
191
+ continue;
192
+ out.push({
193
+ slice_id: sliceId,
194
+ kind: isGate ? 'gate' : 'test',
195
+ id: isGate ? node.label : nodePath,
196
+ path: nodePath,
197
+ protected: isGate ? isProtectedGateNode(node) : false,
198
+ risk_domain: isGate ? riskDomainOf(node) : null,
199
+ reason_path: [...hit.reasonPath],
200
+ explanation: [...hit.explanation],
201
+ provenance: narutoAdvisorProvenance(index, hit)
202
+ });
203
+ if (out.length >= NARUTO_ADVISOR_CAPS.maxRecommendationsPerSlice)
204
+ break;
205
+ }
206
+ return out.sort((a, b) => (a.kind === b.kind ? (a.id < b.id ? -1 : a.id > b.id ? 1 : 0) : a.kind < b.kind ? -1 : 1));
207
+ }
208
+ export function buildNarutoSliceState(index, slice, taskTokens, maxDepth) {
209
+ const unresolved = [];
210
+ const writePaths = narutoAdvisorPathList(slice.writePaths);
211
+ const labels = [...(slice.symbols ?? [])];
212
+ if (!writePaths.length && !labels.length)
213
+ labels.push(...narutoAdvisorTaskTokens(slice.title ?? ''), ...taskTokens);
214
+ const seeds = resolveSeedNodes(index, writePaths, labels, unresolved);
215
+ const walk = walkGraph(index, seeds.map(rootHit), 'out', DEPENDENCY_EDGE_TYPES, maxDepth);
216
+ const impact = walkGraph(index, expandRoots(index, seeds), 'in', IMPACT_EDGE_TYPES, maxDepth);
217
+ const closure = closurePaths(index, walk);
218
+ const writeSet = new Set(writePaths);
219
+ return {
220
+ scope: {
221
+ slice_id: slice.id,
222
+ seed_node_ids: [...seeds].sort(),
223
+ write_paths: writePaths,
224
+ write_closure: [...closure.keys()].sort(),
225
+ unresolved_seeds: [...new Set(unresolved)].sort(),
226
+ truncated: walk.truncated || impact.truncated
227
+ },
228
+ closure,
229
+ writeSet,
230
+ recommendations: recommendationsFrom(index, slice.id, writeSet, impact)
231
+ };
232
+ }
233
+ export function emptyNarutoScope(sliceId, slice) {
234
+ return {
235
+ slice_id: sliceId,
236
+ seed_node_ids: [],
237
+ write_paths: narutoAdvisorPathList(slice?.writePaths),
238
+ write_closure: [],
239
+ unresolved_seeds: [],
240
+ truncated: false
241
+ };
242
+ }
@@ -8,6 +8,7 @@ import { runProcess, writeJsonAtomic } from '../fsx.js';
8
8
  import { listMcpInventory } from '../mcp-config/index.js';
9
9
  import { RemoteSshWorkerClient } from '../remote/ssh-worker-client.js';
10
10
  import { TelegramHubRouter } from '../telegram/hub.js';
11
+ import { buildTelegramConfigV2, telegramTokenFingerprint } from '../telegram/config.js';
11
12
  import { TelegramActionBroker, TelegramAuditLedger, TelegramIdempotencyLedger, TelegramTopicRegistry } from '../telegram/ledgers.js';
12
13
  import { TelegramMessageProjector } from '../telegram/messages.js';
13
14
  import { TelegramHubRuntime } from '../telegram/runtime.js';
@@ -124,10 +125,14 @@ async function measureUpdateCacheRead(tmp, count) {
124
125
  }
125
126
  async function measureTelegramAck(tmp, count) {
126
127
  const base = path.join(tmp, 'telegram');
127
- const config = {
128
- schema: 'sks.telegram-config.v1', bot_token_ref: { type: 'external_file', path: '/dev/null' },
129
- paired_chat_ids: ['1'], paired_user_ids: ['1']
130
- };
128
+ const config = buildTelegramConfigV2({
129
+ botTokenRef: { type: 'external_file', path: '/dev/null' },
130
+ botUserId: '99',
131
+ botTokenFingerprint: telegramTokenFingerprint('123456789:ABCDEFGHIJKLMNOPQRSTUVWX'),
132
+ pairedChatId: '1',
133
+ pairedUserId: '1',
134
+ configGeneration: 1
135
+ });
131
136
  let ackAt = 0;
132
137
  const api = {
133
138
  call: async (method) => {
@@ -257,6 +262,7 @@ function nativeHarnessSource() {
257
262
  return `let application = NSApplication.shared
258
263
  application.setActivationPolicy(.accessory)
259
264
  let processClient = ProcessClient(actionScript: AppRuntime.actionScript, logPath: AppRuntime.lastActionLogPath, projectRoot: AppRuntime.projectRoot)
265
+ let telegramHubSupervisor = TelegramHubSupervisor(processClient: processClient, actionScript: AppRuntime.actionScript, projectRoot: AppRuntime.projectRoot)
260
266
  let operations = OperationCoordinator(directory: AppRuntime.operationDirectory)
261
267
  let notifications = NotificationCoordinator()
262
268
  let status = StatusItemController(processClient: processClient, operations: operations, notifications: notifications, openControlCenter: { _ in })
@@ -264,11 +270,11 @@ let firstStart = DispatchTime.now().uptimeNanoseconds
264
270
  status.start()
265
271
  let firstMs = Double(DispatchTime.now().uptimeNanoseconds - firstStart) / 1_000_000
266
272
  status.stop()
267
- let control = ControlCenterWindowController(processClient: processClient, operations: operations, notifications: notifications)
273
+ let control = ControlCenterWindowController(processClient: processClient, telegramHubSupervisor: telegramHubSupervisor, operations: operations, notifications: notifications)
268
274
  let controlStart = DispatchTime.now().uptimeNanoseconds
269
275
  control.show(section: .overview)
270
276
  let controlMs = Double(DispatchTime.now().uptimeNanoseconds - controlStart) / 1_000_000
271
- control.window?.orderOut(nil)
277
+ control.window?.orderOut(nil as Any?)
272
278
  let data = try! JSONSerialization.data(withJSONObject: ["menu_bar_first_state_render_ms": firstMs, "control_center_open_ms": controlMs])
273
279
  print(String(data: data, encoding: .utf8)!)`;
274
280
  }
@@ -14,6 +14,7 @@ import { writeMistakeMemoryReport } from '../mistake-memory.js';
14
14
  import { MISTAKE_RECALL_ARTIFACT, mistakeRecallGateStatus } from '../mistake-recall.js';
15
15
  import { recordSkillDreamEvent, SKILL_DREAM_POLICY, writeSkillForgeReport } from '../skill-forge.js';
16
16
  import { evaluateResearchGate, researchPaperArtifactForPlan, writeResearchPlan } from '../research.js';
17
+ import { ALIGN_GATE_ARTIFACT, ALIGN_LEDGER_ARTIFACT, ALIGN_PLAN_ARTIFACT, alignNextActionText, writeAlignRouteArtifacts } from '../align/align-route.js';
17
18
  import { PPT_REQUIRED_GATE_FIELDS, writePptRouteArtifacts } from '../ppt.js';
18
19
  import { writeQaLoopArtifacts } from '../qa-loop.js';
19
20
  import { IMAGE_UX_REVIEW_GATE_ARTIFACT, IMAGE_UX_REVIEW_POLICY_ARTIFACT, IMAGE_UX_REVIEW_SCREEN_INVENTORY_ARTIFACT, IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT, IMAGE_UX_REVIEW_ISSUE_LEDGER_ARTIFACT, IMAGE_UX_REVIEW_ITERATION_REPORT_ARTIFACT, IMAGE_UX_REVIEW_REQUIRED_GATE_FIELDS, writeImageUxReviewRouteArtifacts } from '../image-ux-review.js';
@@ -678,6 +679,8 @@ export function promptPipelineContext(prompt, route = null) {
678
679
  lines.push('MAD-SKS SQL-plane: explicit invocation is the approval boundary. Use the mission-local write-capable Supabase MCP profile only for the bound cycle, verify execute_sql/apply_migration inventory before claiming ready, execute requested SQL-plane mutations, read back postconditions, then close the capability/profile and prove normal read-only restoration. Supabase project/account/billing/credential control-plane actions remain denied.');
679
680
  if (route?.id === 'GX')
680
681
  lines.push('GX route: use deterministic vgraph/beta render, validate, drift, and snapshot artifacts.');
682
+ if (route?.id === 'Align')
683
+ lines.push('Align route: modernize SKS prompts, settings, and skill/command surfaces to latest GPT-5.6 official prompting, programmatic tool calling, agents, and openai/skills guidance. Latest-version only: delete legacy compat knobs, deduplicate redundant prompt/config content, protect immutable core skills, update align-ledger.json, and pass align-gate.json.');
681
684
  return lines.join('\n');
682
685
  }
683
686
  export function dfixQuickContext(prompt, route = routePrompt(prompt)) {
@@ -791,6 +794,8 @@ export async function prepareRoute(root, prompt, state = {}, opts = {}) {
791
794
  return finish(await prepareResearch(root, route, task, required, { sessionKey }));
792
795
  if (route.id === 'AutoResearch')
793
796
  return finish(await prepareAutoResearch(root, route, task, required, { sessionKey }));
797
+ if (route.id === 'Align')
798
+ return finish(await prepareAlign(root, route, task, required, { sessionKey }));
794
799
  if (route.id === 'DB')
795
800
  return finish(await prepareDb(root, route, task, required, { sessionKey }));
796
801
  if (route.id === 'GX')
@@ -956,6 +961,16 @@ export async function activeRouteContext(root, state) {
956
961
  return `Route contract sealed for ${state.route_command || state.route || state.mode}. Use decision-contract.json and ${PIPELINE_PLAN_ARTIFACT} before executing the route. Before the next route phase, read relevant TriWiki context, hydrate low-trust claims from source, and refresh/validate TriWiki again after new findings or artifact changes. Next atomic action: continue the original route lifecycle with the inferred goal, constraints, non-goals, risk boundary, and test scope.${planNote}`;
957
962
  }
958
963
  if (state.mode === 'NARUTO') {
964
+ if (state.session_scope) {
965
+ const parentEvidenceState = await appNarutoParentEvidenceState(root, state);
966
+ if (parentEvidenceState === 'completed') {
967
+ return `Active Codex App Naruto mission ${state.mission_id || 'latest'} has trustworthy current-run parent evidence and a passed Naruto gate. Do not resubmit or expose the sks.subagent-parent-summary.v1 JSON. Return concise Markdown in the user's language with an explicit completion summary, verification, remaining gaps, and Honest Mode.${reasoningNote}${planNote}`;
968
+ }
969
+ if (parentEvidenceState === 'blocked') {
970
+ return `Active Codex App Naruto mission ${state.mission_id || 'latest'} has trustworthy current-run blocked or failed parent evidence. Do not resubmit or expose the sks.subagent-parent-summary.v1 JSON. Return concise Markdown in the user's language that states the blocker or failure first, avoids completion or success wording, and includes verification, remaining gaps, and Honest Mode.${reasoningNote}${planNote}`;
971
+ }
972
+ return `Active Codex App Naruto mission ${state.mission_id || 'latest'} uses the official Codex subagent workflow. Read subagent-plan.json, keep ${SUBAGENT_EVENT_LOG_FILENAME} and ${SUBAGENT_EVIDENCE_FILENAME} current from SubagentStart/SubagentStop, and wait for all requested agent threads. Build the exact sks.subagent-parent-summary.v1 object with run_id matching subagent-plan.json.workflow_run_id, then send it only through stdin to "sks naruto parent-summary --mission ${state.mission_id} --stdin --json". Do not expose, paste, embed, quote, or fence that JSON in the user-visible response. Only after the command accepts it, return concise Markdown in the user's language with completion summary, verification, remaining gaps/blockers, and Honest Mode. A successful response must visibly include the completion summary and Honest Mode assessment. If any parent/thread status is blocked or failed, or blockers remain, state that first and do not use completion or success wording. Do not substitute process counts, PID evidence, retired process-pool artifacts, or a custom active pool.${reasoningNote}${planNote}`;
973
+ }
959
974
  return `Active Naruto mission ${state.mission_id || 'latest'} uses the official Codex subagent workflow. Read subagent-plan.json, keep ${SUBAGENT_EVENT_LOG_FILENAME} and ${SUBAGENT_EVIDENCE_FILENAME} current from SubagentStart/SubagentStop, wait for all requested agent threads, then return the exact sks.subagent-parent-summary.v1 JSON result so SKS can persist ${SUBAGENT_PARENT_SUMMARY_FILENAME} and derive naruto-summary.json/naruto-gate.json. Do not substitute process counts, PID evidence, retired process-pool artifacts, or a custom active pool.${reasoningNote}${planNote}`;
960
975
  }
961
976
  if (state.subagents_required && !(await hasSubagentEvidence(root, state))) {
@@ -968,6 +983,40 @@ export async function activeRouteContext(root, state) {
968
983
  }
969
984
  return planNote.trim();
970
985
  }
986
+ async function appNarutoParentEvidenceState(root, state) {
987
+ const missionId = String(state?.mission_id || '').trim();
988
+ const workflowRunId = String(state?.official_subagent_run_id || '').trim();
989
+ if (!missionId || !workflowRunId)
990
+ return null;
991
+ const dir = missionDir(root, missionId);
992
+ const [evidence, summary, gate] = await Promise.all([
993
+ subagentEvidence(root, state).catch(() => null),
994
+ readJson(path.join(dir, NARUTO_SUMMARY_FILENAME), null).catch(() => null),
995
+ readJson(path.join(dir, NARUTO_GATE_FILENAME), null).catch(() => null)
996
+ ]);
997
+ const currentRun = evidence?.run_id === workflowRunId
998
+ && summary?.workflow_run_id === workflowRunId
999
+ && gate?.workflow_run_id === workflowRunId;
1000
+ if (!currentRun || evidence?.parent_summary_trustworthy !== true)
1001
+ return null;
1002
+ if (evidence.ok === true
1003
+ && evidence.status === 'completed'
1004
+ && summary?.status === 'completed'
1005
+ && summary?.completion_evidence === true
1006
+ && gate?.passed === true
1007
+ && gate?.terminal === true
1008
+ && gate?.terminal_state === 'completed') {
1009
+ return 'completed';
1010
+ }
1011
+ if (evidence.parent_summary_status === 'failed'
1012
+ && ['blocked', 'incomplete'].includes(String(summary?.status || ''))
1013
+ && summary?.completion_evidence !== true
1014
+ && gate?.passed !== true
1015
+ && gate?.terminal_state === 'blocked') {
1016
+ return 'blocked';
1017
+ }
1018
+ return null;
1019
+ }
971
1020
  async function activePipelinePlanNote(root, state = {}) {
972
1021
  if (!state?.mission_id)
973
1022
  return '';
@@ -1263,6 +1312,35 @@ async function prepareGx(root, route, task, required, opts = {}) {
1263
1312
  await setCurrent(root, routeState(id, route, 'GX_VALIDATE_REQUIRED', required, { prompt: task, ...pipelinePlanState(pipelinePlan) }), { sessionKey: opts.sessionKey });
1264
1313
  return routeContext(route, id, task, required, 'Run sks gx init/render/validate/drift/snapshot, then pass gx-gate.json.');
1265
1314
  }
1315
+ async function prepareAlign(root, route, task, required, opts = {}) {
1316
+ const { id, dir } = await createMission(root, { mode: 'align', prompt: task, sessionKey: opts.sessionKey });
1317
+ const artifacts = await writeAlignRouteArtifacts(dir, id, task);
1318
+ await writeJsonAtomic(path.join(dir, 'route-context.json'), {
1319
+ route: route.id,
1320
+ command: route.command,
1321
+ mode: route.mode,
1322
+ task,
1323
+ mission_id: id,
1324
+ required_skills: route.requiredSkills,
1325
+ context7_required: required,
1326
+ context_tracking: triwikiContextTracking(),
1327
+ stop_gate: route.stopGate,
1328
+ official_guides: artifacts.plan.official_guides,
1329
+ workstreams: artifacts.plan.workstreams,
1330
+ artifacts: {
1331
+ plan: ALIGN_PLAN_ARTIFACT,
1332
+ ledger: ALIGN_LEDGER_ARTIFACT,
1333
+ gate: ALIGN_GATE_ARTIFACT
1334
+ }
1335
+ });
1336
+ const pipelinePlan = await writePipelinePlan(dir, { missionId: id, route, task, required, ambiguity: { required: false, status: 'direct_route' } });
1337
+ await setCurrent(root, routeState(id, route, 'ALIGN_PREPARED', required, {
1338
+ prompt: task,
1339
+ stop_gate: route.stopGate,
1340
+ ...pipelinePlanState(pipelinePlan)
1341
+ }), { sessionKey: opts.sessionKey });
1342
+ return routeContext(route, id, task, required, alignNextActionText(id));
1343
+ }
1266
1344
  async function prepareLightRoute(root, route, task, required, opts = {}) {
1267
1345
  const { id, dir } = await createMission(root, { mode: route.id.toLowerCase(), prompt: task, sessionKey: opts.sessionKey });
1268
1346
  await writeJsonAtomic(path.join(dir, 'route-context.json'), { route: route.id, command: route.command, task, required_skills: route.requiredSkills, context7_required: required, context_tracking: triwikiContextTracking(), stop_gate: 'honest_mode' });
@@ -413,6 +413,7 @@ function fireAndForgetProjectMemory(root, state = {}) {
413
413
  return appendJsonl(path.join(missionDir(root, id), 'events.jsonl'), {
414
414
  ts: nowIso(),
415
415
  type: 'triwiki.agents_md_projected',
416
+ proof_invalidating: false,
416
417
  ok: report.ok,
417
418
  reason: report.reason,
418
419
  written: report.written
@@ -424,6 +425,7 @@ function fireAndForgetProjectMemory(root, state = {}) {
424
425
  return appendJsonl(path.join(missionDir(root, id), 'events.jsonl'), {
425
426
  ts: nowIso(),
426
427
  type: 'triwiki.agents_md_project_failed',
428
+ proof_invalidating: false,
427
429
  error: err?.message || String(err)
428
430
  }).catch(() => undefined);
429
431
  });
@@ -473,7 +475,7 @@ async function complianceBlock(root, state = {}, reason = '', detail = {}) {
473
475
  missing: Array.isArray(detail.missing) ? detail.missing : []
474
476
  };
475
477
  await writeJsonAtomic(guardPath, record);
476
- await appendJsonl(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'pipeline.compliance_loop_guard', gate: record.gate, repeat_count: count, limit, tripped: record.tripped, missing: record.missing });
478
+ await appendJsonl(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'pipeline.compliance_loop_guard', proof_invalidating: false, gate: record.gate, repeat_count: count, limit, tripped: record.tripped, missing: record.missing });
477
479
  if (!record.tripped)
478
480
  return { decision: 'block', reason, gate: detail.gate || state.stop_gate || null, missing: Array.isArray(detail.missing) ? detail.missing : [] };
479
481
  await writeJsonAtomic(path.join(dir, HARD_BLOCKER_ARTIFACT), {
@@ -492,7 +494,7 @@ async function complianceBlock(root, state = {}, reason = '', detail = {}) {
492
494
  'Pipeline stopped as a hard blocker instead of looping indefinitely; no completion success is claimed.'
493
495
  ]
494
496
  });
495
- await appendJsonl(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'pipeline.compliance_loop_guard.tripped', gate: record.gate, repeat_count: count, limit });
497
+ await appendJsonl(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'pipeline.compliance_loop_guard.tripped', proof_invalidating: false, gate: record.gate, repeat_count: count, limit });
496
498
  return { decision: 'escalate', reason, gate: detail.gate || state.stop_gate || null, repeat_count: count, message: '동일 사유가 반복됩니다. 사용자 개입이 필요합니다.', systemMessage: '동일 사유가 반복됩니다. 사용자 개입이 필요합니다.' };
497
499
  }
498
500
  async function markReflectionInvalidatedForGateFailure(root, state = {}, detail = {}) {
@@ -611,6 +613,7 @@ async function resolveRecoveredComplianceBlocker(root, state = {}, recovered) {
611
613
  await appendJsonl(path.join(dir, 'events.jsonl'), {
612
614
  ts: nowIso(),
613
615
  type: 'pipeline.compliance_loop_guard.resolved',
616
+ proof_invalidating: false,
614
617
  gate,
615
618
  recovery_artifact: artifactName,
616
619
  hard_blocker_sha256: blockerSha256,
@@ -0,0 +1,113 @@
1
+ export const LATEST_VERSION_GUIDANCE_SCHEMA = 'sks.latest-version-guidance.v1';
2
+ const SEMVER_RE = /\b\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?\b/g;
3
+ export const GUIDANCE_TRIGGERS = [
4
+ 'current release',
5
+ 'current version',
6
+ 'currently',
7
+ 'preferred',
8
+ 'minimum',
9
+ 'at least',
10
+ 'requires',
11
+ 'required',
12
+ 'require',
13
+ 'must be',
14
+ 'must use',
15
+ 'update to',
16
+ 'upgrade to',
17
+ 'recommend',
18
+ 'recommended',
19
+ 'or later',
20
+ 'or newer',
21
+ 'or above',
22
+ 'supported version',
23
+ 'baseline remains',
24
+ '이상',
25
+ '권장',
26
+ '최소',
27
+ '필요합니다',
28
+ '사용하세요'
29
+ ];
30
+ export const HISTORICAL_MARKERS = [
31
+ 'changelog',
32
+ 'historical',
33
+ 'history',
34
+ 'previously',
35
+ 'fixed in',
36
+ 'regression',
37
+ 'released in',
38
+ 'shipped in',
39
+ 'was released',
40
+ 'compatibility matrix',
41
+ 'compatibility snapshot',
42
+ 'schema revision',
43
+ 'fixture',
44
+ 'reproduces',
45
+ 'as of the',
46
+ 'no longer'
47
+ ];
48
+ const STRING_LITERAL_RE = /'(?:[^'\\\n]|\\.)*'|"(?:[^"\\\n]|\\.)*"|`(?:[^`\\]|\\.)*`/g;
49
+ const WINDOW_CHARS = 90;
50
+ function containsTrigger(haystack) {
51
+ const lower = haystack.toLowerCase();
52
+ for (const trigger of GUIDANCE_TRIGGERS) {
53
+ if (lower.includes(trigger.toLowerCase()))
54
+ return trigger;
55
+ }
56
+ return null;
57
+ }
58
+ function isHistorical(haystack) {
59
+ const lower = haystack.toLowerCase();
60
+ return HISTORICAL_MARKERS.some((marker) => lower.includes(marker));
61
+ }
62
+ function excerpt(line) {
63
+ const compact = line.trim().replace(/\s+/g, ' ');
64
+ return compact.length > 160 ? `${compact.slice(0, 157)}...` : compact;
65
+ }
66
+ export function scanLatestVersionGuidance(relativePath, text, options = {}) {
67
+ const findings = [];
68
+ const lines = text.split(/\r?\n/);
69
+ for (const [index, rawLine] of lines.entries()) {
70
+ const line = rawLine ?? '';
71
+ const candidates = [];
72
+ if (options.stringLiteralsOnly) {
73
+ STRING_LITERAL_RE.lastIndex = 0;
74
+ let match;
75
+ while ((match = STRING_LITERAL_RE.exec(line)))
76
+ candidates.push(match[0]);
77
+ }
78
+ else {
79
+ candidates.push(line);
80
+ }
81
+ if (!candidates.length)
82
+ continue;
83
+ let reported = false;
84
+ for (const candidate of candidates) {
85
+ if (reported)
86
+ break;
87
+ SEMVER_RE.lastIndex = 0;
88
+ let match;
89
+ while ((match = SEMVER_RE.exec(candidate))) {
90
+ const window = candidate.slice(Math.max(0, match.index - WINDOW_CHARS), match.index + match[0].length + WINDOW_CHARS);
91
+ if (isHistorical(window))
92
+ continue;
93
+ const trigger = containsTrigger(window);
94
+ if (!trigger)
95
+ continue;
96
+ findings.push({ path: relativePath, line: index + 1, version: match[0], trigger, excerpt: excerpt(line) });
97
+ reported = true;
98
+ break;
99
+ }
100
+ }
101
+ }
102
+ return findings;
103
+ }
104
+ export const LATEST_VERSION_GUIDANCE_TEXT = 'Use the official latest stable SKS and Codex CLI releases. Run `sks update-check` and read the capability report for the current state — feature support is decided by capability probes, not by a pinned version number.';
105
+ export function latestVersionGuidanceReport(findings, scannedFiles, exemptions) {
106
+ return {
107
+ schema: LATEST_VERSION_GUIDANCE_SCHEMA,
108
+ ok: findings.length === 0,
109
+ scannedFiles,
110
+ findings: [...findings].sort((left, right) => left.path.localeCompare(right.path) || left.line - right.line),
111
+ exemptions: [...exemptions].sort()
112
+ };
113
+ }