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
@@ -13,7 +13,8 @@ export function buildNarutoHelpResult() {
13
13
  'sks naruto run "<task>" [--agents N] [--max-threads N] [--trusted-project] [--json]',
14
14
  'sks naruto status [latest|M-...] [--json]',
15
15
  'sks naruto subagents [latest|M-...] [--json]',
16
- 'sks naruto proof [latest|M-...] [--json]'
16
+ 'sks naruto proof [latest|M-...] [--json]',
17
+ 'sks naruto parent-summary --mission M-... --stdin [--json] (active Codex App Naruto only)'
17
18
  ],
18
19
  commands: [...NARUTO_ACTIONS],
19
20
  default_requested_subagents: DEFAULT_AUTOMATIC_SUBAGENT_COUNT,
@@ -0,0 +1,161 @@
1
+ export const NARUTO_AUTH_MODES = ['managed', 'host'];
2
+ const IDENTIFIER_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
3
+ export const NARUTO_EFFORT_TIERS = ['minimal', 'low', 'medium', 'high', 'max'];
4
+ function readFlagValue(args, name) {
5
+ const inline = args.find((arg) => arg.startsWith(`${name}=`));
6
+ if (inline)
7
+ return inline.slice(name.length + 1);
8
+ const index = args.indexOf(name);
9
+ if (index >= 0) {
10
+ const next = args[index + 1];
11
+ if (next !== undefined && !next.startsWith('-'))
12
+ return next;
13
+ }
14
+ return null;
15
+ }
16
+ function resolve(args, env, flag, envKey) {
17
+ const fromFlag = readFlagValue(args, flag);
18
+ if (fromFlag !== null && fromFlag !== '')
19
+ return { value: fromFlag, source: 'flag' };
20
+ const fromEnv = env[envKey];
21
+ if (typeof fromEnv === 'string' && fromEnv.trim())
22
+ return { value: fromEnv.trim(), source: 'env' };
23
+ return { value: null, source: 'default' };
24
+ }
25
+ function validIdentifier(value) {
26
+ return IDENTIFIER_RE.test(value);
27
+ }
28
+ export function resolveNarutoCredentialPolicy(input) {
29
+ const args = input.args ?? [];
30
+ const env = input.env ?? process.env;
31
+ const warnings = [];
32
+ const blockers = [];
33
+ const sources = {};
34
+ const authModeRaw = resolve(args, env, '--auth-mode', 'SKS_NARUTO_AUTH_MODE');
35
+ let authMode = 'managed';
36
+ if (authModeRaw.value !== null) {
37
+ if (NARUTO_AUTH_MODES.includes(authModeRaw.value)) {
38
+ authMode = authModeRaw.value;
39
+ sources.authMode = authModeRaw.source;
40
+ }
41
+ else {
42
+ blockers.push(`naruto_auth_mode_invalid:${authModeRaw.value}`);
43
+ }
44
+ }
45
+ if (sources.authMode === undefined)
46
+ sources.authMode = 'default';
47
+ const providerRaw = resolve(args, env, '--model-provider', 'SKS_NARUTO_MODEL_PROVIDER');
48
+ let modelProvider = authMode === 'host' ? null : 'openai';
49
+ sources.modelProvider = 'default';
50
+ if (providerRaw.value !== null) {
51
+ if (!validIdentifier(providerRaw.value)) {
52
+ blockers.push(`naruto_model_provider_invalid:${providerRaw.value.slice(0, 32)}`);
53
+ }
54
+ else if (authMode === 'managed') {
55
+ blockers.push('naruto_model_provider_requires_host_auth_mode');
56
+ }
57
+ else {
58
+ modelProvider = providerRaw.value;
59
+ sources.modelProvider = providerRaw.source;
60
+ }
61
+ }
62
+ const noForcedLogin = args.includes('--no-forced-login-method')
63
+ || String(env.SKS_NARUTO_FORCED_LOGIN_METHOD ?? '').toLowerCase() === 'none';
64
+ let forcedLoginMethod = 'chatgpt';
65
+ sources.forcedLoginMethod = 'default';
66
+ if (authMode === 'host') {
67
+ forcedLoginMethod = null;
68
+ sources.forcedLoginMethod = sources.authMode;
69
+ }
70
+ else if (noForcedLogin) {
71
+ forcedLoginMethod = null;
72
+ sources.forcedLoginMethod = args.includes('--no-forced-login-method') ? 'flag' : 'env';
73
+ warnings.push('naruto_forced_login_method_released_without_host_auth_mode');
74
+ }
75
+ const models = [
76
+ { key: 'parentModel', flag: '--parent-model', envKey: 'SKS_NARUTO_PARENT_MODEL', fallback: input.defaultParentModel, effort: false },
77
+ { key: 'parentEffort', flag: '--parent-effort', envKey: 'SKS_NARUTO_PARENT_EFFORT', fallback: input.defaultParentEffort, effort: true },
78
+ { key: 'subagentModel', flag: '--subagent-model', envKey: 'SKS_NARUTO_SUBAGENT_MODEL', fallback: input.defaultSubagentModel, effort: false },
79
+ { key: 'subagentEffort', flag: '--subagent-effort', envKey: 'SKS_NARUTO_SUBAGENT_EFFORT', fallback: input.defaultSubagentEffort, effort: true }
80
+ ];
81
+ const resolvedModels = {};
82
+ for (const entry of models) {
83
+ const raw = resolve(args, env, entry.flag, entry.envKey);
84
+ if (raw.value === null) {
85
+ resolvedModels[entry.key] = entry.fallback;
86
+ sources[entry.key] = 'default';
87
+ continue;
88
+ }
89
+ const ok = entry.effort
90
+ ? NARUTO_EFFORT_TIERS.includes(raw.value)
91
+ : validIdentifier(raw.value);
92
+ if (!ok) {
93
+ blockers.push(`naruto_${String(entry.key)}_invalid:${raw.value.slice(0, 32)}`);
94
+ resolvedModels[entry.key] = entry.fallback;
95
+ sources[entry.key] = 'default';
96
+ continue;
97
+ }
98
+ resolvedModels[entry.key] = raw.value;
99
+ sources[entry.key] = raw.source;
100
+ }
101
+ const envKeyRaw = resolve(args, env, '--provider-env-key', 'SKS_NARUTO_PROVIDER_ENV_KEY');
102
+ let providerEnvKey = null;
103
+ sources.providerEnvKey = 'default';
104
+ if (envKeyRaw.value !== null) {
105
+ if (!/^[A-Z][A-Z0-9_]{0,63}$/.test(envKeyRaw.value)) {
106
+ blockers.push(`naruto_provider_env_key_invalid:${envKeyRaw.value.slice(0, 32)}`);
107
+ }
108
+ else if (authMode === 'managed') {
109
+ blockers.push('naruto_provider_env_key_requires_host_auth_mode');
110
+ }
111
+ else {
112
+ providerEnvKey = envKeyRaw.value;
113
+ sources.providerEnvKey = envKeyRaw.source;
114
+ }
115
+ }
116
+ if (authMode === 'host' && modelProvider === null) {
117
+ warnings.push('naruto_host_auth_mode_without_explicit_model_provider');
118
+ }
119
+ if (authMode === 'host' && providerEnvKey !== null && env[providerEnvKey] === undefined) {
120
+ blockers.push(`naruto_provider_env_key_absent:${providerEnvKey}`);
121
+ }
122
+ return {
123
+ schema: 'sks.naruto-credential-policy.v1',
124
+ authMode,
125
+ modelProvider,
126
+ forcedLoginMethod,
127
+ providerEnvKey,
128
+ parentModel: String(resolvedModels.parentModel),
129
+ parentEffort: String(resolvedModels.parentEffort),
130
+ subagentModel: String(resolvedModels.subagentModel),
131
+ subagentEffort: String(resolvedModels.subagentEffort),
132
+ sources,
133
+ warnings,
134
+ blockers
135
+ };
136
+ }
137
+ export function narutoCredentialConfigArgs(policy) {
138
+ const out = [];
139
+ if (policy.modelProvider)
140
+ out.push('-c', `model_provider="${policy.modelProvider}"`);
141
+ if (policy.forcedLoginMethod)
142
+ out.push('-c', `forced_login_method="${policy.forcedLoginMethod}"`);
143
+ return out;
144
+ }
145
+ export function narutoCredentialPolicyReceipt(policy) {
146
+ return {
147
+ schema: policy.schema,
148
+ auth_mode: policy.authMode,
149
+ model_provider: policy.modelProvider,
150
+ forced_login_method: policy.forcedLoginMethod,
151
+ provider_env_key: policy.providerEnvKey,
152
+ parent_model: policy.parentModel,
153
+ parent_effort: policy.parentEffort,
154
+ subagent_model: policy.subagentModel,
155
+ subagent_effort: policy.subagentEffort,
156
+ sources: policy.sources,
157
+ warnings: policy.warnings,
158
+ blockers: policy.blockers,
159
+ credential_handled_by: policy.authMode === 'host' ? 'host_config_toml_provider_block' : 'sks_managed_chatgpt_login'
160
+ };
161
+ }
@@ -151,7 +151,10 @@ async function prepareOfficialSubagentMissionLocked(input) {
151
151
  triwikiAttention,
152
152
  recommendedAgents: suggestedAgents,
153
153
  roleModelPreferences: roleModelPreferences.store.roles,
154
- activeMainModel
154
+ activeMainModel,
155
+ parentOutputMode: mode === 'naruto' && input.sessionScope ? 'app_naruto_stdin' : 'raw_json',
156
+ missionId: input.missionId,
157
+ workflowRunId
155
158
  });
