@hassangameryt/freeflow 0.4.0 → 0.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 (285) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +2 -1
  3. package/.codex-plugin/plugin.json +2 -1
  4. package/README.md +150 -155
  5. package/capabilities/cognitive-routing/SKILL.md +229 -0
  6. package/capabilities/context-virtualization/SKILL.md +103 -0
  7. package/capabilities/conversation-history/SKILL.md +53 -0
  8. package/command-surface.json +17 -13
  9. package/hooks/freeflow-runtime-context.mjs +393 -159
  10. package/hooks/hooks.json +26 -1
  11. package/hooks/tests/check-runtime-context-hook.sh +223 -54
  12. package/package.json +25 -16
  13. package/pi-extension/dist/cognitive-routing/commands.js +96 -0
  14. package/pi-extension/dist/cognitive-routing/config.js +236 -0
  15. package/pi-extension/dist/cognitive-routing/controller.js +862 -0
  16. package/pi-extension/dist/cognitive-routing/history.js +439 -0
  17. package/pi-extension/dist/cognitive-routing/host.js +126 -0
  18. package/pi-extension/dist/cognitive-routing/runtime.js +43 -0
  19. package/pi-extension/dist/cognitive-routing/tool.js +211 -0
  20. package/pi-extension/dist/cognitive-routing/types.js +6 -0
  21. package/pi-extension/dist/context-virtualization/commands.js +89 -0
  22. package/pi-extension/dist/context-virtualization/projector.js +42 -0
  23. package/pi-extension/dist/context-virtualization/runtime.js +356 -0
  24. package/pi-extension/dist/context-virtualization/state.js +57 -0
  25. package/pi-extension/dist/context-virtualization/types.js +10 -0
  26. package/pi-extension/dist/conversation-history/passages.js +66 -0
  27. package/pi-extension/dist/conversation-history/ranking.js +105 -0
  28. package/pi-extension/dist/conversation-history/runtime.js +226 -0
  29. package/pi-extension/dist/conversation-history/search.js +56 -0
  30. package/pi-extension/dist/conversation-history/types.js +1 -0
  31. package/pi-extension/dist/freeflow-context/resolver.js +120 -0
  32. package/pi-extension/dist/freeflow-context/runtime.js +80 -0
  33. package/pi-extension/dist/freeflow-context/source-projector.js +95 -0
  34. package/pi-extension/dist/freeflow-context/tool.js +308 -0
  35. package/pi-extension/dist/freeflow-context/types.js +12 -0
  36. package/pi-extension/dist/freeflow-context/visibility.js +28 -0
  37. package/pi-extension/dist/index.js +516 -98
  38. package/pi-extension/dist/runtime/runtime-context.js +318 -271
  39. package/pi-extension/dist/runtime/runtime-identity.js +5 -0
  40. package/pi-extension/dist/settings/settings-tui.js +78 -0
  41. package/pi-extension/dist/settings/settings-ui.js +593 -563
  42. package/pi-extension/dist/ui/text-component.js +57 -0
  43. package/pi-extension/src/cognitive-routing/commands.ts +119 -0
  44. package/pi-extension/src/cognitive-routing/config.ts +307 -0
  45. package/pi-extension/src/cognitive-routing/controller.ts +1118 -0
  46. package/pi-extension/src/cognitive-routing/history.ts +638 -0
  47. package/pi-extension/src/cognitive-routing/host.ts +172 -0
  48. package/pi-extension/src/cognitive-routing/runtime.ts +57 -0
  49. package/pi-extension/src/cognitive-routing/tool.ts +272 -0
  50. package/pi-extension/src/cognitive-routing/types.ts +89 -0
  51. package/pi-extension/src/context-virtualization/commands.ts +111 -0
  52. package/pi-extension/src/context-virtualization/projector.ts +62 -0
  53. package/pi-extension/src/context-virtualization/runtime.ts +406 -0
  54. package/pi-extension/src/context-virtualization/state.ts +79 -0
  55. package/pi-extension/src/context-virtualization/types.ts +92 -0
  56. package/pi-extension/src/conversation-history/passages.ts +81 -0
  57. package/pi-extension/src/conversation-history/ranking.ts +155 -0
  58. package/pi-extension/src/conversation-history/runtime.ts +250 -0
  59. package/pi-extension/src/conversation-history/search.ts +79 -0
  60. package/pi-extension/src/conversation-history/types.ts +31 -0
  61. package/pi-extension/src/freeflow-context/resolver.ts +139 -0
  62. package/pi-extension/src/freeflow-context/runtime.ts +100 -0
  63. package/pi-extension/src/freeflow-context/source-projector.ts +106 -0
  64. package/pi-extension/src/freeflow-context/tool.ts +360 -0
  65. package/pi-extension/src/freeflow-context/types.ts +70 -0
  66. package/pi-extension/src/freeflow-context/visibility.ts +53 -0
  67. package/pi-extension/src/index.ts +553 -101
  68. package/pi-extension/src/runtime/runtime-context.ts +339 -281
  69. package/pi-extension/src/runtime/runtime-identity.ts +8 -0
  70. package/pi-extension/src/settings/settings-tui.ts +93 -0
  71. package/pi-extension/src/settings/settings-ui.ts +700 -613
  72. package/pi-extension/src/ui/text-component.ts +66 -0
  73. package/pi-extension/tests/cognitive-routing/commands.test.js +134 -0
  74. package/pi-extension/tests/cognitive-routing/controller.test.js +1099 -0
  75. package/pi-extension/tests/cognitive-routing/extension-lifecycle.test.js +1071 -0
  76. package/pi-extension/tests/cognitive-routing/history.test.js +274 -0
  77. package/pi-extension/tests/cognitive-routing/host-fixture.js +4 -0
  78. package/pi-extension/tests/cognitive-routing/host-identity.test.js +63 -0
  79. package/pi-extension/tests/cognitive-routing/profile-resolution.test.js +236 -0
  80. package/pi-extension/tests/cognitive-routing/tool.test.js +230 -0
  81. package/pi-extension/tests/context-virtualization/extension.test.js +749 -0
  82. package/pi-extension/tests/context-virtualization/projector.test.js +110 -0
  83. package/pi-extension/tests/context-virtualization/runtime.test.js +216 -0
  84. package/pi-extension/tests/conversation-history/runtime.test.js +98 -0
  85. package/pi-extension/tests/conversation-history/search.test.js +97 -0
  86. package/pi-extension/tests/integration/pi-extension.test.js +928 -2047
  87. package/pi-extension/tests/integration/prompt-architecture.test.js +305 -0
  88. package/runtime/prompts/cognitive-routing.md +9 -0
  89. package/runtime/prompts/context-virtualization.md +3 -0
  90. package/runtime/prompts/conversation-history.md +3 -0
  91. package/runtime/prompts/core.md +13 -0
  92. package/runtime/prompts/interaction-contract.md +7 -0
  93. package/runtime/prompts/skills.md +65 -0
  94. package/skills/action-selection/SKILL.md +91 -0
  95. package/skills/bypass/SKILL.md +3 -2
  96. package/skills/commit-work/SKILL.md +1 -1
  97. package/skills/commit-work/references/staging-decisions.md +1 -1
  98. package/skills/decision-gate/SKILL.md +1 -1
  99. package/skills/design-for-depth/SKILL.md +14 -33
  100. package/skills/design-for-depth/references/design-pressure-signals.md +1 -1
  101. package/skills/design-for-depth/references/interface-design-loop.md +1 -1
  102. package/skills/design-for-depth/references/software-design-philosophy.md +1 -1
  103. package/skills/diagnose-failure/SKILL.md +20 -14
  104. package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +1 -3
  105. package/skills/diagnose-failure/references/{flaky-and-performance.md → flaky-and-performance-diagnosis.md} +1 -1
  106. package/skills/discuss/SKILL.md +34 -22
  107. package/skills/discuss/references/discussion-continuity.md +71 -0
  108. package/skills/evaluate-skill/SKILL.md +5 -5
  109. package/skills/evaluate-skill/references/{definition-schema.md → evaluation-definition-schema.md} +42 -2
  110. package/skills/evaluate-skill/references/execution-and-evidence.md +8 -3
  111. package/skills/evaluate-skill/scripts/lib/definitions.mjs +114 -3
  112. package/skills/evaluate-skill/scripts/lib/grade-interaction.mjs +156 -1
  113. package/skills/evaluate-skill/scripts/lib/grade.mjs +9 -1
  114. package/skills/evaluate-skill/scripts/lib/pi.mjs +147 -32
  115. package/skills/evaluate-skill/scripts/lib/rpc.mjs +4 -4
  116. package/skills/evaluate-skill/scripts/lib/runner.mjs +36 -14
  117. package/skills/evaluate-skill/scripts/lib/sandbox.mjs +92 -0
  118. package/skills/evaluate-skill/scripts/pi-guard.mjs +1 -1
  119. package/skills/evaluate-skill/scripts/pi-observer.mjs +66 -0
  120. package/skills/evaluate-skill/scripts/skill-eval.mjs +6 -5
  121. package/skills/execute-work/SKILL.md +18 -15
  122. package/skills/execute-work/references/code-practices.md +1 -1
  123. package/skills/execute-work/references/execute-work-edges.md +42 -0
  124. package/skills/finish-branch/SKILL.md +2 -2
  125. package/skills/finish-branch/references/integration-options.md +1 -1
  126. package/skills/handoff/SKILL.md +3 -3
  127. package/skills/launch-work/SKILL.md +1 -1
  128. package/skills/launch-work/references/launch-readiness.md +2 -2
  129. package/skills/migration-work/SKILL.md +2 -2
  130. package/skills/migration-work/references/migration-lifecycle.md +1 -1
  131. package/skills/mode-contract/SKILL.md +32 -10
  132. package/skills/release-work/SKILL.md +1 -1
  133. package/skills/release-work/references/release-evidence.md +1 -1
  134. package/skills/review-artifact/SKILL.md +28 -26
  135. package/skills/review-work/SKILL.md +28 -26
  136. package/skills/review-work/references/{reviewer-prompt.md → independent-work-reviewer-contract.md} +1 -1
  137. package/skills/review-work/references/security-risk-lens.md +1 -1
  138. package/skills/setup-freeflow/SKILL.md +6 -10
  139. package/skills/setup-freeflow/references/activation-contract.md +5 -10
  140. package/skills/setup-freeflow/references/host-setup.md +3 -7
  141. package/skills/simplify-code/SKILL.md +1 -1
  142. package/skills/simplify-code/references/simplification-patterns.md +1 -1
  143. package/skills/tdd/SKILL.md +18 -38
  144. package/skills/tdd/references/test-design.md +2 -2
  145. package/skills/track-work/SKILL.md +362 -58
  146. package/skills/track-work/scripts/lib/codec.mjs +570 -0
  147. package/skills/track-work/scripts/lib/commands.mjs +568 -0
  148. package/skills/track-work/scripts/lib/contract.mjs +303 -0
  149. package/skills/track-work/scripts/lib/model.mjs +506 -0
  150. package/skills/track-work/scripts/lib/operations.mjs +965 -0
  151. package/skills/track-work/scripts/lib/record-store.mjs +264 -0
  152. package/skills/track-work/scripts/lib/result.mjs +37 -0
  153. package/skills/track-work/scripts/lib/rewrites.mjs +740 -0
  154. package/skills/track-work/scripts/lib/schema.mjs +277 -0
  155. package/skills/track-work/scripts/lib/source-inventory.mjs +162 -0
  156. package/skills/track-work/scripts/lib/views.mjs +443 -0
  157. package/skills/track-work/scripts/lib/workspace.mjs +134 -0
  158. package/skills/track-work/scripts/working-record.mjs +122 -0
  159. package/skills/verify-work/SKILL.md +14 -2
  160. package/skills/verify-work/references/browser-runtime-evidence.md +1 -1
  161. package/skills/verify-work/references/integration-evidence.md +1 -1
  162. package/skills/verify-work/references/performance-evidence.md +1 -1
  163. package/skills/workflow/SKILL.md +105 -53
  164. package/skills/workflow/references/domain-skill-composition.md +22 -42
  165. package/skills/write-plan/SKILL.md +1 -1
  166. package/skills/write-skill/SKILL.md +3 -2
  167. package/skills/write-skill/references/progressive-disclosure.md +17 -3
  168. package/pi-extension/dist/output-router/host-producer-identification.js +0 -232
  169. package/pi-extension/dist/output-router/native-safety-net.js +0 -216
  170. package/pi-extension/dist/output-router/observed-tool-routing.js +0 -122
  171. package/pi-extension/dist/output-router/renderers.js +0 -1023
  172. package/pi-extension/dist/output-router/router-tools.js +0 -554
  173. package/pi-extension/dist/output-router/schemas.js +0 -383
  174. package/pi-extension/dist/output-router/status.js +0 -744
  175. package/pi-extension/dist/output-router/utils.js +0 -502
  176. package/pi-extension/src/output-router/host-producer-identification.ts +0 -256
  177. package/pi-extension/src/output-router/native-safety-net.ts +0 -248
  178. package/pi-extension/src/output-router/observed-tool-routing.ts +0 -134
  179. package/pi-extension/src/output-router/renderers.ts +0 -1108
  180. package/pi-extension/src/output-router/router-tools.ts +0 -579
  181. package/pi-extension/src/output-router/schemas.ts +0 -387
  182. package/pi-extension/src/output-router/status.ts +0 -791
  183. package/pi-extension/src/output-router/utils.ts +0 -554
  184. package/pi-extension/tests/output-router/observed-producers.test.js +0 -132
  185. package/pi-extension/tests/output-router/search.test.js +0 -722
  186. package/router/dist/benchmarks/benchmark-harness.d.ts +0 -35
  187. package/router/dist/benchmarks/benchmark-harness.js +0 -92
  188. package/router/dist/benchmarks/benchmarks.d.ts +0 -109
  189. package/router/dist/benchmarks/benchmarks.js +0 -979
  190. package/router/dist/benchmarks/codex-qa-benchmarks.d.ts +0 -95
  191. package/router/dist/benchmarks/codex-qa-benchmarks.js +0 -500
  192. package/router/dist/benchmarks/command-benchmarks.d.ts +0 -143
  193. package/router/dist/benchmarks/command-benchmarks.js +0 -698
  194. package/router/dist/benchmarks/context-mode-normalized-benchmarks.d.ts +0 -82
  195. package/router/dist/benchmarks/context-mode-normalized-benchmarks.js +0 -773
  196. package/router/dist/benchmarks/context-mode-real-deep-benchmark.d.ts +0 -132
  197. package/router/dist/benchmarks/context-mode-real-deep-benchmark.js +0 -1380
  198. package/router/dist/benchmarks/context-saving-baseline.d.ts +0 -60
  199. package/router/dist/benchmarks/context-saving-baseline.js +0 -470
  200. package/router/dist/benchmarks/index-benchmarks.d.ts +0 -103
  201. package/router/dist/benchmarks/index-benchmarks.js +0 -862
  202. package/router/dist/benchmarks/storage-policy-benchmarks.d.ts +0 -101
  203. package/router/dist/benchmarks/storage-policy-benchmarks.js +0 -618
  204. package/router/dist/benchmarks/vault-index-benchmarks.d.ts +0 -56
  205. package/router/dist/benchmarks/vault-index-benchmarks.js +0 -322
  206. package/router/dist/config/config.d.ts +0 -103
  207. package/router/dist/config/config.js +0 -632
  208. package/router/dist/config/router-contract.d.ts +0 -10
  209. package/router/dist/config/router-contract.js +0 -54
  210. package/router/dist/config/schema.d.ts +0 -27
  211. package/router/dist/config/schema.js +0 -648
  212. package/router/dist/config/types.d.ts +0 -573
  213. package/router/dist/config/types.js +0 -41
  214. package/router/dist/evidence/bounded-evidence.d.ts +0 -40
  215. package/router/dist/evidence/bounded-evidence.js +0 -323
  216. package/router/dist/evidence/evidence-range-selector.d.ts +0 -24
  217. package/router/dist/evidence/evidence-range-selector.js +0 -232
  218. package/router/dist/evidence/evidence-search.d.ts +0 -27
  219. package/router/dist/evidence/evidence-search.js +0 -490
  220. package/router/dist/evidence/evidence.d.ts +0 -38
  221. package/router/dist/evidence/evidence.js +0 -200
  222. package/router/dist/evidence/failure-contracts.d.ts +0 -37
  223. package/router/dist/evidence/failure-contracts.js +0 -166
  224. package/router/dist/evidence/line-ranges.d.ts +0 -24
  225. package/router/dist/evidence/line-ranges.js +0 -22
  226. package/router/dist/experiments/local-index.d.ts +0 -64
  227. package/router/dist/experiments/local-index.js +0 -581
  228. package/router/dist/index.d.ts +0 -30
  229. package/router/dist/index.js +0 -32
  230. package/router/dist/local/local-traversal.d.ts +0 -18
  231. package/router/dist/local/local-traversal.js +0 -334
  232. package/router/dist/processing/engine.d.ts +0 -132
  233. package/router/dist/processing/engine.js +0 -533
  234. package/router/dist/processing/reducers.d.ts +0 -280
  235. package/router/dist/processing/reducers.js +0 -1673
  236. package/router/dist/processing/renderers.d.ts +0 -31
  237. package/router/dist/processing/renderers.js +0 -195
  238. package/router/dist/processing/scripts.d.ts +0 -91
  239. package/router/dist/processing/scripts.js +0 -386
  240. package/router/dist/repo/repo-traversal.d.ts +0 -18
  241. package/router/dist/repo/repo-traversal.js +0 -245
  242. package/router/dist/routing/observed-reducers.d.ts +0 -41
  243. package/router/dist/routing/observed-reducers.js +0 -661
  244. package/router/dist/routing/observed-routing.d.ts +0 -26
  245. package/router/dist/routing/observed-routing.js +0 -378
  246. package/router/dist/routing/parsers.d.ts +0 -16
  247. package/router/dist/routing/parsers.js +0 -521
  248. package/router/dist/routing/run-filters.d.ts +0 -60
  249. package/router/dist/routing/run-filters.js +0 -238
  250. package/router/dist/routing/run-reducers.d.ts +0 -49
  251. package/router/dist/routing/run-reducers.js +0 -122
  252. package/router/dist/sandbox/adapter-roots.d.ts +0 -46
  253. package/router/dist/sandbox/adapter-roots.js +0 -66
  254. package/router/dist/sandbox/eryx-python-adapter.d.ts +0 -13
  255. package/router/dist/sandbox/eryx-python-adapter.js +0 -868
  256. package/router/dist/sandbox/jq-wasm-adapter.d.ts +0 -9
  257. package/router/dist/sandbox/jq-wasm-adapter.js +0 -429
  258. package/router/dist/sandbox/quickjs-wasi-adapter.d.ts +0 -13
  259. package/router/dist/sandbox/quickjs-wasi-adapter.js +0 -384
  260. package/router/dist/sandbox/script-sandbox.d.ts +0 -134
  261. package/router/dist/sandbox/script-sandbox.js +0 -337
  262. package/router/dist/setup/script-transform-adapters.d.ts +0 -32
  263. package/router/dist/setup/script-transform-adapters.js +0 -278
  264. package/router/dist/tools/batch.d.ts +0 -33
  265. package/router/dist/tools/batch.js +0 -586
  266. package/router/dist/tools/run.d.ts +0 -62
  267. package/router/dist/tools/run.js +0 -1304
  268. package/router/dist/tools/search.d.ts +0 -66
  269. package/router/dist/tools/search.js +0 -1364
  270. package/router/dist/transform/engine.d.ts +0 -168
  271. package/router/dist/transform/engine.js +0 -2072
  272. package/router/dist/vault/vault-index.d.ts +0 -125
  273. package/router/dist/vault/vault-index.js +0 -416
  274. package/router/dist/vault/vault.d.ts +0 -138
  275. package/router/dist/vault/vault.js +0 -630
  276. package/runtime/interaction-contract.md +0 -16
  277. package/skills/discuss/references/checkpoints.md +0 -66
  278. package/skills/execute-work/references/execution-loop.md +0 -77
  279. package/skills/output-router/SKILL.md +0 -206
  280. package/skills/output-router/references/safety-policy.md +0 -40
  281. package/skills/setup-freeflow/references/output-router-setup.md +0 -110
  282. package/skills/track-work/references/working-record-schema.md +0 -378
  283. package/skills/workflow/references/workflow-loop.md +0 -92
  284. /package/skills/handoff/references/{templates.md → handoff-templates.md} +0 -0
  285. /package/skills/review-artifact/references/{reviewer-prompt.md → independent-artifact-reviewer-contract.md} +0 -0
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ import crypto from "node:crypto";
3
4
  import fs from "node:fs";
