@nklisch/pi-enhanced 0.1.2 → 0.1.4

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 (236) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +3 -2
  3. package/node_modules/@nklisch/pi-clearance/README.md +10 -5
  4. package/node_modules/@nklisch/pi-clearance/docs/ARCHITECTURE.md +12 -7
  5. package/node_modules/@nklisch/pi-clearance/docs/CONFIGURATION.md +25 -35
  6. package/node_modules/@nklisch/pi-clearance/docs/DEVELOPER_GUIDE.md +30 -30
  7. package/node_modules/@nklisch/pi-clearance/docs/PACK_AUTHORING.md +5 -4
  8. package/node_modules/@nklisch/pi-clearance/docs/PRINCIPLES.md +14 -10
  9. package/node_modules/@nklisch/pi-clearance/docs/REFERENCE_PATTERNS.md +1 -1
  10. package/node_modules/@nklisch/pi-clearance/docs/REVIEWER_PROMPTS.md +12 -8
  11. package/node_modules/@nklisch/pi-clearance/docs/RULE_PACKS.md +1 -1
  12. package/node_modules/@nklisch/pi-clearance/docs/SPEC.md +7 -6
  13. package/node_modules/@nklisch/pi-clearance/docs/TUNE.md +2 -2
  14. package/node_modules/@nklisch/pi-clearance/docs/USER_GUIDE.md +7 -3
  15. package/node_modules/@nklisch/pi-clearance/docs/VISION.md +3 -3
  16. package/node_modules/@nklisch/pi-clearance/native/clearance-core.darwin-arm64.node +0 -0
  17. package/node_modules/@nklisch/pi-clearance/native/clearance-core.darwin-x64.node +0 -0
  18. package/node_modules/@nklisch/pi-clearance/native/clearance-core.linux-arm64-gnu.node +0 -0
  19. package/node_modules/@nklisch/pi-clearance/native/clearance-core.linux-x64-gnu.node +0 -0
  20. package/node_modules/@nklisch/pi-clearance/native/clearance-core.win32-arm64-msvc.node +0 -0
  21. package/node_modules/@nklisch/pi-clearance/native/clearance-core.win32-x64-msvc.node +0 -0
  22. package/node_modules/@nklisch/pi-clearance/package.json +9 -5
  23. package/node_modules/@nklisch/pi-clearance/src/config/config-command-plans.ts +79 -3
  24. package/node_modules/@nklisch/pi-clearance/src/config/config-command-writer.ts +7 -1
  25. package/node_modules/@nklisch/pi-clearance/src/config/defaults.ts +42 -0
  26. package/node_modules/@nklisch/pi-clearance/src/config/gated-tools.ts +7 -0
  27. package/node_modules/@nklisch/pi-clearance/src/config/loader.ts +7 -1
  28. package/node_modules/@nklisch/pi-clearance/src/config/pack-enablement-writer.ts +1 -0
  29. package/node_modules/@nklisch/pi-clearance/src/config/paths.ts +14 -3
  30. package/node_modules/@nklisch/pi-clearance/src/config/persistence.ts +135 -0
  31. package/node_modules/@nklisch/pi-clearance/src/config/schema.ts +74 -26
  32. package/node_modules/@nklisch/pi-clearance/src/contracts/AdversarialCase.ts +1 -1
  33. package/node_modules/@nklisch/pi-clearance/src/contracts/AdversarialCaseCategory.ts +1 -1
  34. package/node_modules/@nklisch/pi-clearance/src/contracts/AdversarialCaseExpectation.ts +1 -1
  35. package/node_modules/@nklisch/pi-clearance/src/contracts/AdversarialCaseResult.ts +1 -1
  36. package/node_modules/@nklisch/pi-clearance/src/contracts/AdversarialCaseResultOutcome.ts +1 -1
  37. package/node_modules/@nklisch/pi-clearance/src/contracts/AdversarialCaseSource.ts +1 -1
  38. package/node_modules/@nklisch/pi-clearance/src/contracts/AdversarialValidationReport.ts +1 -1
  39. package/node_modules/@nklisch/pi-clearance/src/contracts/AdversarialValidationStatus.ts +1 -1
  40. package/node_modules/@nklisch/pi-clearance/src/contracts/BashBlock.ts +1 -1
  41. package/node_modules/@nklisch/pi-clearance/src/contracts/BashCommandShape.ts +1 -1
  42. package/node_modules/@nklisch/pi-clearance/src/contracts/BashConditionalArm.ts +1 -1
  43. package/node_modules/@nklisch/pi-clearance/src/contracts/BashControlConstruct.ts +1 -1
  44. package/node_modules/@nklisch/pi-clearance/src/contracts/BashFlag.ts +1 -1
  45. package/node_modules/@nklisch/pi-clearance/src/contracts/BashForLoopKeywordSpans.ts +1 -1
  46. package/node_modules/@nklisch/pi-clearance/src/contracts/BashIteratorEntry.ts +1 -1
  47. package/node_modules/@nklisch/pi-clearance/src/contracts/BashIteratorEntryKind.ts +1 -1
  48. package/node_modules/@nklisch/pi-clearance/src/contracts/BashListOperator.ts +1 -1
  49. package/node_modules/@nklisch/pi-clearance/src/contracts/BashLoopVariableReference.ts +1 -1
  50. package/node_modules/@nklisch/pi-clearance/src/contracts/BashPathFact.ts +1 -1
  51. package/node_modules/@nklisch/pi-clearance/src/contracts/BashPathFactContext.ts +1 -1
  52. package/node_modules/@nklisch/pi-clearance/src/contracts/BashPathFactProvenance.ts +1 -1
  53. package/node_modules/@nklisch/pi-clearance/src/contracts/BashPathFactProvenanceEntry.ts +1 -1
  54. package/node_modules/@nklisch/pi-clearance/src/contracts/BashPathFacts.ts +1 -1
  55. package/node_modules/@nklisch/pi-clearance/src/contracts/BashPipeline.ts +1 -1
  56. package/node_modules/@nklisch/pi-clearance/src/contracts/BashStage.ts +1 -1
  57. package/node_modules/@nklisch/pi-clearance/src/contracts/BashStageProgram.ts +1 -1
  58. package/node_modules/@nklisch/pi-clearance/src/contracts/BlockOperator.ts +1 -1
  59. package/node_modules/@nklisch/pi-clearance/src/contracts/CapturedOutcome.ts +1 -1
  60. package/node_modules/@nklisch/pi-clearance/src/contracts/CommandFamilyKey.ts +1 -1
  61. package/node_modules/@nklisch/pi-clearance/src/contracts/CommandFamilySummary.ts +1 -1
  62. package/node_modules/@nklisch/pi-clearance/src/contracts/CompileError.ts +1 -1
  63. package/node_modules/@nklisch/pi-clearance/src/contracts/CompositionOperator.ts +1 -1
  64. package/node_modules/@nklisch/pi-clearance/src/contracts/CompoundBodyReason.ts +1 -1
  65. package/node_modules/@nklisch/pi-clearance/src/contracts/CompoundFeatureReason.ts +1 -1
  66. package/node_modules/@nklisch/pi-clearance/src/contracts/CompoundForm.ts +1 -1
  67. package/node_modules/@nklisch/pi-clearance/src/contracts/CompoundIteratorReason.ts +1 -1
  68. package/node_modules/@nklisch/pi-clearance/src/contracts/CorpusEntry.ts +1 -1
  69. package/node_modules/@nklisch/pi-clearance/src/contracts/CorpusExpectedLabel.ts +1 -1
  70. package/node_modules/@nklisch/pi-clearance/src/contracts/CorpusFidelity.ts +1 -1
  71. package/node_modules/@nklisch/pi-clearance/src/contracts/CorpusQueryModel.ts +1 -1
  72. package/node_modules/@nklisch/pi-clearance/src/contracts/CorpusQuerySummary.ts +1 -1
  73. package/node_modules/@nklisch/pi-clearance/src/contracts/CorpusQuerySummarySnapshot.ts +1 -1
  74. package/node_modules/@nklisch/pi-clearance/src/contracts/CorpusRecord.ts +1 -1
  75. package/node_modules/@nklisch/pi-clearance/src/contracts/CorpusSource.ts +1 -1
  76. package/node_modules/@nklisch/pi-clearance/src/contracts/CountByLabel.ts +1 -1
  77. package/node_modules/@nklisch/pi-clearance/src/contracts/Decision.ts +1 -1
  78. package/node_modules/@nklisch/pi-clearance/src/contracts/DecisionEffect.ts +1 -1
  79. package/node_modules/@nklisch/pi-clearance/src/contracts/DecisionProvenance.ts +1 -1
  80. package/node_modules/@nklisch/pi-clearance/src/contracts/DecisionSource.ts +1 -1
  81. package/node_modules/@nklisch/pi-clearance/src/contracts/DiagnosticSeverity.ts +1 -1
  82. package/node_modules/@nklisch/pi-clearance/src/contracts/EffectivePolicy.ts +1 -1
  83. package/node_modules/@nklisch/pi-clearance/src/contracts/EmbeddedShellProjection.ts +1 -1
  84. package/node_modules/@nklisch/pi-clearance/src/contracts/EnvironmentAssignment.ts +1 -1
  85. package/node_modules/@nklisch/pi-clearance/src/contracts/IteratorSourceKind.ts +1 -1
  86. package/node_modules/@nklisch/pi-clearance/src/contracts/LoopQuoteKind.ts +1 -1
  87. package/node_modules/@nklisch/pi-clearance/src/contracts/LoopVariableUnknownReason.ts +1 -1
  88. package/node_modules/@nklisch/pi-clearance/src/contracts/MatcherExpr.ts +1 -1
  89. package/node_modules/@nklisch/pi-clearance/src/contracts/MutationShapeKind.ts +1 -1
  90. package/node_modules/@nklisch/pi-clearance/src/contracts/MutationTrustBoundaryClassification.ts +1 -1
  91. package/node_modules/@nklisch/pi-clearance/src/contracts/MutationTrustBoundaryKind.ts +1 -1
  92. package/node_modules/@nklisch/pi-clearance/src/contracts/PackCompileResult.ts +1 -1
  93. package/node_modules/@nklisch/pi-clearance/src/contracts/PackWarningLevel.ts +1 -1
  94. package/node_modules/@nklisch/pi-clearance/src/contracts/ParsedEvidence.ts +1 -1
  95. package/node_modules/@nklisch/pi-clearance/src/contracts/ParsedShapeSummary.ts +1 -1
  96. package/node_modules/@nklisch/pi-clearance/src/contracts/PathAccess.ts +1 -1
  97. package/node_modules/@nklisch/pi-clearance/src/contracts/PathFactProjectScope.ts +1 -1
  98. package/node_modules/@nklisch/pi-clearance/src/contracts/PathFactsRequirement.ts +1 -1
  99. package/node_modules/@nklisch/pi-clearance/src/contracts/PathFactsResolvedConfig.ts +1 -1
  100. package/node_modules/@nklisch/pi-clearance/src/contracts/PathNormalization.ts +1 -1
  101. package/node_modules/@nklisch/pi-clearance/src/contracts/PathScope.ts +1 -1
  102. package/node_modules/@nklisch/pi-clearance/src/contracts/PathScopeMatcherExpr.ts +1 -1
  103. package/node_modules/@nklisch/pi-clearance/src/contracts/PathScopeMatcherMode.ts +1 -1
  104. package/node_modules/@nklisch/pi-clearance/src/contracts/PathUnknownReason.ts +1 -1
  105. package/node_modules/@nklisch/pi-clearance/src/contracts/PathUsageKind.ts +1 -1
  106. package/node_modules/@nklisch/pi-clearance/src/contracts/PiBuiltinToolOperation.ts +1 -1
  107. package/node_modules/@nklisch/pi-clearance/src/contracts/PiBuiltinToolPathInput.ts +1 -1
  108. package/node_modules/@nklisch/pi-clearance/src/contracts/PiBuiltinToolShape.ts +1 -1
  109. package/node_modules/@nklisch/pi-clearance/src/contracts/PiBuiltinToolSpec.ts +1 -1
  110. package/node_modules/@nklisch/pi-clearance/src/contracts/PiFileMutationToolName.ts +1 -1
  111. package/node_modules/@nklisch/pi-clearance/src/contracts/PiToolMutationFacts.ts +1 -1
  112. package/node_modules/@nklisch/pi-clearance/src/contracts/PolicyPack.ts +1 -1
  113. package/node_modules/@nklisch/pi-clearance/src/contracts/PolicyPackDocLink.ts +1 -1
  114. package/node_modules/@nklisch/pi-clearance/src/contracts/PolicyPackExample.ts +1 -1
  115. package/node_modules/@nklisch/pi-clearance/src/contracts/PolicyPackMetadata.ts +1 -1
  116. package/node_modules/@nklisch/pi-clearance/src/contracts/PolicyPackWarning.ts +1 -1
  117. package/node_modules/@nklisch/pi-clearance/src/contracts/PolicyRule.ts +1 -1
  118. package/node_modules/@nklisch/pi-clearance/src/contracts/ProposalNotRunReason.ts +1 -1
  119. package/node_modules/@nklisch/pi-clearance/src/contracts/ProposalNotRunSeverity.ts +1 -1
  120. package/node_modules/@nklisch/pi-clearance/src/contracts/QuoteKind.ts +1 -1
  121. package/node_modules/@nklisch/pi-clearance/src/contracts/Redirect.ts +1 -1
  122. package/node_modules/@nklisch/pi-clearance/src/contracts/RedirectStream.ts +1 -1
  123. package/node_modules/@nklisch/pi-clearance/src/contracts/RedirectTargetKind.ts +1 -1
  124. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayCorpus.ts +1 -1
  125. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayDelta.ts +1 -1
  126. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayDeltaBlockedSummary.ts +1 -1
  127. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayDeltaChangedRecord.ts +1 -1
  128. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayDeltaFamily.ts +1 -1
  129. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayDeltaImprovement.ts +1 -1
  130. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayDeltaRegression.ts +1 -1
  131. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayDeltaStatus.ts +1 -1
  132. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayDeltaTransitionCount.ts +1 -1
  133. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayPathFacts.ts +1 -1
  134. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayReviewerMode.ts +1 -1
  135. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayReviewerOutcome.ts +1 -1
  136. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayStatus.ts +1 -1
  137. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayedDecision.ts +1 -1
  138. package/node_modules/@nklisch/pi-clearance/src/contracts/ShapeDiagnostic.ts +1 -1
  139. package/node_modules/@nklisch/pi-clearance/src/contracts/SourceFidelity.ts +1 -1
  140. package/node_modules/@nklisch/pi-clearance/src/contracts/SourceSpan.ts +1 -1
  141. package/node_modules/@nklisch/pi-clearance/src/contracts/Substitution.ts +1 -1
  142. package/node_modules/@nklisch/pi-clearance/src/contracts/SubstitutionKind.ts +1 -1
  143. package/node_modules/@nklisch/pi-clearance/src/contracts/ToolCallIdentity.ts +1 -1
  144. package/node_modules/@nklisch/pi-clearance/src/contracts/ToolPathAccess.ts +1 -1
  145. package/node_modules/@nklisch/pi-clearance/src/contracts/ToolPathFact.ts +1 -1
  146. package/node_modules/@nklisch/pi-clearance/src/contracts/ToolPathFactContext.ts +1 -1
  147. package/node_modules/@nklisch/pi-clearance/src/contracts/ToolPathFacts.ts +1 -1
  148. package/node_modules/@nklisch/pi-clearance/src/contracts/ToolPathUsage.ts +1 -1
  149. package/node_modules/@nklisch/pi-clearance/src/contracts/ToolShape.ts +1 -1
  150. package/node_modules/@nklisch/pi-clearance/src/contracts/UnknownPathBehavior.ts +1 -1
  151. package/node_modules/@nklisch/pi-clearance/src/contracts/UnknownToolShape.ts +1 -1
  152. package/node_modules/@nklisch/pi-clearance/src/contracts/index.ts +1 -1
  153. package/node_modules/@nklisch/pi-clearance/src/index.ts +33 -2
  154. package/node_modules/@nklisch/pi-clearance/src/native/loader.ts +29 -42
  155. package/node_modules/@nklisch/pi-clearance/src/replay/reviewer-config-proposals.ts +7 -4
  156. package/node_modules/@nklisch/pi-clearance/src/runtime/allow-request-message.ts +21 -0
  157. package/node_modules/@nklisch/pi-clearance/src/runtime/auto-reviewer-read-models.ts +165 -8
  158. package/node_modules/@nklisch/pi-clearance/src/runtime/command-registry.ts +1 -1
  159. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/allow.ts +26 -2
  160. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/packs.ts +6 -6
  161. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/scope.ts +5 -5
  162. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings/actions.ts +11 -0
  163. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings/dispatcher.ts +274 -1
  164. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings/model-options.ts +52 -0
  165. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings/native-ui.ts +54 -67
  166. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings/panels/reviewer.ts +19 -3
  167. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings/panels/scope.ts +2 -11
  168. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings/panels.ts +10 -1
  169. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings/read-model.ts +47 -13
  170. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings.ts +32 -37
  171. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/status.ts +3 -0
  172. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/types.ts +14 -5
  173. package/node_modules/@nklisch/pi-clearance/src/runtime/handler.ts +36 -0
  174. package/node_modules/@nklisch/pi-clearance/src/runtime/mode-prompt.ts +0 -1
  175. package/node_modules/@nklisch/pi-clearance/src/runtime/model-adapter.ts +5 -1
  176. package/node_modules/@nklisch/pi-clearance/src/runtime/reviewer-context.ts +73 -10
  177. package/node_modules/@nklisch/pi-clearance/src/runtime/reviewer-prompts.ts +186 -175
  178. package/node_modules/@nklisch/pi-clearance/src/runtime/reviewer.ts +26 -6
  179. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/state/local-lock-filesystem.d.ts +23 -1
  180. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/state/local-lock-filesystem.js +46 -50
  181. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/state/local-lock-filesystem.js.map +1 -1
  182. package/node_modules/@nklisch/pi-plugins/dist/runtime/mcp/pi-mcp-adapter-package.js +2 -2
  183. package/node_modules/@nklisch/pi-plugins/dist/runtime/mcp/pi-mcp-adapter-runtime.js +2 -2
  184. package/node_modules/@nklisch/pi-plugins/dist/runtime/subagents/pi-subagents-lifecycle.js +1 -1
  185. package/node_modules/@nklisch/pi-plugins/dist/runtime/subagents/pi-subagents-package.js +2 -2
  186. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/README.md +41 -31
  187. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/dist/public.d.ts +2 -1
  188. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/FORK-MAINTENANCE.md +17 -11
  189. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/VISION.md +14 -7
  190. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/architecture.md +11 -19
  191. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/comparison-with-upstream.md +58 -53
  192. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/package.json +2 -2
  193. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/config/agent-type-resolution.ts +43 -0
  194. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/config/agent-types.ts +11 -6
  195. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/config/custom-agents.ts +13 -6
  196. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/config/default-agents.ts +0 -52
  197. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/config/thinking-levels.ts +12 -0
  198. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/handlers/interrupt.ts +5 -2
  199. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/handlers/lifecycle.ts +3 -3
  200. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/index.ts +7 -6
  201. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/create-subagent-session.ts +7 -2
  202. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/parent-snapshot.ts +11 -3
  203. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/subagent-manager.ts +26 -56
  204. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/subagent-session.ts +48 -9
  205. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/subagent-state.ts +32 -0
  206. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/subagent.ts +105 -17
  207. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/observation/composite-subagent-observer.ts +4 -0
  208. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/observation/notification.ts +50 -46
  209. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/observation/renderer.ts +1 -1
  210. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/observation/subagent-events-observer.ts +15 -0
  211. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/runtime.ts +2 -0
  212. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/service/service-adapter.ts +35 -6
  213. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/service/service.ts +1 -0
  214. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/session/model-label.ts +19 -0
  215. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/session/prompts.ts +24 -1
  216. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/session/session-config.ts +11 -6
  217. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/settings.ts +70 -3
  218. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/agent-tool.ts +27 -10
  219. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/background-spawner.ts +6 -2
  220. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/foreground-runner.ts +12 -3
  221. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/get-result-report.ts +17 -3
  222. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/get-result-tool.ts +13 -20
  223. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/result-renderer.ts +6 -1
  224. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/spawn-config.ts +16 -16
  225. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/steer-tool.ts +12 -4
  226. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/types.ts +2 -2
  227. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/agent-widget.ts +24 -11
  228. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/display.ts +10 -7
  229. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/glyphs.ts +7 -0
  230. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/session-navigation.ts +34 -25
  231. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/session-navigator.ts +30 -10
  232. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/subagents-settings.ts +58 -13
  233. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/widget-renderer.ts +32 -19
  234. package/node_modules/@nklisch/pi-plugins/package.json +3 -3
  235. package/package.json +1 -1
  236. package/test/verify-bundle.mjs +28 -1
