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