4
5
  import path from "node:path";
5
6
  import process from "node:process";
@@ -8,6 +9,9 @@ import { fileURLToPath } from "node:url";
8
9
  const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
9
10
  const PLUGIN_ROOT = path.resolve(SCRIPT_DIR, "..");
10
11
  const VALID_MODES = new Set(["conversation", "workflow", "strict-workflow"]);
12
+ const SESSION_STATE_VERSION = 1;
13
+ const SESSION_STATE_MAX_AGE_MS = 90 * 24 * 60 * 60 * 1000;
14
+ const CLEAR_TRANSFER_MAX_AGE_MS = 60 * 1000;
11
15
 
12
16
  function readStdinJson() {
13
17
  const raw = fs.readFileSync(0, "utf8").trim();
@@ -49,24 +53,17 @@ function findWorkspaceRoot(cwd) {
49
53
  function loadRuntimeContext(options = {}) {
50
54
  const includeInteractionContract = options.interactionContract === true;
51
55
  const includeSkills = options.skills === true;
52
- const includeOutputRouter = options.outputRouter === true;
56
+ const corePrompt = readText(path.join(PLUGIN_ROOT, "runtime", "prompts", "core.md"));
53
57
  const interactionContract = includeInteractionContract
54
- ? readText(path.join(PLUGIN_ROOT, "runtime", "interaction-contract.md"))
55
- : null;
56
- const workflowSkill = includeSkills ? readText(path.join(PLUGIN_ROOT, "skills", "workflow", "SKILL.md")) : null;
57
- const outputRouterSkill = includeOutputRouter
58
- ? readText(path.join(PLUGIN_ROOT, "skills", "output-router", "SKILL.md"))
58
+ ? readText(path.join(PLUGIN_ROOT, "runtime", "prompts", "interaction-contract.md"))
59
59
  : null;
60
+ const skillsPrompt = includeSkills ? readText(path.join(PLUGIN_ROOT, "runtime", "prompts", "skills.md")) : null;
60
61
 
61
- if (
62
- (includeInteractionContract && !interactionContract) ||
63
- (includeSkills && !workflowSkill) ||
64
- (includeOutputRouter && !outputRouterSkill)
65
- ) {
66
- throw new Error("Freeflow runtime context files are missing.");
62
+ if (!corePrompt || (includeInteractionContract && !interactionContract) || (includeSkills && !skillsPrompt)) {
63
+ throw new Error("Freeflow runtime prompt files are missing.");
67
64
  }
68
65
 
69
- return { interactionContract, workflowSkill, outputRouterSkill };
66
+ return { corePrompt, interactionContract, skillsPrompt };
70
67
  }
71
68
 
72
69
  function readConfig(root) {
@@ -76,7 +73,6 @@ function readConfig(root) {
76
73
  const configured = repository.valid && localValid;
77
74
  const core = resolveCoreConfig(repository.valid ? repository.parsed : {}, local.valid ? local.parsed : {});
78
75
  const enabled = configured && core.config.enabled;
79
- const repositoryConfig = repository.valid ? repository.parsed : {};
80
76
 
81
77
  return {
82
78
  exists: repository.exists,
@@ -85,13 +81,12 @@ function readConfig(root) {
85
81
  repositoryValid: repository.valid,
86
82
  localExists: local.exists,
87
83
  localValid,
88
- error: !repository.valid ? repository.error : local.error,
84
+ error: repository.valid ? local.error : repository.error,
89
85
  enabled,
90
86
  interactionContractEnabled: enabled && core.config.interactionContract,
91
87
  skillsEnabled: enabled && core.config.skills.enabled,
92
88
  defaultMode: core.config.defaultMode,
93
89
  sources: core.sources,
94
- outputRouterEnabled: enabled && repositoryConfig?.outputRouter?.enabled === true,
95
90
  };
96
91
  }
97
92
 
@@ -126,23 +121,20 @@ function isRecord(value) {
126
121
  }
127
122
 
128
123
  function validateCoreConfigFields(value) {
129
- if (Object.prototype.hasOwnProperty.call(value, "defaultMode") && !VALID_MODES.has(value.defaultMode)) {
124
+ if (Object.hasOwn(value, "defaultMode") && !VALID_MODES.has(value.defaultMode)) {
130
125
  return "defaultMode must be conversation, workflow, or strict-workflow";
131
126
  }
132
- if (Object.prototype.hasOwnProperty.call(value, "enabled") && typeof value.enabled !== "boolean") {
127
+ if (Object.hasOwn(value, "enabled") && typeof value.enabled !== "boolean") {
133
128
  return "enabled must be a boolean";
134
129
  }
135
- if (
136
- Object.prototype.hasOwnProperty.call(value, "interactionContract") &&
137
- typeof value.interactionContract !== "boolean"
138
- ) {
130
+ if (Object.hasOwn(value, "interactionContract") && typeof value.interactionContract !== "boolean") {
139
131
  return "interactionContract must be a boolean";
140
132
  }
141
- if (Object.prototype.hasOwnProperty.call(value, "skills")) {
133
+ if (Object.hasOwn(value, "skills")) {
142
134
  if (!isRecord(value.skills)) {
143
135
  return "skills must be an object";
144
136
  }
145
- if (Object.prototype.hasOwnProperty.call(value.skills, "enabled") && typeof value.skills.enabled !== "boolean") {
137
+ if (Object.hasOwn(value.skills, "enabled") && typeof value.skills.enabled !== "boolean") {
146
138
  return "skills.enabled must be a boolean";
147
139
  }
148
140
  }
@@ -162,6 +154,7 @@ function isValidSetupConfig(value) {
162
154
  "outputRouter",
163
155
  "observedRouting",
164
156
  "scriptTransform",
157
+ "cognitiveRouting",
165
158
  ]);
166
159
  if (!Object.keys(value).every((key) => allowedKeys.has(key))) {
167
160
  return "repository config contains unsupported top-level keys";
@@ -173,7 +166,7 @@ function isValidSetupConfig(value) {
173
166
  }
174
167
 
175
168
  for (const key of ["outputRouter", "observedRouting", "scriptTransform"]) {
176
- if (Object.prototype.hasOwnProperty.call(value, key) && !isRecord(value[key])) {
169
+ if (Object.hasOwn(value, key) && !isRecord(value[key])) {
177
170
  return `${key} must be an object`;
178
171
  }
179
172
  }
@@ -185,7 +178,14 @@ function isValidLocalConfig(value) {
185
178
  return "local config must be a JSON object";
186
179
  }
187
180
 
188
- const allowedKeys = new Set(["enabled", "defaultMode", "interactionContract", "skills", "processing"]);
181
+ const allowedKeys = new Set([
182
+ "enabled",
183
+ "defaultMode",
184
+ "interactionContract",
185
+ "skills",
186
+ "processing",
187
+ "cognitiveRouting",
188
+ ]);
189
189
  if (!Object.keys(value).every((key) => allowedKeys.has(key))) {
190
190
  return "local config contains unsupported top-level keys";
191
191
  }
@@ -193,7 +193,7 @@ function isValidLocalConfig(value) {
193
193
  }
194
194
 
195
195
  function hasOwn(value, key) {
196
- return Object.prototype.hasOwnProperty.call(value, key);
196
+ return Object.hasOwn(value, key);
197
197
  }
198
198
 
199
199
  function resolveLayeredValue(repository, local, key, fallback) {
@@ -230,159 +230,391 @@ function resolveCoreConfig(repository, local) {
230
230
  };
231
231
  }
232
232
 
233
- function inspectSetup(root) {
234
- return { config: readConfig(root) };
233
+ function hostName() {
234
+ return process.env.PLUGIN_DATA ? "codex" : "claude";
235
235
  }
236
236
 
237
- function configStatus(config) {
238
- if (!config.valid) {
239
- return "invalid layered configuration";
240
- }
241
- const source = config.sources.defaultMode;
242
- const personal = config.localExists ? "; personal overrides present" : "";
243
- return `defaultMode \`${config.defaultMode}\` from ${source}${personal}`;
244
- }
245
-
246
- function modeOverlayGuidance(mode) {
247
- if (mode === "conversation") {
248
- return [
249
- "",
250
- "## Conversation Mode Boundary",
251
- "",
252
- "Conversation mode is active and read-only.",
253
- "",
254
- "Do not call write, edit, or mutating tools. Do not create, delete, commit, push, or change repository, system, external, or durable state.",
255
- "",
256
- "A mutation request does not switch mode, and an execution skill does not override this boundary. Explain that conversation mode is read-only and ask the user to switch to workflow or strict-workflow.",
257
- ];
258
- }
259
-
260
- if (mode === "strict-workflow") {
261
- return [
262
- "",
263
- "## Strict Workflow Overlay",
264
- "",
265
- "Strict Workflow is active. Use the adaptive Workflow, but increase decision, evidence, verification, and checkpoint pressure at high-risk or hard-to-reverse boundaries.",
266
- "",
267
- "For work affecting security, privacy, billing, data loss, migrations, public interfaces, compatibility, deployment, or architecture:",
268
- "",
269
- "- stop for any user-owned choice or source conflict;",
270
- "- inspect the relevant risk surface before crossing the boundary;",
271
- "- select only artifacts, checkpoints, and independent review that materially reduce risk;",
272
- "- verify at the affected boundary before claiming success.",
273
- "",
274
- "Do not manufacture ceremony for low-risk, reversible work. Strict Workflow does not authorize mutation, bypass safety, or make every implementation detail a user decision.",
275
- ];
276
- }
277
-
278
- return [];
279
- }
280
-
281
- function modeGuidance(mode) {
282
- return [
283
- `Current Freeflow default mode: \`${mode}\`.`,
284
- "Treat this as the resolved default at session start, resume, clear, and compact.",
285
- "Mode behavior:",
286
- "- `conversation`: answer, discuss, critique, and inspect read-only; switch modes before mutating state.",
287
- "- `workflow`: use the adaptive workflow for normal consequential work.",
288
- "- `strict-workflow`: strengthen user-owned decisions, review, and verification for high-risk or hard-to-reverse work.",
289
- "Do not announce the current mode on every reply. Mention it when the user asks, setup/config is discussed, or the mode changes the next action.",
290
- ...modeOverlayGuidance(mode),
291
- ];
237
+ function pluginDataRoot() {
238
+ const configured = process.env.PLUGIN_DATA || process.env.CLAUDE_PLUGIN_DATA;
239
+ return configured ? path.resolve(configured) : null;
292
240
  }
293
241
 
294
- function buildSetupStatus(root) {
295
- const { config } = inspectSetup(root);
296
- if (!config.valid) {
297
- return "Setup status: Freeflow is not configured for this repo.";
242
+ function sessionStatePath(sessionId) {
243
+ const dataRoot = pluginDataRoot();
244
+ if (!dataRoot || typeof sessionId !== "string" || sessionId.length === 0) {
245
+ return null;
298
246
  }
299
247
 
300
- const modeStatus = config.skillsEnabled
301
- ? modeGuidance(config.defaultMode)
302
- : [`Resolved default mode: \`${config.defaultMode}\` (dormant because Skills are disabled).`];
303
- return [
304
- `Setup status: configured by \`.freeflow/config.json\` with ${configStatus(config)}.`,
305
- "Runtime delivery: confirmed for this lifecycle-hook invocation.",
306
- ...modeStatus,
307
- ].join("\n");
248
+ const key = crypto.createHash("sha256").update(`${hostName()}\0${sessionId}`).digest("hex");
249
+ return path.join(dataRoot, "session-modes", hostName(), `${key}.json`);
308
250
  }
309
251
 
310
- function shouldInject(eventName) {
311
- if (process.env.FREEFLOW_DISABLE_RUNTIME_CONTEXT === "1") {
252
+ function workspaceKey(root) {
253
+ return crypto.createHash("sha256").update(path.resolve(root)).digest("hex");
254
+ }
255
+
256
+ function claudeProcessKey() {
257
+ const processId = process.env.CLAUDE_PID;
258
+ if (typeof processId !== "string" || processId.length === 0) {
259
+ return null;
260
+ }
261
+ return crypto.createHash("sha256").update(processId).digest("hex");
262
+ }
263
+
264
+ function clearTransferPath(root) {
265
+ const dataRoot = pluginDataRoot();
266
+ const processKey = claudeProcessKey();
267
+ if (!dataRoot || hostName() !== "claude" || !processKey) {
268
+ return null;
269
+ }
270
+ return path.join(dataRoot, "session-modes", "claude-clear", `${workspaceKey(root)}-${processKey}.json`);
271
+ }
272
+
273
+ function writeJsonAtomic(filePath, value) {
274
+ const directory = path.dirname(filePath);
275
+ fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
276
+ const temporaryPath = `${filePath}.${process.pid}.${crypto.randomUUID()}.tmp`;
277
+ try {
278
+ fs.writeFileSync(temporaryPath, `${JSON.stringify(value)}\n`, { encoding: "utf8", mode: 0o600 });
279
+ fs.renameSync(temporaryPath, filePath);
280
+ } catch (error) {
281
+ try {
282
+ fs.unlinkSync(temporaryPath);
283
+ } catch {
284
+ // Best-effort cleanup of a failed atomic write.
285
+ }
286
+ throw error;
287
+ }
288
+ }
289
+
290
+ function readSessionMode(sessionId) {
291
+ const filePath = sessionStatePath(sessionId);
292
+ if (!filePath) {
293
+ return null;
294
+ }
295
+
296
+ try {
297
+ const state = JSON.parse(fs.readFileSync(filePath, "utf8"));
298
+ if (state.version !== SESSION_STATE_VERSION || !VALID_MODES.has(state.mode)) {
299
+ return null;
300
+ }
301
+ return state.mode;
302
+ } catch {
303
+ return null;
304
+ }
305
+ }
306
+
307
+ function writeSessionMode(sessionId, mode) {
308
+ const filePath = sessionStatePath(sessionId);
309
+ if (!filePath) {
312
310
  return false;
313
311
  }
314
312
 
315
- return eventName === "SessionStart";
313
+ writeJsonAtomic(filePath, {
314
+ version: SESSION_STATE_VERSION,
315
+ mode,
316
+ updatedAt: new Date().toISOString(),
317
+ });
318
+ return true;
319
+ }
320
+
321
+ function clearSessionMode(sessionId) {
322
+ const filePath = sessionStatePath(sessionId);
323
+ if (!filePath) {
324
+ return false;
325
+ }
326
+
327
+ try {
328
+ fs.unlinkSync(filePath);
329
+ } catch (error) {
330
+ if (error?.code !== "ENOENT") {
331
+ throw error;
332
+ }
333
+ }
334
+ return true;
335
+ }
336
+
337
+ function stageClearTransfer(input) {
338
+ if (hostName() !== "claude" || input.reason !== "clear") {
339
+ return;
340
+ }
341
+
342
+ const root = findWorkspaceRoot(input.cwd || process.cwd());
343
+ const mode = readSessionMode(input.session_id);
344
+ const filePath = clearTransferPath(root);
345
+ if (!filePath) {
346
+ return;
347
+ }
348
+
349
+ if (!mode) {
350
+ try {
351
+ fs.unlinkSync(filePath);
352
+ } catch (error) {
353
+ if (error?.code !== "ENOENT") {
354
+ throw error;
355
+ }
356
+ }
357
+ return;
358
+ }
359
+
360
+ writeJsonAtomic(filePath, {
361
+ version: SESSION_STATE_VERSION,
362
+ mode,
363
+ sourceSessionKey: crypto.createHash("sha256").update(String(input.session_id)).digest("hex"),
364
+ createdAt: new Date().toISOString(),
365
+ });
366
+ }
367
+
368
+ function consumeClearTransfer(input, root) {
369
+ if (hostName() !== "claude" || input.source !== "clear") {
370
+ return null;
371
+ }
372
+
373
+ const filePath = clearTransferPath(root);
374
+ if (!filePath) {
375
+ return null;
376
+ }
377
+
378
+ const claimedPath = `${filePath}.${process.pid}.${crypto.randomUUID()}.claimed`;
379
+ try {
380
+ fs.renameSync(filePath, claimedPath);
381
+ } catch (error) {
382
+ if (error?.code === "ENOENT") {
383
+ return null;
384
+ }
385
+ return null;
386
+ }
387
+
388
+ let state;
389
+ try {
390
+ state = JSON.parse(fs.readFileSync(claimedPath, "utf8"));
391
+ } catch {
392
+ return null;
393
+ } finally {
394
+ try {
395
+ fs.unlinkSync(claimedPath);
396
+ } catch {
397
+ // The atomic claim already prevents reuse; cleanup is best-effort.
398
+ }
399
+ }
400
+
401
+ if (!isRecord(state)) {
402
+ return null;
403
+ }
404
+ const createdAt = Date.parse(state.createdAt);
405
+ if (
406
+ state.version !== SESSION_STATE_VERSION ||
407
+ !VALID_MODES.has(state.mode) ||
408
+ !Number.isFinite(createdAt) ||
409
+ Date.now() - createdAt > CLEAR_TRANSFER_MAX_AGE_MS ||
410
+ Date.now() < createdAt
411
+ ) {
412
+ return null;
413
+ }
414
+
415
+ return writeSessionMode(input.session_id, state.mode) ? state.mode : null;
416
+ }
417
+
418
+ function cleanupExpiredSessionModes() {
419
+ const root = pluginDataRoot();
420
+ if (!root) {
421
+ return;
422
+ }
423
+
424
+ const stateRoot = path.join(root, "session-modes");
425
+ const cutoff = Date.now() - SESSION_STATE_MAX_AGE_MS;
426
+ for (const host of ["claude", "codex", "claude-clear"]) {
427
+ const directory = path.join(stateRoot, host);
428
+ let entries = [];
429
+ try {
430
+ entries = fs.readdirSync(directory, { withFileTypes: true });
431
+ } catch {
432
+ continue;
433
+ }
434
+
435
+ for (const entry of entries) {
436
+ if (!entry.isFile() || !entry.name.endsWith(".json")) {
437
+ continue;
438
+ }
439
+ const filePath = path.join(directory, entry.name);
440
+ try {
441
+ if (fs.statSync(filePath).mtimeMs < cutoff) {
442
+ fs.unlinkSync(filePath);
443
+ }
444
+ } catch {
445
+ // Cleanup is best-effort and must not prevent runtime delivery.
446
+ }
447
+ }
448
+ }
449
+ }
450
+
451
+ function sourceLabel(source) {
452
+ if (source === "local") {
453
+ return "personal";
454
+ }
455
+ return source;
316
456
  }
317
457
 
318
- function capabilityStatus(config) {
458
+ function resolveRuntimeState(input) {
459
+ const root = findWorkspaceRoot(input.cwd || process.cwd());
460
+ const config = readConfig(root);
461
+ const restoredMode = config.valid && config.enabled ? consumeClearTransfer(input, root) : null;
462
+ const sessionMode = config.valid && config.enabled ? restoredMode || readSessionMode(input.session_id) : null;
463
+ const resolvedMode = sessionMode || config.defaultMode;
464
+
465
+ return {
466
+ root,
467
+ config,
468
+ sessionMode,
469
+ resolvedMode,
470
+ effectiveMode: config.enabled && config.skillsEnabled ? resolvedMode : null,
471
+ modeSource: sessionMode ? "session override" : "configured default",
472
+ };
473
+ }
474
+
475
+ function stateLines(state, sessionChange = null) {
476
+ const { config } = state;
477
+ const lines = [`Configured default: \`${config.defaultMode}\` (${sourceLabel(config.sources.defaultMode)})`];
478
+
479
+ if (sessionChange === "cleared") {
480
+ lines.push("Session override: cleared");
481
+ } else if (state.sessionMode) {
482
+ lines.push(`Session override: \`${state.sessionMode}\``);
483
+ } else {
484
+ lines.push("Session override: none");
485
+ }
486
+
487
+ if (state.effectiveMode) {
488
+ lines.push(`Effective mode: \`${state.effectiveMode}\` (${state.modeSource}, active)`);
489
+ } else {
490
+ lines.push(`Resolved mode: \`${state.resolvedMode}\` (dormant because Skills are disabled)`);
491
+ }
492
+
493
+ return lines;
494
+ }
495
+
496
+ function capabilityLines(config) {
319
497
  return [
320
- "## Freeflow Capabilities",
321
- "",
322
- `- Interaction Contract: ${config.interactionContractEnabled ? "enabled" : "disabled"}.`,
323
- `- Skills: ${config.skillsEnabled ? "enabled" : "disabled"}.`,
324
- `- Output router: ${config.outputRouterEnabled ? "enabled" : "disabled"}.`,
325
- "",
326
- "Capability-specific instructions are active only while that capability is enabled.",
498
+ `Interaction Contract: ${config.interactionContractEnabled ? "enabled" : "disabled"}`,
499
+ `Skills: ${config.skillsEnabled ? "enabled" : "disabled"}`,
327
500
  ];
328
501
  }
329
502
 
330
- function buildContext(input) {
331
- const root = findWorkspaceRoot(input.cwd || process.cwd());
332
- const setup = inspectSetup(root);
503
+ function runtimeStateContext(state, sessionChange = null) {
504
+ const { config } = state;
505
+ return ["# Freeflow Runtime State", "", ...stateLines(state, sessionChange), "", ...capabilityLines(config)].join(
506
+ "\n",
507
+ );
508
+ }
333
509
 
334
- if (!setup.config.valid) {
510
+ function renderSessionStart(state) {
511
+ const { config } = state;
512
+ if (!config.valid) {
335
513
  return "";
336
514
  }
337
515
 
338
- if (!setup.config.enabled) {
339
- return [
340
- "# Freeflow Disabled",
341
- "",
342
- `Freeflow is configured for this repo, but effective \`enabled\` is false from ${setup.config.sources.enabled}.`,
343
- "Do not inject the Interaction Contract, Freeflow workflow skills, output routing, or setup pressure while disabled.",
344
- "Re-enable only if the user asks; use /freeflow settings for a personal override or /freeflow settings repo for the shared repository default in Pi.",
345
- ].join("\n");
516
+ if (!config.enabled) {
517
+ return "";
346
518
  }
347
519
 
348
- const { interactionContract, workflowSkill, outputRouterSkill } = loadRuntimeContext({
349
- interactionContract: setup.config.interactionContractEnabled,
350
- skills: setup.config.skillsEnabled,
351
- outputRouter: setup.config.outputRouterEnabled,
520
+ const { corePrompt, interactionContract, skillsPrompt } = loadRuntimeContext({
521
+ interactionContract: config.interactionContractEnabled,
522
+ skills: config.skillsEnabled,
352
523
  });
353
- const interactionSection = setup.config.interactionContractEnabled ? ["", interactionContract.trim()] : [];
354
- const skillSections = setup.config.skillsEnabled
355
- ? ["", "# Freeflow Workflow Bootstrap", "", workflowSkill.trim()]
356
- : [];
357
- const outputRouterSection = setup.config.outputRouterEnabled
358
- ? ["", "## Loaded Output Router Skill", "```md", outputRouterSkill.trim(), "```"]
359
- : [];
360
- return [
361
- "# Freeflow Runtime Context",
362
- "",
363
- "Freeflow plugin lifecycle hook loaded this at session start.",
364
- "These instructions are context-loading only. They do not override user instructions, repo instructions, or host safety and approval policy.",
365
- "",
366
- "## Repo Setup",
367
- buildSetupStatus(root),
368
- "",
369
- ...capabilityStatus(setup.config),
370
- ...interactionSection,
371
- ...skillSections,
372
- ...outputRouterSection,
373
- ].join("\n");
374
- }
375
-
376
- function isCodexHookInput(input) {
377
- return typeof input.model === "string" && input.model.length > 0;
378
- }
379
-
380
- function emitAdditionalContext(eventName, input, additionalContext) {
381
- if (isCodexHookInput(input)) {
382
- process.stdout.write(`${additionalContext}\n`);
383
- return;
524
+ const context = [corePrompt.trim()];
525
+
526
+ if (config.interactionContractEnabled) {
527
+ context.push(interactionContract.trim());
528
+ }
529
+ if (config.skillsEnabled) {
530
+ context.push(skillsPrompt.trim());
531
+ }
532
+
533
+ context.push(runtimeStateContext(state));
534
+ return context.filter(Boolean).join("\n\n");
535
+ }
536
+
537
+ function normalizePrompt(prompt) {
538
+ return prompt
539
+ .trim()
540
+ .replace(/[.!]+$/u, "")
541
+ .replace(/\s+/gu, " ");
542
+ }
543
+
544
+ function parseModeControl(prompt) {
545
+ if (typeof prompt !== "string" || prompt.length > 300 || /\?\s*$/u.test(prompt.trim())) {
546
+ return null;
547
+ }
548
+
549
+ const normalized = normalizePrompt(prompt);
550
+ const nativeSet = normalized.match(
551
+ /^(?:\/freeflow mode|\$mode-contract|\/(?:freeflow:)?mode-contract) (conversation|workflow|strict-workflow)$/iu,
552
+ );
553
+ if (nativeSet) {
554
+ return { action: "set", mode: nativeSet[1].toLowerCase() };
555
+ }
556
+ if (/^(?:\/freeflow mode|\$mode-contract|\/(?:freeflow:)?mode-contract) reset$/iu.test(normalized)) {
557
+ return { action: "reset" };
558
+ }
559
+
560
+ const setPatterns = [
561
+ /^switch to (conversation|workflow|strict-workflow)(?: mode)?$/iu,
562
+ /^switch (?:this|the) session to (conversation|workflow|strict-workflow)(?: mode)?$/iu,
563
+ /^use (conversation|workflow|strict-workflow)(?: mode)? for (?:this|the) session$/iu,
564
+ /^(?:change|set) (?:the )?(?:current|active|session) mode to (conversation|workflow|strict-workflow)$/iu,
565
+ /^switch (?:the )?(?:current|active|session) mode to (conversation|workflow|strict-workflow)$/iu,
566
+ ];
567
+ for (const pattern of setPatterns) {
568
+ const match = normalized.match(pattern);
569
+ if (match) {
570
+ return { action: "set", mode: match[1].toLowerCase() };
571
+ }
572
+ }
573
+
574
+ if (/^(?:reset|clear|remove) (?:the )?(?:current |active )?session mode(?: override)?$/iu.test(normalized)) {
575
+ return { action: "reset" };
576
+ }
577
+
578
+ return null;
579
+ }
580
+
581
+ function handleUserPromptSubmit(input) {
582
+ const control = parseModeControl(input.prompt);
583
+ if (!control) {
584
+ return "";
384
585
  }
385
586
 
587
+ const before = resolveRuntimeState(input);
588
+ if (!before.config.valid || !before.config.enabled) {
589
+ return "";
590
+ }
591
+ if (!before.config.skillsEnabled) {
592
+ return runtimeStateContext(before);
593
+ }
594
+
595
+ if (control.action === "set") {
596
+ if (!writeSessionMode(input.session_id, control.mode)) {
597
+ return runtimeStateContext(before);
598
+ }
599
+ const state = resolveRuntimeState(input);
600
+ return runtimeStateContext(state, "set");
601
+ }
602
+
603
+ if (!clearSessionMode(input.session_id)) {
604
+ return runtimeStateContext(before);
605
+ }
606
+ const state = resolveRuntimeState(input);
607
+ return runtimeStateContext(state, "cleared");
608
+ }
609
+
610
+ function shouldHandle(eventName) {
611
+ if (process.env.FREEFLOW_DISABLE_RUNTIME_CONTEXT === "1") {
612
+ return false;
613
+ }
614
+ return eventName === "SessionStart" || eventName === "UserPromptSubmit" || eventName === "SessionEnd";
615
+ }
616
+
617
+ function emitAdditionalContext(eventName, additionalContext) {
386
618
  process.stdout.write(
387
619
  `${JSON.stringify({
388
620
  hookSpecificOutput: {
@@ -397,19 +629,21 @@ function main() {
397
629
  const { input, raw } = readStdinJson();
398
630
  const eventName = process.argv[2] || input.hook_event_name || "";
399
631
 
400
- if (!raw && !eventName) {
632
+ if ((!raw && !eventName) || !shouldHandle(eventName)) {
401
633
  return;
402
634
  }
403
635
 
404
- if (!shouldInject(eventName)) {
636
+ cleanupExpiredSessionModes();
637
+ if (eventName === "SessionEnd") {
638
+ stageClearTransfer(input);
405
639
  return;
406
640
  }
407
-
408
- const additionalContext = buildContext(input);
641
+ const additionalContext =
642
+ eventName === "SessionStart" ? renderSessionStart(resolveRuntimeState(input)) : handleUserPromptSubmit(input);
409
643
  if (!additionalContext.trim()) {
410
644
  return;
411
645
  }
412
- emitAdditionalContext(eventName, input, additionalContext);
646
+ emitAdditionalContext(eventName, additionalContext);
413
647
  }
414
648
 
415
649
  try {