@@ -4,7 +4,7 @@ Pi Clearance makes long-running Pi sessions feel fast without turning tool calls
4
4
 
5
5
  ## Product shape
6
6
 
7
- 1. **Structural analysis** — bash and typed Pi tools become inspectable shapes; parser uncertainty reviews or denies.
7
+ 1. **Structural analysis** — Bash and explicitly opted-in typed Pi tools become inspectable shapes; parser uncertainty reviews or denies. Non-Bash tools not opted in remain host-approved bypasses.
8
8
  2. **Broad built-in baseline** — common development, project-scoped mutation, network-read, typed research, non-secret home/support reads, and safe-home workflows are active by default while the sealed floor remains non-overridable.
9
9
  3. **One behavioral dial** — `mode: off | ask | auto` globally controls only dispatch of deterministic `review` results.
10
10
  4. **Model reviewer** — Auto mode resolves one uncertain call with configurable prompt/context/model details; model decisions never become policy.
@@ -16,8 +16,8 @@ Pi Clearance makes long-running Pi sessions feel fast without turning tool calls
16
16
  - **Ask:** known-safe calls run; uncertainty asks the user and blocks unattended calls.
17
17
  - **Auto:** known-safe calls run; uncertainty goes model-first, then human/block fallback.
18
18
 