156
159
  const selectedAgentPlan = officialSubagentOnDemandRolePlan(suggestedAgents);
157
160
  const agentRouting = Object.fromEntries(Object.entries(selectedAgentPlan).map(([name, config]) => {
@@ -45,6 +45,9 @@ export function buildOfficialSubagentPrompt(input) {
45
45
  const activeMainModel = normalizedActiveMainModel(input.activeMainModel);
46
46
  const inheritActiveMainOntoChildren = childInheritsActiveMainModel(activeMainModel?.model);
47
47
  const spawnModelRouting = renderSpawnModelRouting(activeMainModel);
48
+ const parentOutputMode = input.parentOutputMode === 'app_naruto_stdin'
49
+ ? 'app_naruto_stdin'
50
+ : 'raw_json';
48
51
  const rows = resolvedSlices.map(({ slice, agentName }, index) => {
49
52
  const mode = slice.readOnly ? 'read-only' : 'use the parent permission mode';
50
53
  const paths = (slice.paths || []).map((entry) => String(entry).trim()).filter(Boolean);
@@ -160,7 +163,13 @@ ${String(input.goal || '').trim()}
160
163
  Slices:
161
164
  ${rows || '(parent decomposition required before any subagent is spawned)'}
162
165
 
163
- Final parent output:
166
+ ${parentOutputMode === 'app_naruto_stdin'
167
+ ? renderAppNarutoParentOutput(input.missionId, input.workflowRunId)
168
+ : renderRawJsonParentOutput()}
169
+ `.trim();
170
+ }
171
+ function renderRawJsonParentOutput() {
172
+ return `Final parent output:
164
173
  - return one JSON object as the final message; prose outside that object is not completion evidence
165
174
  - use this exact schema so SKS can correlate every stopped agent thread with a trustworthy parent outcome:
166
175
  {
@@ -200,7 +209,40 @@ Final parent output:
200
209
  - if changed_files is non-empty, include at least one passed named check or a specifically justified not_applicable verification row
201
210
  - use empty artifacts/capabilities_used arrays when no host capability was used; SKS overwrites these fields with observed Codex JSONL evidence before persistence
202
211
  - keep completion summary and Honest Mode wording inside the JSON fields; do not add prose outside the object
203
- `.trim();
212
+ `;
213
+ }
214
+ function renderAppNarutoParentOutput(missionId, workflowRunId) {
215
+ const mission = String(missionId || '').trim();
216
+ const runId = String(workflowRunId || '').trim();
217
+ return `Final parent output for this active Codex App Naruto run:
218
+ - build one exact JSON object using the strict sks.subagent-parent-summary.v1 schema below, with run_id=${JSON.stringify(runId || 'workflow_run_id from subagent-plan.json')}
219
+ - send that object only through stdin to \`sks naruto parent-summary --mission ${mission || '<mission-id>'} --stdin --json\`; this command is the sole parent-summary commit path
220
+ - do not expose, paste, quote, embed, or fence the JSON in the user-visible response
221
+ - only after the command accepts the object, return concise Markdown in the user's language with completion summary, verification, remaining gaps/blockers, and Honest Mode
222
+ - if the parent status, any thread outcome, or any blocker is blocked/failed, state the blocker or failure first and do not use completion or success wording
223
+ - a successful visible response must contain an explicit completion summary and Honest Mode assessment; hard-blocked/failed responses state the blocker first instead
224
+ - use this exact object schema for the stdin submission:
225
+ {
226
+ "schema": "sks.subagent-parent-summary.v1",
227
+ "run_id": "workflow_run_id from subagent-plan.json",
228
+ "status": "completed|blocked|failed",
229
+ "summary": "Concise integrated result and Honest Mode assessment.",
230
+ "thread_outcomes": [
231
+ { "thread_id": "official agent/thread id", "status": "completed|blocked|failed", "summary": "slice result" }
232
+ ],
233
+ "changed_files": [],
234
+ "verification": [
235
+ { "name": "focused check", "status": "passed|not_applicable", "reason": "required when not_applicable" }
236
+ ],
237
+ "artifacts": [],
238
+ "capabilities_used": [],
239
+ "blockers": []
240
+ }
241
+ - include one thread_outcomes row for every requested subagent; a SubagentStop event alone never proves success
242
+ - copy workflow_run_id from subagent-plan.json into run_id exactly so delayed or stale summaries cannot bind to another run
243
+ - if changed_files is non-empty, include at least one passed named check or a specifically justified not_applicable verification row
244
+ - use empty artifacts/capabilities_used arrays when no host capability was used; SKS replaces these fields with observed Codex JSONL evidence before canonical persistence
245
+ `;
204
246
  }
205
247
  function renderRoleModelPreferenceMetadata(preferences, activeMainModel) {
206
248
  const rows = Object.entries(preferences || {}).map(([role, preference]) => ({
@@ -9,6 +9,7 @@ import { resolveOfficialCodexPackageRuntime } from '../codex-runtime/resolve-cod
9
9
  import { withFileLock } from '../locks/file-lock.js';
10
10
  import { HOST_CAPABILITY_HOOK_PENDING_RUNTIME_FILENAME, HOST_CAPABILITY_MCP_SERVER, createHostCapabilityHookPendingRuntime, createHostCapabilityEventCollector, hostCapabilityCodexConfigArgs, inspectHostCapabilityRuntime, requestHostCapabilities } from '../agent-bridge/host-capability-runtime.js';
11
11
  import { uniqueStringsSorted as uniqueStrings } from '../text/strings.js';
12
+ import { narutoCredentialConfigArgs, resolveNarutoCredentialPolicy } from './naruto-host-credentials.js';
12
13
  export const OFFICIAL_SUBAGENT_WORKFLOW_SCHEMA = 'sks.subagent-workflow.v1';
13
14
  function officialSubagentMissionDir(root, missionId) {
14
15
  return path.join(root, '.sneakoscope', 'missions', missionId);
@@ -82,23 +83,33 @@ export function codexAppSessionKey(env = process.env) {
82
83
  const threadId = String(env.CODEX_THREAD_ID || '').trim();
83
84
  return threadId || null;
84
85
  }
86
+ export function defaultNarutoCredentialPolicy() {
87
+ return resolveNarutoCredentialPolicy({
88
+ args: [],
89
+ env: {},
90
+ defaultParentModel: NARUTO_PARENT_MODEL,
91
+ defaultParentEffort: NARUTO_PARENT_EFFORT,
92
+ defaultSubagentModel: DEFAULT_SUBAGENT_MODEL,
93
+ defaultSubagentEffort: DEFAULT_SUBAGENT_EFFORT
94
+ });
95
+ }
85
96
  export function buildOfficialSubagentCodexArgs(input) {
86
97
  const maxThreads = Math.max(1, Math.floor(input.maxThreads));
87
98
  const maV2Total = maxThreads + 1;
99
+ const policy = input.credentialPolicy ?? defaultNarutoCredentialPolicy();
88
100
  return [
89
101
  'exec',
90
102
  '--json',
91
103
  ...(input.workingDirectory ? ['-C', input.workingDirectory] : []),
92
- '-m', NARUTO_PARENT_MODEL,
93
- '-c', `model_reasoning_effort="${NARUTO_PARENT_EFFORT}"`,
94
- '-c', 'model_provider="openai"',
95
- '-c', 'forced_login_method="chatgpt"',
104
+ '-m', policy.parentModel,
105
+ '-c', `model_reasoning_effort="${policy.parentEffort}"`,
106
+ ...narutoCredentialConfigArgs(policy),
96
107
  '-c', `features.multi_agent_v2={enabled=true,max_concurrent_threads_per_session=${maV2Total},expose_spawn_agent_model_overrides=true}`,
97
108
  '-c', 'agents.enabled=true',
98
109
  '-c', `agents.max_concurrent_threads_per_session=${maxThreads}`,
99
110
  '-c', 'agents.max_depth=1',
100
- '-c', `agents.default_subagent_model="${DEFAULT_SUBAGENT_MODEL}"`,
101
- '-c', `agents.default_subagent_reasoning_effort="${DEFAULT_SUBAGENT_EFFORT}"`,
111
+ '-c', `agents.default_subagent_model="${policy.subagentModel}"`,
112
+ '-c', `agents.default_subagent_reasoning_effort="${policy.subagentEffort}"`,
102
113
  '-c', 'agents.interrupt_message=true',
103
114
  ...(input.projectConfigArgs || []),
104
115
  ...(input.hostCapabilityConfigArgs || []),
@@ -113,6 +124,10 @@ export function buildOfficialSubagentChildEnv(input = {}) {
113
124
  if (source[key] !== undefined)
114
125
  childEnv[key] = source[key];
115
126
  }
127
+ const providerEnvKey = input.credentialPolicy?.providerEnvKey;
128
+ if (providerEnvKey && source[providerEnvKey] !== undefined) {
129
+ childEnv[providerEnvKey] = source[providerEnvKey];
130
+ }
116
131
  childEnv.SKS_NARUTO_STANDALONE_CLI = '0';
117
132
  childEnv.SKS_NARUTO_PARENT_LAUNCH = '1';
118
133
  if (input.missionId)
@@ -343,11 +358,13 @@ export async function runOfficialSubagentWorkflow(input) {
343
358
  hostCapabilityLaunchNonce = randomId(32);
344
359
  hostCapabilityPendingDir = officialSubagentMissionDir(canonicalRoot, input.missionId);
345
360
  }
361
+ const credentialPolicy = input.credentialPolicy ?? defaultNarutoCredentialPolicy();
346
362
  const childEnv = buildOfficialSubagentChildEnv({
347
363
  ...(input.env ? { env: input.env } : {}),
348
364
  ...(input.missionId ? { missionId: input.missionId } : {}),
349
365
  ...(input.workflowRunId ? { workflowRunId: input.workflowRunId } : {}),
350
- ...(hostCapabilityLaunchNonce ? { hostCapabilityLaunchNonce } : {})
366
+ ...(hostCapabilityLaunchNonce ? { hostCapabilityLaunchNonce } : {}),
367
+ credentialPolicy
351
368
  });
352
369
  const outputSecretValues = hostCapabilityLaunchNonce
353
370
  ? [...inheritedSecretValues, hostCapabilityLaunchNonce]
@@ -450,7 +467,8 @@ export async function runOfficialSubagentWorkflow(input) {
450
467
  projectTrusted: input.projectTrusted === true,
451
468
  globalHostCapabilityConfigured
452
469
  }),
453
- hostCapabilityConfigArgs: hostCapabilityCodexConfigArgs(hostCapabilityRuntime)
470
+ hostCapabilityConfigArgs: hostCapabilityCodexConfigArgs(hostCapabilityRuntime),
471
+ credentialPolicy
454
472
  });
455
473
  const toolOutputRecovery = await inspectCodexLbCliLaunchRecovery({
456
474
  root: input.root,
@@ -1,158 +1,170 @@
1
- import path from 'node:path';
2
- import { readJson } from '../fsx.js';
1
+ import { CONTEXT_GRAPH_REPAIR_COMMAND } from '../triwiki/context-graph/contracts.js';
2
+ import { readContextGraphAttention } from '../triwiki/context-graph/projections/attention.js';
3
3
  import { asRecordOrEmpty as asRecord } from '../json/records.js';
4
4
  export const BOUNDED_TRIWIKI_ATTENTION_SCHEMA = 'sks.subagent-triwiki-attention.v1';
5
5
  export const DEFAULT_TRIWIKI_ATTENTION_ANCHOR_LIMIT = 8;
6
- export async function readBoundedTriwikiAttention(root, limit = DEFAULT_TRIWIKI_ATTENTION_ANCHOR_LIMIT, query = '') {
7
- const pack = await readJson(path.join(root, '.sneakoscope', 'wiki', 'context-pack.json'), null);
8
- return extractBoundedTriwikiAttention(pack, limit, query);
6
+ export const MAX_TRIWIKI_ATTENTION_ANCHOR_LIMIT = 16;
7
+ export const DEFAULT_TRIWIKI_ATTENTION_TOKEN_BUDGET = 2000;
8
+ export const TRIWIKI_ATTENTION_GRAPH_SOURCE = '.sneakoscope/wiki/context-graph.json';
9
+ export const TRIWIKI_ATTENTION_PACK_SOURCE = '.sneakoscope/wiki/context-pack.json';
10
+ export async function readBoundedTriwikiAttention(root, limit = DEFAULT_TRIWIKI_ATTENTION_ANCHOR_LIMIT, query = '', options = {}) {
11
+ const anchorLimit = normalizeLimit(limit);
12
+ const tokenBudget = Math.max(0, options.tokenBudget ?? DEFAULT_TRIWIKI_ATTENTION_TOKEN_BUDGET);
13
+ const result = await readContextGraphAttention({
14
+ root,
15
+ query,
16
+ limit: anchorLimit,
17
+ profile: options.profile ?? 'implementation',
18
+ tokenBudget,
19
+ risk: options.risk
20
+ }, options);
21
+ return {
22
+ schema: BOUNDED_TRIWIKI_ATTENTION_SCHEMA,
23
+ source: TRIWIKI_ATTENTION_GRAPH_SOURCE,
24
+ available: result.available,
25
+ attention_mode: result.available ? `context_graph:${result.profile}` : null,
26
+ anchor_limit: anchorLimit,
27
+ anchors: result.anchors.map(toAnchor),
28
+ hydration_policy: 'on_demand_only',
29
+ full_pack_injected: false,
30
+ reason: result.reason,
31
+ repair_command: CONTEXT_GRAPH_REPAIR_COMMAND,
32
+ snapshot_hash: result.snapshotHash,
33
+ snapshot_freshness: result.snapshotFreshness,
34
+ profile: result.available ? result.profile : null,
35
+ token_cost: result.tokenCost,
36
+ token_budget: result.tokenBudget
37
+ };
9
38
  }
10
- export function extractBoundedTriwikiAttention(value, limit = DEFAULT_TRIWIKI_ATTENTION_ANCHOR_LIMIT, query = '') {
39
+ export function extractBoundedTriwikiAttention(value, limit = DEFAULT_TRIWIKI_ATTENTION_ANCHOR_LIMIT, _retiredQueryHint) {
11
40
  const pack = asRecord(value);
12
41
  const attention = asRecord(pack.attention);
13
42
  const anchorLimit = normalizeLimit(limit);
14
- const hydrateHints = new Map();
15
- const hydrateOrder = new Map();
16
- for (const [index, row] of (Array.isArray(attention.hydrate_first) ? attention.hydrate_first : []).entries()) {
17
- if (!Array.isArray(row))
18
- continue;
19
- const id = text(row[0]);
20
- const hint = text(row[1]);
21
- if (id && hint) {
22
- hydrateHints.set(id, hint.slice(0, 240));
23
- hydrateOrder.set(id, index);
24
- }
43
+ const hints = new Map();
44
+ for (const row of rowsOf(attention.hydrate_first)) {
45
+ if (row.id && row.hydrate_hint)
46
+ hints.set(row.id, row.hydrate_hint.slice(0, 240));
25
47
  }
26
- const useFirst = [];
27
- for (const [index, row] of (Array.isArray(attention.use_first) ? attention.use_first : []).entries()) {
28
- if (!Array.isArray(row))
29
- continue;
30
- const id = text(row[0]);
31
- if (!id || useFirst.some((anchor) => anchor.id === id))
48
+ const anchors = [];
49
+ const seen = new Set();
50
+ for (const row of rowsOf(attention.use_first)) {
51
+ if (anchors.length >= anchorLimit)
52
+ break;
53
+ if (!row.id || seen.has(row.id))
32
54
  continue;
33
- useFirst.push({
34
- id,
35
- claim_hash: text(row[1]) || null,
36
- source_hash: text(row[2]) || null,
37
- hydrate_hint: hydrateHints.get(id) || null,
38
- order: index
39
- });
55
+ seen.add(row.id);
56
+ anchors.push({ ...row, hydrate_hint: row.hydrate_hint ?? hints.get(row.id) ?? null });
40
57
  }
41
- const anchors = selectAnchors(useFirst, hydrateHints, hydrateOrder, anchorLimit, query);
58
+ const tokenCost = anchors.reduce((sum, anchor) => sum + anchor.token_cost, 0);
42
59
  return {
43
60
  schema: BOUNDED_TRIWIKI_ATTENTION_SCHEMA,
44
- source: '.sneakoscope/wiki/context-pack.json',
61
+ source: TRIWIKI_ATTENTION_PACK_SOURCE,
45
62
  available: anchors.length > 0,
46
63
  attention_mode: text(attention.mode) || null,
47
64
  anchor_limit: anchorLimit,
48
65
  anchors,
49
66
  hydration_policy: 'on_demand_only',
50
- full_pack_injected: false
67
+ full_pack_injected: false,
68
+ reason: anchors.length > 0 ? null : 'context_graph_no_match',
69
+ repair_command: CONTEXT_GRAPH_REPAIR_COMMAND,
70
+ snapshot_hash: text(pack.snapshot_hash) || null,
71
+ snapshot_freshness: null,
72
+ profile: null,
73
+ token_cost: tokenCost,
74
+ token_budget: DEFAULT_TRIWIKI_ATTENTION_TOKEN_BUDGET
51
75
  };
52
76
  }
53
- function selectAnchors(useFirst, hydrateHints, hydrateOrder, limit, query) {
54
- const tokens = attentionQueryTokens(query);
55
- if (!tokens.length)
56
- return useFirst.slice(0, limit).map(stripOrder);
57
- const selected = [];
58
- const seen = new Set();
59
- for (const anchor of useFirst.slice(0, Math.min(3, limit))) {
60
- selected.push(stripOrder(anchor));
61
- seen.add(anchor.id);
62
- }
63
- const candidates = [];
64
- for (const anchor of useFirst) {
65
- if (seen.has(anchor.id))
66
- continue;
67
- candidates.push({
68
- ...stripOrder(anchor),
69
- score: attentionRelevance(anchor.id, anchor.hydrate_hint, tokens),
70
- order: anchor.order,
71
- priority: 0
72
- });
73
- }
74
- for (const [id, hint] of hydrateHints.entries()) {
75
- if (seen.has(id) || useFirst.some((anchor) => anchor.id === id))
76
- continue;
77
- const score = attentionRelevance(id, hint, tokens);
78
- if (score <= 0)
79
- continue;
80
- candidates.push({
81
- id,
82
- claim_hash: null,
83
- source_hash: null,
84
- hydrate_hint: hint,
85
- score,
86
- order: hydrateOrder.get(id) ?? Number.MAX_SAFE_INTEGER,
87
- priority: 1
88
- });
89
- }
90
- candidates.sort((left, right) => right.score - left.score || left.priority - right.priority || left.order - right.order);
91
- for (const candidate of candidates) {
92
- if (selected.length >= limit)
93
- break;
94
- if (candidate.score <= 0 || seen.has(candidate.id))
95
- continue;
96
- selected.push(stripRank(candidate));
97
- seen.add(candidate.id);
98
- }
99
- for (const anchor of useFirst) {
100
- if (selected.length >= limit)
101
- break;
102
- if (seen.has(anchor.id))
103
- continue;
104
- selected.push(stripOrder(anchor));
105
- seen.add(anchor.id);
106
- }
107
- return selected;
108
- }
109
- function attentionQueryTokens(value) {
110
- const stop = new Set([
111
- 'and', 'the', 'for', 'with', 'from', 'into', 'this', 'that', 'sks', 'src', 'core',
112
- '작업', '구현', '개선', '추가', '변경', '기능', '모든', '최대한'
113
- ]);
114
- return [...new Set(String(value || '')
115
- .normalize('NFKC')
116
- .toLowerCase()
117
- .replace(/[^\p{L}\p{N}]+/gu, ' ')
118
- .split(/\s+/)
119
- .map((token) => token.trim())
120
- .filter((token) => token.length >= 2 && !stop.has(token)))]
121
- .slice(0, 64);
122
- }
123
- function attentionRelevance(id, hint, tokens) {
124
- const haystack = `${id} ${hint || ''}`
125
- .normalize('NFKC')
126
- .toLowerCase()
127
- .replace(/[^\p{L}\p{N}]+/gu, ' ');
128
- const compact = haystack.replace(/\s+/g, '');
129
- return tokens.reduce((score, token) => {
130
- if (haystack.includes(token))
131
- return score + (token.length >= 5 ? 4 : 3);
132
- return compact.includes(token) ? score + 2 : score;
133
- }, 0);
134
- }
135
- function stripOrder(anchor) {
77
+ function toAnchor(anchor) {
136
78
  return {
137
79
  id: anchor.id,
138
80
  claim_hash: anchor.claim_hash,
139
81
  source_hash: anchor.source_hash,
140
- hydrate_hint: anchor.hydrate_hint
82
+ hydrate_hint: anchor.hydrate_hint,
83
+ reason_path: anchor.reason_path,
84
+ trust_score: anchor.trust_score,
85
+ freshness: anchor.freshness,
86
+ token_cost: anchor.token_cost,
87
+ provenance: anchor.provenance.map((ref) => ({
88
+ path: ref.path,
89
+ ...(ref.line === undefined ? {} : { line: ref.line }),
90
+ hash: ref.hash
91
+ }))
141
92
  };
142
93
  }
143
- function stripRank(anchor) {
94
+ function rowsOf(value) {
95
+ if (!Array.isArray(value))
96
+ return [];
97
+ const out = [];
98
+ for (const raw of value) {
99
+ const row = Array.isArray(raw) ? tupleRow(raw) : objectRow(raw);
100
+ if (row)
101
+ out.push(row);
102
+ }
103
+ return out;
104
+ }
105
+ function tupleRow(row) {
106
+ const id = text(row[0]);
107
+ if (!id)
108
+ return null;
109
+ const second = text(row[1]);
110
+ const third = text(row[2]);
111
+ const isHydrateRow = row.length <= 2;
144
112
  return {
145
- id: anchor.id,
146
- claim_hash: anchor.claim_hash,
147
- source_hash: anchor.source_hash,
148
- hydrate_hint: anchor.hydrate_hint
113
+ id,
114
+ claim_hash: isHydrateRow ? null : second || null,
115
+ source_hash: isHydrateRow ? null : third || null,
116
+ hydrate_hint: isHydrateRow ? second || null : null,
117
+ reason_path: [],
118
+ trust_score: 0,
119
+ freshness: 'unknown',
120
+ token_cost: 0,
121
+ provenance: []
149
122
  };
150
123
  }
124
+ function objectRow(raw) {
125
+ const row = asRecord(raw);
126
+ const id = text(row.id);
127
+ if (!id)
128
+ return null;
129
+ return {
130
+ id,
131
+ claim_hash: text(row.claim_hash) || null,
132
+ source_hash: text(row.source_hash) || null,
133
+ hydrate_hint: text(row.hydrate_hint) || null,
134
+ reason_path: Array.isArray(row.reason_path) ? row.reason_path.map(text).filter(Boolean) : [],
135
+ trust_score: number(row.trust_score),
136
+ freshness: freshnessOf(row.freshness),
137
+ token_cost: Math.max(0, Math.trunc(number(row.token_cost))),
138
+ provenance: provenanceOf(row.provenance)
139
+ };
140
+ }
141
+ function provenanceOf(value) {
142
+ if (!Array.isArray(value))
143
+ return [];
144
+ const out = [];
145
+ for (const raw of value) {
146
+ const ref = asRecord(raw);
147
+ const refPath = text(ref.path);
148
+ const hash = text(ref.hash);
149
+ if (!refPath || !hash)
150
+ continue;
151
+ const line = number(ref.line);
152
+ out.push({ path: refPath, ...(line > 0 ? { line } : {}), hash });
153
+ }
154
+ return out;
155
+ }
156
+ function freshnessOf(value) {
157
+ return value === 'fresh' || value === 'stale' ? value : 'unknown';
158
+ }
151
159
  function normalizeLimit(value) {
152
160
  const parsed = Number(value);
153
161
  if (!Number.isFinite(parsed))
154
162
  return DEFAULT_TRIWIKI_ATTENTION_ANCHOR_LIMIT;
155
- return Math.max(1, Math.min(16, Math.floor(parsed)));
163
+ return Math.max(1, Math.min(MAX_TRIWIKI_ATTENTION_ANCHOR_LIMIT, Math.floor(parsed)));
164
+ }
165
+ function number(value) {
166
+ const parsed = Number(value);
167
+ return Number.isFinite(parsed) ? parsed : 0;
156
168
  }
157
169
  function text(value) {
158
170
  return typeof value === 'string' ? value.trim() : '';