19
- The sealed floor always wins. Invalid config fails closed to floor-only policy. Unknown (non-bash) tools default to allow through the config-file-only `unknownToolPosture` setting.
19
+ The sealed floor always wins. Invalid config fails closed to floor-only policy. Non-Bash tools bypass Clearance by default and can be opted in by exact name through global `gatedTools`; Bash remains fully gated. `unknownToolPosture` applies only to opted-in unknown tools.
20
20
 
21
21
  ## Trust and growth
22
22
 
23
- User-owned global/project config may widen policy. Repository policy and installed packs cannot silently widen it. Tune replay, adversarial checks, explicit confirmation, and post-write validation make policy growth auditable.
23
+ User-owned global/project config may widen policy. Repository policy and installed packs cannot silently widen it. Persisted settings written through Clearance contain only non-default choices, while runtime normalization supplies the rest. Package installation neither changes config nor enables packs. Tune replay, adversarial checks, explicit confirmation, and post-write validation make policy growth auditable. The default non-Bash bypass is an intentional published behavioral break requiring next-minor release communication.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nklisch/pi-clearance",
3
- "version": "0.1.0",
3
+ "version": "0.2.2",
4
4
  "description": "Configurable auto-reviewer Pi extension for parsed, structural command policy",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -15,13 +15,17 @@
15
15
  "tree-sitter"
16
16
  ],
17
17
  "engines": {
18
- "node": ">=22"
18
+ "node": ">=22.18"
19
19
  },
20
20
  "napi": {
21
21
  "binaryName": "clearance-core",
22
22
  "targets": [
23
23
  "x86_64-unknown-linux-gnu",
24
- "aarch64-apple-darwin"
24
+ "aarch64-unknown-linux-gnu",
25
+ "x86_64-apple-darwin",
26
+ "aarch64-apple-darwin",
27
+ "x86_64-pc-windows-msvc",
28
+ "aarch64-pc-windows-msvc"
25
29
  ]
26
30
  },
27
31
  "exports": {
@@ -59,8 +63,7 @@
59
63
  "cargo:fmt": "cargo fmt --all -- --check"
60
64
  },
61
65
  "dependencies": {
62
- "@sinclair/typebox": "^0.34.49",
63
- "jiti": "^2.7.0"
66
+ "@sinclair/typebox": "^0.34.49"
64
67
  },
65
68
  "peerDependencies": {
66
69
  "@earendil-works/pi-ai": "*",
@@ -73,6 +76,7 @@
73
76
  "@earendil-works/pi-tui": "0.82.0",
74
77
  "@napi-rs/cli": "^3.7.4",
75
78
  "@types/node": "^24.0.0",
79
+ "jiti": "^2.7.0",
76
80
  "tsx": "^4.23.1",
77
81
  "typescript": "^5.9.3",
78
82
  "vitest": "^4.0.0",
@@ -1,5 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
 
3
+ import { DEFAULT_PROJECT_SCOPE_BEHAVIOR } from "./defaults.ts";
4
+ import { isExactNonBashToolName } from "./gated-tools.ts";
3
5
  import type { JsonPatchOperation } from "../replay/proposal-schema.ts";
4
6
  import { isHighCostReviewerModel } from "../runtime/reviewer-model.ts";
5
7
  import type {
@@ -91,10 +93,12 @@ export function inferScopePreset(
91
93
  const fields = SCOPE_PRESET_FIELDS[preset];
92
94
  if (
93
95
  scope.safeHomeUseDefaults === fields.safeHomeUseDefaults &&
94
- (scope.agentSupportUseDefaults ?? true) ===
96
+ (scope.agentSupportUseDefaults !== false) ===
95
97
  fields.agentSupportUseDefaults &&
96
- (scope.homePathBehavior ?? "allow") === fields.homePathBehavior &&
97
- (scope.sensitivePathBehavior ?? "review") ===
98
+ (scope.homePathBehavior ?? DEFAULT_PROJECT_SCOPE_BEHAVIOR.homePathBehavior) ===
99
+ fields.homePathBehavior &&
100
+ (scope.sensitivePathBehavior ??
101
+ DEFAULT_PROJECT_SCOPE_BEHAVIOR.sensitivePathBehavior) ===
98
102
  fields.sensitivePathBehavior &&
99
103
  scope.unknownPathBehavior === fields.unknownPathBehavior
100
104
  ) {
@@ -137,6 +141,10 @@ export type ReviewNoteDisplayCommandChange =
137
141
  | { readonly kind: "show-model-label"; readonly enabled: boolean }
138
142
  | { readonly kind: "accent"; readonly enabled: boolean };
139
143
 
144
+ export type GatedToolCommandChange =
145
+ | { readonly kind: "add"; readonly toolName: string }
146
+ | { readonly kind: "remove"; readonly toolName: string };
147
+
140
148
  export type ReviewerCommandChange =
141
149
  | { readonly kind: "prompt-posture"; readonly promptPosture: string }
142
150
  | { readonly kind: "context-mode"; readonly contextMode: ReviewerContextMode }
@@ -181,6 +189,74 @@ export type PlanProjectScopeCommandChangeResult =
181
189
  }
182
190
  | { readonly ok: false; readonly reason: string };
183
191
 
192
+ export function planGatedToolCommandChange(input: {
193
+ readonly change: GatedToolCommandChange;
194
+ readonly resolvedConfig: ResolvedConfig;
195
+ }):
196
+ | { readonly ok: true; readonly plan: ConfigCommandPlan }
197
+ | { readonly ok: false; readonly reason: string } {
198
+ const toolName = input.change.toolName;
199
+ if (!isExactNonBashToolName(toolName)) {
200
+ return {
201
+ ok: false,
202
+ reason:
203
+ "gatedTools accepts exact non-Bash tool names only; wildcards, whitespace, and bash are not allowed",
204
+ };
205
+ }
206
+
207
+ const snapshots = input.resolvedConfig.sourceSnapshots;
208
+ if (snapshots === undefined) {
209
+ return {
210
+ ok: false,
211
+ reason:
212
+ "resolved config did not include source snapshots; reload config before planning a gated-tool write",
213
+ };
214
+ }
215
+
216
+ const before = cloneJsonish({
217
+ ...snapshots.global,
218
+ gatedTools: [...(snapshots.global.gatedTools ?? [])],
219
+ });
220
+ const after = cloneJsonish(before) as Mutable<GlobalConfig>;
221
+ const current = [...before.gatedTools];
222
+ const next =
223
+ input.change.kind === "add"
224
+ ? current.includes(toolName)
225
+ ? current
226
+ : [...current, toolName]
227
+ : current.filter((candidate) => candidate !== toolName);
228
+ after.gatedTools = next;
229
+ const patch = fieldPatch("/gatedTools", current, next);
230
+ const title =
231
+ input.change.kind === "add"
232
+ ? `Gate non-Bash tool ${toolName}`
233
+ : `Stop gating non-Bash tool ${toolName}`;
234
+ const planWithoutId = {
235
+ target: { kind: "global", path: snapshots.paths.globalConfigFile },
236
+ title,
237
+ summary:
238
+ patch.length === 0
239
+ ? `${title}; no config write is needed.`
240
+ : `${title}; only this exact tool name will use Clearance analysis and policy.`,
241
+ patch,
242
+ before,
243
+ after,
244
+ requiredAcknowledgementCodes: [],
245
+ warnings: [],
246
+ } satisfies Omit<ConfigCommandPlan, "id">;
247
+
248
+ return {
249
+ ok: true,
250
+ plan: {
251
+ id: deterministicPlanId({
252
+ gatedToolChange: input.change,
253
+ ...planWithoutId,
254
+ }),
255
+ ...planWithoutId,
256
+ },
257
+ };
258
+ }
259
+
184
260
  export function planModeCommandChange(input: {
185
261
  readonly mode: ClearanceMode;
186
262
  readonly resolvedConfig: ResolvedConfig;
@@ -12,6 +12,10 @@ import type { TSchema } from "@sinclair/typebox";
12
12
 
13
13
  import type { JsonPatchOperation } from "../replay/proposal-schema.ts";
14
14
  import type { ResolvedConfig } from "./loader.ts";
15
+ import {
16
+ type ConfigPersistenceKind,
17
+ serializeSparseConfigText,
18
+ } from "./persistence.ts";
15
19
  import {
16
20
  type GlobalConfig,
17
21
  GlobalConfigSchema,
@@ -101,6 +105,7 @@ export type ConfigCommandApplyResult =
101
105
  interface AtomicConfigWriteInput {
102
106
  readonly planId: string;
103
107
  readonly targetPath: string;
108
+ readonly configKind: ConfigPersistenceKind;
104
109
  readonly value: GlobalConfig | ProjectOverlayConfig;
105
110
  readonly hadExistingFile: boolean;
106
111
  readonly reloadConfig: () => Promise<ResolvedConfig>;
@@ -184,6 +189,7 @@ export async function applyConfigCommandPlan(
184
189
  return await writeConfigTargetAndValidate({
185
190
  planId: plan.id,
186
191
  targetPath: plan.target.path,
192
+ configKind: plan.target.kind,
187
193
  value: patched.value,
188
194
  hadExistingFile: readResult.exists,
189
195
  reloadConfig: dependencies.reloadConfig,
@@ -207,7 +213,7 @@ export async function writeConfigTargetAndValidate(
207
213
  await mkdir(path.dirname(input.targetPath), { recursive: true });
208
214
  await writeFile(
209
215
  tempPath,
210
- `${JSON.stringify(input.value, null, 2)}\n`,
216
+ serializeSparseConfigText(input.configKind, input.value),
211
217
  "utf8",
212
218
  );
213
219
  tempPresent = true;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * User-visible defaults shared by schema normalization and status/read models.
3
+ * Keeping these values in one module prevents a new schema default from being
4
+ * mistaken for a customization by diagnostics.
5
+ */
6
+ export const DEFAULT_REVIEWER_PROMPT_POSTURE = "reviewer.default" as const;
7
+ export const DEFAULT_REVIEWER_CONTEXT_MODE = "recentContext" as const;
8
+
9
+ export const DEFAULT_REVIEWER_TOKEN_BUDGET = {
10
+ window: "24h",
11
+ limit: null,
12
+ } as const;
13
+
14
+ export const DEFAULT_REVIEWER_RECENT_CONTEXT = {
15
+ decisionLimit: 25,
16
+ decisionWindow: "2h",
17
+ conversationTurns: 3,
18
+ userTurns: 5,
19
+ conversationCharLimit: 6000,
20
+ } as const;
21
+
22
+ export const DEFAULT_REVIEWER_ESCALATION = {
23
+ enabled: true,
24
+ denialLimit: 3,
25
+ window: "10m",
26
+ } as const;
27
+
28
+ export const DEFAULT_UNKNOWN_TOOL_POSTURE = "allow" as const;
29
+
30
+ export const DEFAULT_PROJECT_SCOPE_BEHAVIOR = {
31
+ safeHomeUseDefaults: true,
32
+ agentSupportUseDefaults: true,
33
+ unknownPathBehavior: "review",
34
+ sensitivePathBehavior: "review",
35
+ homePathBehavior: "allow",
36
+ } as const;
37
+
38
+ export const DEFAULT_REVIEW_NOTE_DISPLAY = {
39
+ mode: "reason+accent",
40
+ showModelLabel: false,
41
+ accent: true,
42
+ } as const;
@@ -0,0 +1,7 @@
1
+ /** Exact-name syntax for the global non-Bash Clearance opt-in list. */
2
+ export const EXACT_NON_BASH_TOOL_NAME_PATTERN =
3
+ "^(?!bash$)[^\\s*?\\[\\]{}]+$" as const;
4
+
5
+ export function isExactNonBashToolName(value: string): boolean {
6
+ return value.length > 0 && new RegExp(EXACT_NON_BASH_TOOL_NAME_PATTERN).test(value);
7
+ }
@@ -3,6 +3,7 @@ import { homedir, tmpdir } from "node:os";
3
3
  import path from "node:path";
4
4
  import type { Static, TSchema } from "@sinclair/typebox";
5
5
  import type { DecisionEffect } from "../policy/core.ts";
6
+ import { DEFAULT_UNKNOWN_TOOL_POSTURE } from "./defaults.ts";
6
7
  import {
7
8
  isHighCostReviewerModel,
8
9
  parseModelSpec,
@@ -69,6 +70,7 @@ export interface ResolvedReviewerConfig {
69
70
  readonly decisionLimit: number;
70
71
  readonly decisionWindow: string;
71
72
  readonly conversationTurns: number;
73
+ readonly userTurns?: number;
72
74
  readonly conversationCharLimit: number;
73
75
  };
74
76
  readonly escalation: {
@@ -133,6 +135,8 @@ export interface ResolvedConfig {
133
135
  */
134
136
  readonly sourceSnapshots?: ResolvedConfigSourceSnapshots;
135
137
  readonly mode: ClearanceMode;
138
+ /** Exact non-Bash tool names explicitly opted into Clearance gating. */
139
+ readonly gatedTools?: readonly string[];
136
140
  readonly unknownToolPosture: DecisionEffect;
137
141
  readonly projectScope: ResolvedProjectScope;
138
142
  readonly packEnablement: ResolvedPackEnablement;
@@ -250,7 +254,9 @@ export async function loadConfig(
250
254
  repository: repositoryPolicy,
251
255
  },
252
256
  mode,
253
- unknownToolPosture: globalConfig.unknownToolPosture ?? "allow",
257
+ gatedTools: [...globalConfig.gatedTools],
258
+ unknownToolPosture:
259
+ globalConfig.unknownToolPosture ?? DEFAULT_UNKNOWN_TOOL_POSTURE,
254
260
  projectScope: projectScopeResolution.scope,
255
261
  packEnablement,
256
262
  globalPacks: withPackSource(globalConfig.packs, "user-global"),
@@ -113,6 +113,7 @@ export async function applyPackEnablementPlan(
113
113
  return await writeConfigTargetAndValidate({
114
114
  planId: plan.id,
115
115
  targetPath: plan.targetPath,
116
+ configKind: plan.request.scope,
116
117
  value: patched.value,
117
118
  hadExistingFile: readResult.exists,
118
119
  reloadConfig: dependencies.reloadConfig,
@@ -12,12 +12,18 @@ export interface ConfigPaths {
12
12
  }
13
13
 
14
14
  export function resolveUserConfigRoot(): string {
15
- const home = homedir();
15
+ // Read the environment explicitly so isolated callers honor the process
16
+ // environment even when os.homedir() is cached.
17
+ const home = firstNonEmpty(
18
+ process.env.HOME,
19
+ process.env.USERPROFILE,
20
+ ) ?? homedir();
16
21
 
17
22
  switch (process.platform) {
18
23
  case "win32":
19
24
  return path.join(
20
- process.env.LOCALAPPDATA ?? path.join(home, "AppData", "Local"),
25
+ firstNonEmpty(process.env.LOCALAPPDATA) ??
26
+ path.join(home, "AppData", "Local"),
21
27
  "pi",
22
28
  "pi-clearance",
23
29
  );
@@ -31,13 +37,18 @@ export function resolveUserConfigRoot(): string {
31
37
  );
32
38
  default:
33
39
  return path.join(
34
- process.env.XDG_CONFIG_HOME ?? path.join(home, ".config"),
40
+ firstNonEmpty(process.env.XDG_CONFIG_HOME) ??
41
+ path.join(home, ".config"),
35
42
  "pi",
36
43
  "pi-clearance",
37
44
  );
38
45
  }
39
46
  }
40
47
 
48
+ function firstNonEmpty(...values: readonly (string | undefined)[]): string | undefined {
49
+ return values.find((value) => value !== undefined && value.length > 0);
50
+ }
51
+
41
52
  export function projectKeyFor(cwd: string): string {
42
53
  const absolute = path.resolve(cwd);
43
54
  const hash = createHash("sha256").update(absolute).digest("hex").slice(0, 8);
@@ -0,0 +1,135 @@
1
+ import {
2
+ CONFIG_SCHEMA_VERSION,
3
+ type GlobalConfig,
4
+ GlobalConfigSchema,
5
+ normalizeConfig,
6
+ type ProjectOverlayConfig,
7
+ ProjectOverlaySchema,
8
+ } from "./schema.ts";
9
+
10
+ export type ConfigPersistenceKind = "global" | "project";
11
+ export type NormalizedConfig = GlobalConfig | ProjectOverlayConfig;
12
+
13
+ /** The only JSON shape emitted for user-owned Clearance config files. */
14
+ export type SparseConfigDocument = Readonly<Record<string, unknown>> & {
15
+ readonly version: typeof CONFIG_SCHEMA_VERSION;
16
+ };
17
+
18
+ const OMIT = Symbol("omit-default");
19
+
20
+ /**
21
+ * Serialize a schema-normalized runtime config without materializing defaults.
22
+ *
23
+ * Defaults are obtained by normalizing the schema's version-only seed rather
24
+ * than copied here. Objects are compacted recursively; arrays are semantic
25
+ * values and are retained as a whole whenever they differ from their default.
26
+ */
27
+ export function serializeSparseConfig(
28
+ kind: ConfigPersistenceKind,
29
+ config: NormalizedConfig,
30
+ ): SparseConfigDocument {
31
+ const defaults = normalizedDefaults(kind);
32
+ const compacted = compactValue(config, defaults);
33
+ const document: Record<string, unknown> = {
34
+ version: config.version,
35
+ };
36
+
37
+ if (isRecord(compacted)) {
38
+ for (const [key, value] of Object.entries(compacted)) {
39
+ if (key !== "version") {
40
+ document[key] = value;
41
+ }
42
+ }
43
+ }
44
+
45
+ return canonicalize(document) as SparseConfigDocument;
46
+ }
47
+
48
+ /** Return the canonical on-disk representation for a normalized config. */
49
+ export function serializeSparseConfigText(
50
+ kind: ConfigPersistenceKind,
51
+ config: NormalizedConfig,
52
+ ): string {
53
+ return `${JSON.stringify(serializeSparseConfig(kind, config), null, 2)}\n`;
54
+ }
55
+
56
+ function normalizedDefaults(kind: ConfigPersistenceKind): NormalizedConfig {
57
+ const schema = kind === "global" ? GlobalConfigSchema : ProjectOverlaySchema;
58
+ const result = normalizeConfig(schema, { version: CONFIG_SCHEMA_VERSION });
59
+ if (!result.ok) {
60
+ throw new Error(`failed to normalize ${kind} config defaults`);
61
+ }
62
+ return result.value;
63
+ }
64
+
65
+ function compactValue(value: unknown, defaults: unknown): unknown {
66
+ if (jsonEqual(value, defaults)) {
67
+ return OMIT;
68
+ }
69
+
70
+ if (Array.isArray(value)) {
71
+ return value.map((entry) => cloneJsonish(entry));
72
+ }
73
+
74
+ if (isRecord(value)) {
75
+ const defaultRecord = isRecord(defaults) ? defaults : undefined;
76
+ const compacted: Record<string, unknown> = {};
77
+ for (const key of orderedKeys(Object.keys(value))) {
78
+ const child = compactValue(value[key], defaultRecord?.[key]);
79
+ if (child !== OMIT) {
80
+ compacted[key] = child;
81
+ }
82
+ }
83
+ return Object.keys(compacted).length === 0 ? OMIT : compacted;
84
+ }
85
+
86
+ return value;
87
+ }
88
+
89
+ function canonicalize(value: unknown): unknown {
90
+ if (Array.isArray(value)) {
91
+ return value.map(canonicalize);
92
+ }
93
+
94
+ if (isRecord(value)) {
95
+ const result: Record<string, unknown> = {};
96
+ for (const key of orderedKeys(Object.keys(value))) {
97
+ result[key] = canonicalize(value[key]);
98
+ }
99
+ return result;
100
+ }
101
+
102
+ return value;
103
+ }
104
+
105
+ function orderedKeys(keys: readonly string[]): readonly string[] {
106
+ return [...keys].sort((left, right) => {
107
+ if (left === "version") return -1;
108
+ if (right === "version") return 1;
109
+ return left < right ? -1 : left > right ? 1 : 0;
110
+ });
111
+ }
112
+
113
+ function jsonEqual(left: unknown, right: unknown): boolean {
114
+ return (
115
+ JSON.stringify(canonicalize(left)) === JSON.stringify(canonicalize(right))
116
+ );
117
+ }
118
+
119
+ function cloneJsonish<T>(value: T): T {
120
+ if (Array.isArray(value)) {
121
+ return value.map((entry) => cloneJsonish(entry)) as T;
122
+ }
123
+
124
+ if (isRecord(value)) {
125
+ return Object.fromEntries(
126
+ Object.entries(value).map(([key, entry]) => [key, cloneJsonish(entry)]),
127
+ ) as T;
128
+ }
129
+
130
+ return value;
131
+ }
132
+
133
+ function isRecord(value: unknown): value is Record<string, unknown> {
134
+ return typeof value === "object" && value !== null && !Array.isArray(value);
135
+ }