@causyne/agent 0.0.0-stage → 1.10.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 (274) hide show
  1. package/LICENSE +102 -0
  2. package/README.md +31 -2
  3. package/SECURITY.md +24 -0
  4. package/bin/causyne.js +13 -0
  5. package/docs/ARCHITECTURE.md +63 -0
  6. package/docs/PROVENANCE.md +29 -0
  7. package/docs/SECURITY_REVIEW.md +89 -0
  8. package/docs/THIRD_PARTY_NOTICES.md +49 -0
  9. package/native-skills/ai-engineering/context-window-management/SKILL.md +43 -0
  10. package/native-skills/ai-engineering/llm-evaluation/SKILL.md +46 -0
  11. package/native-skills/ai-engineering/model-cost-and-latency/SKILL.md +46 -0
  12. package/native-skills/ai-engineering/prompt-construction/SKILL.md +42 -0
  13. package/native-skills/ai-engineering/retrieval-augmented-generation/SKILL.md +42 -0
  14. package/native-skills/ai-engineering/structured-output-and-schemas/SKILL.md +45 -0
  15. package/native-skills/ai-engineering/tool-calling-agents/SKILL.md +43 -0
  16. package/native-skills/android/app-performance-profiling/SKILL.md +42 -0
  17. package/native-skills/android/background-work-scheduling/SKILL.md +47 -0
  18. package/native-skills/android/compose-state-and-recomposition/SKILL.md +43 -0
  19. package/native-skills/android/gradle-build-configuration/SKILL.md +41 -0
  20. package/native-skills/android/lifecycle-and-leaks/SKILL.md +46 -0
  21. package/native-skills/android/play-store-release/SKILL.md +43 -0
  22. package/native-skills/android/room-and-persistence/SKILL.md +40 -0
  23. package/native-skills/backend/authentication-and-sessions/SKILL.md +45 -0
  24. package/native-skills/backend/background-jobs-and-queues/SKILL.md +42 -0
  25. package/native-skills/backend/caching-strategy/SKILL.md +44 -0
  26. package/native-skills/backend/graceful-shutdown/SKILL.md +42 -0
  27. package/native-skills/backend/rate-limiting-and-quotas/SKILL.md +40 -0
  28. package/native-skills/backend/rest-api-design/SKILL.md +40 -0
  29. package/native-skills/backend/webhook-delivery-and-receipt/SKILL.md +43 -0
  30. package/native-skills/cloud/cost-optimisation/SKILL.md +46 -0
  31. package/native-skills/cloud/iam-least-privilege/SKILL.md +43 -0
  32. package/native-skills/cloud/infrastructure-as-code/SKILL.md +43 -0
  33. package/native-skills/cloud/multi-region-and-failover/SKILL.md +43 -0
  34. package/native-skills/cloud/serverless-patterns/SKILL.md +47 -0
  35. package/native-skills/data/data-quality-monitoring/SKILL.md +51 -0
  36. package/native-skills/data/etl-pipeline-reliability/SKILL.md +43 -0
  37. package/native-skills/data/exploratory-analysis/SKILL.md +43 -0
  38. package/native-skills/data/metric-definition/SKILL.md +42 -0
  39. package/native-skills/data/notebook-to-production/SKILL.md +43 -0
  40. package/native-skills/database/backup-and-recovery/SKILL.md +43 -0
  41. package/native-skills/database/connection-pooling/SKILL.md +42 -0
  42. package/native-skills/database/data-integrity-and-constraints/SKILL.md +42 -0
  43. package/native-skills/database/query-optimisation/SKILL.md +38 -0
  44. package/native-skills/database/safe-migrations/SKILL.md +47 -0
  45. package/native-skills/database/schema-design/SKILL.md +38 -0
  46. package/native-skills/database/transactions-and-isolation/SKILL.md +41 -0
  47. package/native-skills/design/colour-system-construction/SKILL.md +38 -0
  48. package/native-skills/design/design-direction-brief/SKILL.md +41 -0
  49. package/native-skills/design/motion-and-transitions/SKILL.md +38 -0
  50. package/native-skills/design/responsive-strategy/SKILL.md +39 -0
  51. package/native-skills/design/spacing-and-rhythm/SKILL.md +38 -0
  52. package/native-skills/design/type-scale-and-hierarchy/SKILL.md +39 -0
  53. package/native-skills/design/visual-qa-loop/SKILL.md +42 -0
  54. package/native-skills/devops/docker-multi-stage-builds/SKILL.md +60 -0
  55. package/native-skills/devops/github-actions-ci/SKILL.md +60 -0
  56. package/native-skills/devops/incident-triage/SKILL.md +38 -0
  57. package/native-skills/devops/kubernetes-deployment-hardening/SKILL.md +64 -0
  58. package/native-skills/devops/observability-baseline/SKILL.md +38 -0
  59. package/native-skills/devops/secret-management/SKILL.md +40 -0
  60. package/native-skills/devops/terraform-state-and-modules/SKILL.md +36 -0
  61. package/native-skills/devops/zero-downtime-deploys/SKILL.md +40 -0
  62. package/native-skills/git/bisect-regression-hunt/SKILL.md +43 -0
  63. package/native-skills/git/branch-strategy/SKILL.md +39 -0
  64. package/native-skills/git/commit-hygiene/SKILL.md +41 -0
  65. package/native-skills/git/history-recovery/SKILL.md +44 -0
  66. package/native-skills/git/large-repository-surgery/SKILL.md +46 -0
  67. package/native-skills/git/merge-conflict-resolution/SKILL.md +42 -0
  68. package/native-skills/media/cinematic-3d-shot/SKILL.md +104 -0
  69. package/native-skills/mobile/app-startup-optimisation/SKILL.md +41 -0
  70. package/native-skills/mobile/cross-platform-tradeoffs/SKILL.md +42 -0
  71. package/native-skills/mobile/deep-linking/SKILL.md +39 -0
  72. package/native-skills/mobile/mobile-storage-security/SKILL.md +45 -0
  73. package/native-skills/mobile/offline-first-sync/SKILL.md +42 -0
  74. package/native-skills/mobile/push-notifications/SKILL.md +38 -0
  75. package/native-skills/performance/algorithmic-complexity/SKILL.md +43 -0
  76. package/native-skills/performance/concurrency-and-parallelism/SKILL.md +42 -0
  77. package/native-skills/performance/load-testing/SKILL.md +42 -0
  78. package/native-skills/performance/memory-and-leak-hunting/SKILL.md +41 -0
  79. package/native-skills/performance/network-and-payload-efficiency/SKILL.md +42 -0
  80. package/native-skills/performance/profiling-methodology/SKILL.md +46 -0
  81. package/native-skills/python/async-correctness/SKILL.md +40 -0
  82. package/native-skills/python/cli-tool-construction/SKILL.md +44 -0
  83. package/native-skills/python/data-processing-pipelines/SKILL.md +38 -0
  84. package/native-skills/python/environment-isolation/SKILL.md +41 -0
  85. package/native-skills/python/packaging-and-distribution/SKILL.md +56 -0
  86. package/native-skills/python/pytest-suite-design/SKILL.md +39 -0
  87. package/native-skills/python/type-hints-and-static-checking/SKILL.md +42 -0
  88. package/native-skills/security/authorization-model/SKILL.md +42 -0
  89. package/native-skills/security/crypto-usage/SKILL.md +48 -0
  90. package/native-skills/security/dependency-supply-chain/SKILL.md +42 -0
  91. package/native-skills/security/input-validation-and-injection/SKILL.md +44 -0
  92. package/native-skills/security/secure-file-uploads/SKILL.md +46 -0
  93. package/native-skills/security/security-headers-and-csp/SKILL.md +44 -0
  94. package/native-skills/security/threat-modelling/SKILL.md +46 -0
  95. package/native-skills/testing/end-to-end-test-stability/SKILL.md +42 -0
  96. package/native-skills/testing/property-based-testing/SKILL.md +46 -0
  97. package/native-skills/testing/regression-test-from-a-bug/SKILL.md +41 -0
  98. package/native-skills/testing/snapshot-and-visual-regression/SKILL.md +42 -0
  99. package/native-skills/testing/test-doubles-and-mocking/SKILL.md +43 -0
  100. package/native-skills/testing/test-strategy-and-pyramid/SKILL.md +39 -0
  101. package/native-skills/testing/writing-good-assertions/SKILL.md +42 -0
  102. package/native-skills/web/accessible-components/SKILL.md +45 -0
  103. package/native-skills/web/api-client-resilience/SKILL.md +39 -0
  104. package/native-skills/web/bundle-size-reduction/SKILL.md +38 -0
  105. package/native-skills/web/core-web-vitals/SKILL.md +40 -0
  106. package/native-skills/web/css-layout-systems/SKILL.md +41 -0
  107. package/native-skills/web/form-validation-ux/SKILL.md +39 -0
  108. package/native-skills/web/react-state-architecture/SKILL.md +41 -0
  109. package/native-skills/web/ssr-hydration-correctness/SKILL.md +45 -0
  110. package/package.json +68 -4
  111. package/src/agentLoop.js +2458 -0
  112. package/src/appChannel.js +109 -0
  113. package/src/approvalGrants.js +66 -0
  114. package/src/auditLog.js +52 -0
  115. package/src/autoUpdate.js +12 -0
  116. package/src/capabilities.js +36 -0
  117. package/src/checkpoints.js +1199 -0
  118. package/src/cliMode.js +638 -0
  119. package/src/commands.js +805 -0
  120. package/src/compiledSkills.js +540 -0
  121. package/src/config.js +130 -0
  122. package/src/contextManager.js +498 -0
  123. package/src/cortex/autoCondenser.js +192 -0
  124. package/src/cortex/provisioner.js +27 -0
  125. package/src/cortex/vectorDb.js +447 -0
  126. package/src/designPalettes.js +99 -0
  127. package/src/devMode.js +6 -0
  128. package/src/embodimentSession.js +180 -0
  129. package/src/entrypoint.js +194 -0
  130. package/src/execSafe.js +21 -0
  131. package/src/gitSnapshot.js +378 -0
  132. package/src/mcp/jsonRpc.js +165 -0
  133. package/src/mcp/mcpClient.js +136 -0
  134. package/src/mcp/mcpRegistry.js +207 -0
  135. package/src/memory.js +62 -0
  136. package/src/memoryManager.js +124 -0
  137. package/src/modelCatalogue.js +132 -0
  138. package/src/modelIds.js +42 -0
  139. package/src/modelRouter.js +145 -0
  140. package/src/outputStore.js +246 -0
  141. package/src/perception.js +124 -0
  142. package/src/planCache.js +437 -0
  143. package/src/procSignal.js +32 -0
  144. package/src/providers/anthropic.js +408 -0
  145. package/src/providers/base.js +328 -0
  146. package/src/providers/dashscope-byok.js +86 -0
  147. package/src/providers/deepseek.js +80 -0
  148. package/src/providers/factory.js +37 -0
  149. package/src/providers/gemini.js +346 -0
  150. package/src/providers/moonshot.js +73 -0
  151. package/src/providers/muse.js +169 -0
  152. package/src/providers/ollama.js +104 -0
  153. package/src/providers/openai-compatible.js +84 -0
  154. package/src/providers/openai.js +20 -0
  155. package/src/providers/qwen-byok.js +76 -0
  156. package/src/providers/xai.js +73 -0
  157. package/src/provisioning/capabilityConsent.js +24 -0
  158. package/src/provisioning/capabilityProvisioner.js +271 -0
  159. package/src/provisioning/capabilityTools.js +29 -0
  160. package/src/reporting.js +373 -0
  161. package/src/sandbox.js +529 -0
  162. package/src/sessionStore.js +279 -0
  163. package/src/skillsManager.js +288 -0
  164. package/src/slopDetector.js +307 -0
  165. package/src/taskBuffer.js +45 -0
  166. package/src/taskShape.js +241 -0
  167. package/src/textDiff.js +150 -0
  168. package/src/tokenTracker.js +560 -0
  169. package/src/tool-scripts/ax_snapshot.applescript +39 -0
  170. package/src/tool-scripts/ax_snapshot.ps1 +10 -0
  171. package/src/tool-scripts/desktop_act.applescript +58 -0
  172. package/src/tool-scripts/resolve_automator.py +119 -0
  173. package/src/toolPacks.js +524 -0
  174. package/src/tools/archive.js +63 -0
  175. package/src/tools/astryxOracle.js +78 -0
  176. package/src/tools/axSnapshot.js +165 -0
  177. package/src/tools/blenderAutomator.js +349 -0
  178. package/src/tools/browserAutomator.js +252 -0
  179. package/src/tools/checkSystemSpecs.js +64 -0
  180. package/src/tools/clipboard.js +36 -0
  181. package/src/tools/codeSearchAst.js +101 -0
  182. package/src/tools/codeSearcher.js +230 -0
  183. package/src/tools/codeTools.js +262 -0
  184. package/src/tools/codebaseSearch.js +75 -0
  185. package/src/tools/createSkill.js +129 -0
  186. package/src/tools/deferredMcpSearch.js +246 -0
  187. package/src/tools/desktopAct.js +208 -0
  188. package/src/tools/desktopOpen.js +34 -0
  189. package/src/tools/dynamicLoader.js +44 -0
  190. package/src/tools/executeProceduralMedia.js +93 -0
  191. package/src/tools/fsDelete.js +51 -0
  192. package/src/tools/fsEdit.js +105 -0
  193. package/src/tools/fsFind.js +128 -0
  194. package/src/tools/fsList.js +77 -0
  195. package/src/tools/fsMove.js +57 -0
  196. package/src/tools/fsRead.js +87 -0
  197. package/src/tools/fsSearch.js +198 -0
  198. package/src/tools/fsWrite.js +41 -0
  199. package/src/tools/gameAiBehaviorTree.js +208 -0
  200. package/src/tools/gameAnimationRig.js +194 -0
  201. package/src/tools/gameAssetFetcher.js +372 -0
  202. package/src/tools/gameAssetHarvester.js +210 -0
  203. package/src/tools/gameAudioSynth.js +267 -0
  204. package/src/tools/gameBuilder.js +886 -0
  205. package/src/tools/gameCanvasAudit.js +101 -0
  206. package/src/tools/gameCinematicSequencer.js +151 -0
  207. package/src/tools/gameDataForge.js +124 -0
  208. package/src/tools/gameDialogueTreeGen.js +166 -0
  209. package/src/tools/gameEconomyBalancer.js +161 -0
  210. package/src/tools/gameEntitySpawner.js +207 -0
  211. package/src/tools/gameLoreForge.js +153 -0
  212. package/src/tools/gameQuestArcBuilder.js +152 -0
  213. package/src/tools/gameSceneBuilder.js +158 -0
  214. package/src/tools/gameShaderForge.js +181 -0
  215. package/src/tools/gameTilemapGenerator.js +164 -0
  216. package/src/tools/gameUiHudBuilder.js +292 -0
  217. package/src/tools/gameVfxEmitterGen.js +108 -0
  218. package/src/tools/gameWebRuntimeInspector.js +91 -0
  219. package/src/tools/generateMediaViaApi.js +358 -0
  220. package/src/tools/gitExec.js +54 -0
  221. package/src/tools/godotAutomator.js +437 -0
  222. package/src/tools/manageLocalAiDependencies.js +220 -0
  223. package/src/tools/memoryWrite.js +99 -0
  224. package/src/tools/multiEdit.js +107 -0
  225. package/src/tools/palmierAutomator.js +104 -0
  226. package/src/tools/pdfReader.js +112 -0
  227. package/src/tools/persistentShell.js +148 -0
  228. package/src/tools/printer.js +316 -0
  229. package/src/tools/procTerm.js +91 -0
  230. package/src/tools/processList.js +52 -0
  231. package/src/tools/readNativeSkill.js +39 -0
  232. package/src/tools/registry.js +277 -0
  233. package/src/tools/resolveAutomator.js +102 -0
  234. package/src/tools/screenshot.js +75 -0
  235. package/src/tools/subagentOrchestrator.js +305 -0
  236. package/src/tools/sysRun.js +125 -0
  237. package/src/tools/todoWrite.js +75 -0
  238. package/src/tools/toolDispatch.js +127 -0
  239. package/src/tools/toolForge.js +124 -0
  240. package/src/tools/updatePlan.js +88 -0
  241. package/src/tools/viewImage.js +82 -0
  242. package/src/tools/visionInspector.js +154 -0
  243. package/src/tools/webFetch.js +74 -0
  244. package/src/tools/webSearch.js +82 -0
  245. package/src/tui/ask.js +164 -0
  246. package/src/tui/banner.js +158 -0
  247. package/src/tui/block.js +152 -0
  248. package/src/tui/chatRenderer.js +953 -0
  249. package/src/tui/completion.js +424 -0
  250. package/src/tui/dialogs.js +606 -0
  251. package/src/tui/diff.js +296 -0
  252. package/src/tui/history.js +135 -0
  253. package/src/tui/home.js +217 -0
  254. package/src/tui/invocation.js +105 -0
  255. package/src/tui/keys.js +443 -0
  256. package/src/tui/lineEditor.js +256 -0
  257. package/src/tui/overlay.js +256 -0
  258. package/src/tui/prompt.js +1258 -0
  259. package/src/tui/selectList.js +335 -0
  260. package/src/tui/stage.js +330 -0
  261. package/src/tui/stageSurface.js +609 -0
  262. package/src/tui/statusLine.js +254 -0
  263. package/src/tui/text.js +345 -0
  264. package/src/tui/theme.js +393 -0
  265. package/src/tui/toolDisplay.js +176 -0
  266. package/src/tui/transcript.js +235 -0
  267. package/src/tui/viewport.js +337 -0
  268. package/src/uncertainty.js +271 -0
  269. package/src/userSkills.js +265 -0
  270. package/src/utils/cli_ui.js +0 -0
  271. package/src/utils/inquirer.js +12 -0
  272. package/src/utils/paths.js +37 -0
  273. package/src/version.js +38 -0
  274. package/src/vfeTracker.js +220 -0
@@ -0,0 +1,119 @@
1
+ import sys
2
+ import json
3
+ import os
4
+
5
+ def run_bridge(args):
6
+ try:
7
+ import DaVinciResolveScript as dvr_script
8
+ except ImportError:
9
+ print(json.dumps({"success": False, "error": "[ENGINE MISSING] DaVinci Resolve Studio Python API not found. Please ensure 'External scripting using: Local' is enabled in Preferences -> System -> General, and DaVinci Resolve Studio is running."}))
10
+ return
11
+
12
+ resolve = dvr_script.scriptapp("Resolve")
13
+ if resolve is None:
14
+ print(json.dumps({"success": False, "error": "[CONNECTION ERROR] Could not connect to DaVinci Resolve. Please ensure DaVinci Resolve Studio is open."}))
15
+ return
16
+
17
+ projectManager = resolve.GetProjectManager()
18
+ project = projectManager.GetCurrentProject()
19
+ if not project:
20
+ print(json.dumps({"success": False, "error": "No open project found."}))
21
+ return
22
+
23
+ mediaPool = project.GetMediaPool()
24
+ timeline = project.GetCurrentTimeline()
25
+
26
+ action = args.get("action")
27
+
28
+ if action == "checkConnection":
29
+ print(json.dumps({
30
+ "success": True,
31
+ "message": f"Connected to DaVinci Resolve. Active Project: {project.GetName()}"
32
+ }))
33
+ return
34
+
35
+ elif action == "setupColorManagement":
36
+ mode = args.get("color_space_mode", "automatic").lower()
37
+ try:
38
+ project.SetSetting("colorScienceMode", "davinciYRGBColorManaged")
39
+
40
+ if mode == "automatic":
41
+ project.SetSetting("automaticColorManagement", "1")
42
+ print(json.dumps({
43
+ "success": True,
44
+ "message": "Successfully applied Automatic Resolve Color Management (RCM). DaVinci will now auto-detect S-Log, LogC, BRAW, etc."
45
+ }))
46
+ else:
47
+ project.SetSetting("automaticColorManagement", "0")
48
+ project.SetSetting("colorSpaceManager", "Custom")
49
+ project.SetSetting("colorSpaceInput", args.get("input_color_space", "ARRI LogC3"))
50
+ project.SetSetting("colorSpaceTimeline", "DaVinci WG/Intermediate")
51
+ project.SetSetting("colorSpaceOutput", args.get("output_color_space", "Rec.709 Gamma 2.4"))
52
+
53
+ print(json.dumps({
54
+ "success": True,
55
+ "message": f"Successfully applied Custom RCM: {args.get('input_color_space', 'ARRI LogC3')} to {args.get('output_color_space', 'Rec.709 Gamma 2.4')}."
56
+ }))
57
+ except Exception as e:
58
+ print(json.dumps({"success": False, "error": f"Failed to setup RCM: {str(e)}"}))
59
+ return
60
+
61
+ elif action == "importAndAssemble":
62
+ media_path = args.get("mediaPath")
63
+ if not media_path or not os.path.exists(media_path):
64
+ print(json.dumps({"success": False, "error": f"Media path not found: {media_path}"}))
65
+ return
66
+
67
+ try:
68
+ imported_clips = mediaPool.ImportMedia([media_path])
69
+ if not imported_clips:
70
+ print(json.dumps({"success": False, "error": "No media found or imported."}))
71
+ return
72
+
73
+ fps = str(args.get("fps", "auto")).lower()
74
+ res_x = str(args.get("resolution_x", "auto")).lower()
75
+ res_y = str(args.get("resolution_y", "auto")).lower()
76
+
77
+ if fps == "auto" or res_x == "auto" or res_y == "auto":
78
+ first_clip = imported_clips[0]
79
+
80
+ if fps == "auto":
81
+ detected_fps = first_clip.GetClipProperty("FPS")
82
+ fps = str(detected_fps) if detected_fps else "23.976"
83
+
84
+ if res_x == "auto" or res_y == "auto":
85
+ detected_res = first_clip.GetClipProperty("Resolution")
86
+ if detected_res and "x" in str(detected_res):
87
+ parts = str(detected_res).split("x")
88
+ res_x, res_y = parts[0], parts[1]
89
+ else:
90
+ res_x, res_y = "4096", "2160"
91
+
92
+ project.SetSetting("timelineFrameRate", fps)
93
+ project.SetSetting("timelineResolutionWidth", res_x)
94
+ project.SetSetting("timelineResolutionHeight", res_y)
95
+
96
+ if not timeline:
97
+ timeline = mediaPool.CreateEmptyTimeline("Causyne_Automated_Timeline")
98
+
99
+ mediaPool.AppendToTimeline(imported_clips)
100
+
101
+ print(json.dumps({
102
+ "success": True,
103
+ "message": f"Successfully imported {len(imported_clips)} clips and auto-assembled on timeline '{timeline.GetName()}' at detected {res_x}x{res_y} {fps}fps."
104
+ }))
105
+ except Exception as e:
106
+ print(json.dumps({"success": False, "error": f"Failed during auto-import/assembly: {str(e)}"}))
107
+ return
108
+
109
+ else:
110
+ print(json.dumps({"success": False, "error": f"Unknown action: {action}"}))
111
+ return
112
+
113
+ if __name__ == "__main__":
114
+ try:
115
+ raw_args = sys.argv[1]
116
+ args = json.loads(raw_args)
117
+ run_bridge(args)
118
+ except Exception as e:
119
+ print(json.dumps({"success": False, "error": f"Python Bridge Error: {str(e)}"}))
@@ -0,0 +1,524 @@
1
+ // Sovereign Progressive Disclosure — Capability Packs
2
+ //
3
+ // Causyne ships 69 native tools. Sending every schema on every LLM call costs
4
+ // ~14,700 tokens per step before the agent has done any work, and half of that
5
+ // is game tooling that a "rename my files" task never touches.
6
+ //
7
+ // This module does NOT remove any capability. It only controls *when* a schema
8
+ // enters the context window. Every tool stays reachable through three routes:
9
+ // 1. Intent pre-warm — routePacks() unlocks the right packs before step 1
10
+ // 2. tool_search — the agent discovers and unlocks any pack on demand
11
+ // 3. Auto-unlock recovery — calling a locked-but-real tool unlocks its pack
12
+ //
13
+ // Set config.grantAllTools = true to disable deferral entirely.
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Pack membership. Names match the `name` field of each tool schema, with dots
17
+ // already normalised to underscores (see tools/registry.js TOOL_DEFINITIONS).
18
+ // ---------------------------------------------------------------------------
19
+
20
+ export const CORE_TOOLS = [
21
+ 'sysRun',
22
+ 'sysRunBg',
23
+ 'terminal_persistent_shell',
24
+ 'fs_read',
25
+ 'fs_write',
26
+ 'fs_edit',
27
+ 'MultiEdit',
28
+ 'fs_list',
29
+ 'fs_search',
30
+ 'fs_find',
31
+ 'fs_move',
32
+ 'fs_delete',
33
+ 'gitOp',
34
+ 'code_search_ripgrep',
35
+ 'codebase_search',
36
+ 'code_verify_syntax',
37
+ 'web_search',
38
+ 'web_fetch',
39
+ 'TodoWrite',
40
+ 'update_plan',
41
+ 'memory_write',
42
+ 'fs_read_skill',
43
+ 'skill_create',
44
+ 'tool_search'
45
+ ];
46
+
47
+ export const PACKS = {
48
+ game: [
49
+ // Stage 1 of the game cascade calls check_system_specs to verify the
50
+ // engine, so it must travel with this pack as well as with media.
51
+ 'check_system_specs',
52
+ 'godot_act',
53
+ 'game_build_html5',
54
+ 'game_asset_fetch',
55
+ 'game_asset_harvest',
56
+ 'game_lore_forge',
57
+ 'game_dialogue_tree_gen',
58
+ 'game_quest_arc_builder',
59
+ 'game_animation_rig',
60
+ 'game_ai_behavior_tree',
61
+ 'game_cinematic_sequencer',
62
+ 'game_audio_synth',
63
+ 'game_vfx_emitter_gen',
64
+ 'game_shader_forge',
65
+ 'game_scene_build',
66
+ 'game_entity_spawner',
67
+ 'game_tilemap_gen',
68
+ 'game_ui_hud_builder',
69
+ 'game_economy_balancer',
70
+ 'game_data_forge',
71
+ 'game_web_runtime_inspector',
72
+ 'game_canvas_audit'
73
+ ],
74
+ web: [
75
+ 'browser_act',
76
+ 'screenshot',
77
+ 'vision_inspect_ui',
78
+ 'view_image',
79
+ 'astryx_oracle'
80
+ ],
81
+ media: [
82
+ 'generate_media_via_api',
83
+ 'execute_procedural_media',
84
+ 'check_system_specs',
85
+ 'manage_local_dependencies',
86
+ 'blender_act',
87
+ 'resolve_act',
88
+ 'palmier_act'
89
+ ],
90
+ system: [
91
+ // Channel B of the Three-Channel Doctrine: reading the screen as structured
92
+ // text instead of pixels. Lives here rather than in core because most tasks
93
+ // never touch a GUI, and tool_search brings it in the moment one does.
94
+ 'ax_snapshot',
95
+ // Channel C. Ships beside its observer on purpose: acting on a screen you
96
+ // have not read is how an agent clicks the wrong button confidently.
97
+ 'desktop_act',
98
+ 'process_list',
99
+ 'proc_term',
100
+ 'archive',
101
+ 'clipboard',
102
+ 'desktop_open',
103
+ 'printer_print',
104
+ 'printer_list_printers',
105
+ 'printer_get_status'
106
+ ],
107
+ advanced: [
108
+ 'tool_forge',
109
+ 'agent_subagent_orchestrate',
110
+ 'code_search_ast',
111
+ 'code_list_symbols',
112
+ 'pdf_read'
113
+ ]
114
+ };
115
+
116
+ export const PACK_NAMES = Object.keys(PACKS);
117
+
118
+ /**
119
+ * The core tools that ship in EVERY request on a provider with no native
120
+ * schema deferral. The rest of CORE_TOOLS is withheld until the model asks for
121
+ * it with `tool_search`, exactly as a capability pack is.
122
+ *
123
+ * WHY: Anthropic can hold schemas server-side (`defer_loading` +
124
+ * `tool_search_tool_regex`), so on Claude the full core costs nothing to
125
+ * advertise. That is one provider out of eleven. On OpenAI, Gemini, DeepSeek,
126
+ * Qwen, Muse, xAI, Moonshot, Ollama and the two BYOK paths, all 24 core
127
+ * schemas were serialised into every single request — 3,341 tokens, of which a
128
+ * typical turn calls two.
129
+ *
130
+ * WHAT IS IN, AND WHY EXACTLY THESE: read, write, edit, list and search cover
131
+ * every file operation a first turn can need; `sysRun` is the escape hatch that
132
+ * can reach anything the other seven cannot; `TodoWrite` is how a multi-step
133
+ * task announces its plan on step one; `tool_search` is the door to the other
134
+ * 62. With this set the agent is never stuck — worst case it spends one call
135
+ * discovering the tool it wants, and the tool it discovers stays loaded for the
136
+ * rest of the task.
137
+ *
138
+ * WHAT WITHHOLDING DOES NOT DO: it does not remove capability. `resolveTool()`
139
+ * still resolves a withheld core tool by name, so a model that calls one from
140
+ * memory simply gets it. Only the schema is absent, never the tool.
141
+ */
142
+ export const RESIDENT_TOOLS = [
143
+ 'fs_read',
144
+ 'fs_write',
145
+ 'fs_edit',
146
+ 'fs_list',
147
+ 'fs_search',
148
+ 'sysRun',
149
+ 'TodoWrite',
150
+ 'tool_search'
151
+ ];
152
+
153
+ const RESIDENT_TOOL_SET = new Set(RESIDENT_TOOLS);
154
+
155
+ /** True when this core tool ships in every request even without native deferral. */
156
+ export function isResidentTool(toolName) {
157
+ return RESIDENT_TOOL_SET.has(toolName);
158
+ }
159
+
160
+ // A tool may legitimately belong to more than one pack — check_system_specs is
161
+ // Stage 1 of the game cascade and Stage 2 of the media hierarchy — so
162
+ // membership is a set, not a single value.
163
+ const TOOL_TO_PACKS = (() => {
164
+ const map = new Map();
165
+ for (const packName of PACK_NAMES) {
166
+ for (const toolName of PACKS[packName]) {
167
+ if (!map.has(toolName)) map.set(toolName, new Set());
168
+ map.get(toolName).add(packName);
169
+ }
170
+ }
171
+ return map;
172
+ })();
173
+
174
+ const CORE_TOOL_SET = new Set(CORE_TOOLS);
175
+
176
+ /** Every pack a tool belongs to. Empty for core and forged tools. */
177
+ export function packsForTool(toolName) {
178
+ return TOOL_TO_PACKS.get(toolName) || null;
179
+ }
180
+
181
+ /** Primary pack for a tool, or 'core' if it is always loaded. */
182
+ export function packForTool(toolName) {
183
+ if (CORE_TOOL_SET.has(toolName)) return 'core';
184
+ const packs = TOOL_TO_PACKS.get(toolName);
185
+ return packs ? [...packs][0] : null;
186
+ }
187
+
188
+ export function isCoreTool(toolName) {
189
+ return CORE_TOOL_SET.has(toolName);
190
+ }
191
+
192
+ // ---------------------------------------------------------------------------
193
+ // Pack playbooks. These were previously hardcoded into the always-on
194
+ // SYSTEM_PROMPT, costing every task ~2,000 tokens of instructions about game
195
+ // cascades and media hierarchies it would never use. They now travel with their
196
+ // pack, so they arrive exactly when they become relevant.
197
+ // ---------------------------------------------------------------------------
198
+
199
+ export const PACK_PLAYBOOKS = {
200
+ web: `# FRONTEND ENGINEERING MANDATE (CREATIVE DIVERSITY ENGINE)
201
+ When building websites, UIs, dashboards, or web applications, follow this design system hierarchy:
202
+ ## Design System Selection
203
+ 1. **If the user specifies a design system** (Tailwind, Material-UI, Astryx, etc.) — use it without question.
204
+ 2. **If the project already has a design system installed** (detected via package.json) — use the existing one. Never mix systems.
205
+ 3. **If building from scratch with no preference stated** — You have CREATIVE FREEDOM. Intelligently choose the approach that best fits the project's personality and user intent:
206
+ - **Astryx (@astryxdesign/core)**: Use the 'astryx_oracle' tool for docs. Best for enterprise dashboards, SaaS products, and design-system-heavy projects. If you choose Astryx, never hallucinate props — always query the oracle first.
207
+ - **Tailwind CSS**: Best for rapid prototyping, marketing sites, and projects needing maximum customization speed.
208
+ - **Vanilla CSS**: Best for artistic, portfolio, and showcase sites where every pixel is intentional.
209
+ - **CSS Modules / Styled Components**: Best for React component libraries and design-token-driven architectures.
210
+ ## ANTI-AI-SLOP DIRECTIVE (MANDATORY)
211
+ You MUST produce visually UNIQUE output on every project. Specifically:
212
+ - **BANNED**: Purple-to-blue gradients as the primary color scheme. This is the #1 AI slop signature.
213
+ - **BANNED**: Generic hero sections with centered text + gradient background + "Get Started" button.
214
+ - **REQUIRED**: Before writing any CSS, internally decide on a SPECIFIC design movement to draw from. Examples: Swiss/International Style, Brutalism, Art Deco, Japanese Minimalism, Neomorphism, Glassmorphism, Memphis Design, Bauhaus, Editorial/Magazine, Retro-Futurism, Scandinavian Minimal. Commit to one. Don't blend 5 styles into mush.
215
+ - **REQUIRED**: Choose a curated, harmonious color palette (NOT random defaults). Use HSL for precision. Reference real-world brands known for outstanding design (Stripe's clean whites, Linear's deep darks, Notion's warm neutrals, Vercel's stark monochrome).
216
+ - **REQUIRED**: Use modern web fonts (Inter, Outfit, Space Grotesk, JetBrains Mono, etc.) — NEVER browser defaults.
217
+ - **REQUIRED**: After building, use 'screenshot' or 'browser_act' to VISUALLY VERIFY your work. If it looks like every other AI-generated site, tear it down and rebuild with a different approach.
218
+ - **REQUIRED**: Once the design is finished, call 'memory_write' with the creativePattern argument (project, designApproach, colorPalette, fonts). This is what lets the next project deliberately differ from this one instead of drifting back into the same house style.
219
+ - **ENFORCED, NOT ADVISORY**: every HTML/CSS file you write is read back and scored by an automatic gate before the task can finish (src/slopDetector.js). It looks for the stock gradient hexes, purple-to-blue ramps, placeholder marketing copy, emoji used as icons, default-only typography, the centered-hero/3-cards/"Get Started" skeleton and the stock soft shadow. Score too high and you will be sent back to rebuild with a named design movement. Design it properly the first time; the gate is not fooled by swapping one gradient for another.`,
220
+
221
+ media: `# Media Generation Protocol (Sovereign 5-Stage Hierarchy & Seamless Consent Routing)
222
+ When the user asks you to generate images, audio, music, video, or any creative media, you MUST navigate this exact 5-Stage priority chain:
223
+
224
+ ## Stage 1: Specialized Paid APIs (Primary / Auto-Router)
225
+ Always attempt calling \`generate_media_via_api\` FIRST.
226
+ - **Capabilities**: Auto-routes to Gemini Veo 2 / Imagen 3, OpenAI DALL-E 3 / Sora 2 / TTS, or Qwen Wan based on configured keys.
227
+ - **Interactive Pre-Flight Consent**: \`generate_media_via_api\` will automatically show exact Cost Estimation ($) and Token Usage Estimation (~Equivalent MEU Tokens) to the user before spending credits.
228
+ - **Seamless Denial Routing**: If the user denies or rejects the cost modal (\`approved: false\`) OR if no paid API keys exist, do NOT crash or stop. Immediately transition to Stage 2.
229
+
230
+ ## Stage 2: Local AI Models / Binaries (Mode B & C — First Fallback)
231
+ When Stage 1 is denied or unconfigured, you MUST evaluate local processing on the user's PC:
232
+ 1. **Mandatory Gatekeeper**: Call \`check_system_specs\` FIRST right now to verify hardware.
233
+ 2. **Eligibility & Action**: If RAM >= 8GB and Free Disk > 10GB, use \`manage_local_ai_dependencies\` (\`pipInst\` or \`fetchBin\`) + \`sysRun\` to run:
234
+ - **Audio / Voice / Music**: Kokoro TTS, XTTS, MusicGen (\`pipInst\`), or Whisper (\`whisper.cpp\` / \`bark.cpp\`).
235
+ - **Image / Art**: Stable Diffusion via \`diffusers\` or \`stable-diffusion.cpp\`.
236
+ 3. **Download Consent**: Present exact model/binary size to the user before downloading multi-GB files.
237
+ 4. **Seamless Denial Routing**: If \`check_system_specs\` shows RAM < 8GB OR the user denies multi-GB local downloads, immediately transition to Stage 3.
238
+
239
+ ## Stage 3: Free Cloud APIs / Pollinations.ai (Mode D — Second Fallback)
240
+ When Stage 2 is impossible or denied, fall back to free cloud APIs via \`generate_media_via_api\` (\`provider: 'pollinations'\`).
241
+ - **Capabilities**: High-quality images (\`flux\`, \`turbo\`) and audio TTS (\`nova\`, \`alloy\`) at $0 Cost and 0 MB local storage.
242
+ - **Seamless Denial/Failure Routing**: If Pollinations fails (\`HTTP 500\`) OR does not natively support the format (e.g., instrumental music tracks or complex video animations), immediately transition to Stage 4.
243
+
244
+ ## Stage 4: Targeted Web Search & Asset Retrieval (Third Fallback)
245
+ When Stage 3 fails or does not support the format:
246
+ - Use \`web_search\` + \`web_fetch\` to search the internet for royalty-free stock videos, public domain images, sound effects, or instrumental background music tracks matching the user's exact intent and prompt. Download and verify the asset locally.
247
+ - **Seamless Failure Routing**: If no satisfactory asset can be retrieved online, immediately transition to Stage 5.
248
+
249
+ ## Stage 5: Sandboxed Procedural Code Generation (Mode A — Ultimate Fallback)
250
+ When all previous stages are unavailable or fail, generate the media procedurally from scratch using \`execute_procedural_media\`:
251
+ - **Image / Art**: Write SVG vector graphics or HTML5 Canvas scripts.
252
+ - **Video / Animation**: Write HTML5 Canvas animation scripts that capture frames to video, or Python mathematical video scripts (\`matplotlib.animation\` / \`cv2\`). If frames need encoding and Blender is present, \`blender_act\` -> \`render_animation\` encodes them without any external binary; never conclude from a failed \`which ffmpeg\` that encoding is impossible.
253
+ - **Audio / Music**: Write mathematical Python scripts (\`numpy\` + \`scipy.io.wavfile\` or \`wave\`) generating custom waveforms, chiptune melodies, or sound effects.
254
+
255
+ ## CRITICAL MULTIMEDIA MANDATE:
256
+ Never stop or say "I cannot generate video/music" without traversing this entire 5-Stage hierarchy from Stage 1 down to Stage 5! Every stage denial (\`approved: false\`) is a signal to immediately trigger the next stage.
257
+ # BLENDER / 3D MANDATE — READ BEFORE ANY blender_act CALL
258
+
259
+ THE ONE RULE THAT DECIDES WHETHER THE RESULT IS GOOD:
260
+ **Use the smart actions. Do NOT write raw bpy Python for anything they cover.**
261
+ \`smartGeometry\`, \`smartMaterial\`, \`setupStudioLighting\`, \`cinematicAtmosphere\`
262
+ and \`autoFrameCamera\` already carry the craft — bevel widths, subdivision,
263
+ three-point key/fill/rim ratios, volumetric fog, a 65mm lens at a studio angle.
264
+ Hand-written bpy re-derives all of that badly and is why AI 3D looks like AI 3D:
265
+ default grey material, one flat lamp, camera at the origin. \`executePython\` is
266
+ for the gaps between these, never a replacement for them.
267
+
268
+ ## Order of operations — do not reorder, do not skip
269
+
270
+ 1. Verify the local Blender binary and use \`runHeadless\` for automation.
271
+ 2. \`clearScene\`. Blender starts with a cube, a lamp and a camera. Shipping the
272
+ default cube in frame is the single most common failure.
273
+ 3. Geometry via \`smartGeometry\`. Build the silhouette first and only then the
274
+ detail; a shape that does not read in a 64px thumbnail will not read at 4K.
275
+ 4. \`smartMaterial\` on EVERY visible object. Untouched objects render as default
276
+ grey plastic, and one grey object in an otherwise dressed scene is what makes
277
+ a render read as unfinished.
278
+ 5. \`setupStudioLighting\`, then \`cinematicAtmosphere\`. Lighting before atmosphere:
279
+ fog scatters the lights you have, so adding it first means tuning blind.
280
+ 6. \`autoFrameCamera\`. Never leave the camera where Blender put it.
281
+ 7. LOOK AT IT. Produce a single \`renderFrame\` at low samples, then
282
+ \`view_image\` or \`vision_inspect_ui\`. **A render you
283
+ have not looked at is not a finished render.** If it reads as flat, grey or
284
+ empty, fix it and look again — that loop is the whole difference.
285
+ 8. Only now \`renderAnimation\`.
286
+
287
+ ## Cinematic defaults that are not negotiable
288
+
289
+ - **Never one light.** \`setupStudioLighting\` gives key/fill/rim; a single lamp is
290
+ the signature of an unfinished scene.
291
+ - **Never the default camera angle.** Slightly above eye line, subject off-centre.
292
+ - **Motion must be motivated.** An 8-second shot is ONE move — a slow orbit, a
293
+ push-in, or a rise. Three moves in eight seconds reads as a screensaver.
294
+ - **Frame rate and length are arithmetic, so state them.** 8s at 24fps is frames
295
+ 1-192. Set the range explicitly; do not leave Blender's 250-frame default.
296
+ - **Render settings:** EEVEE for anything under a minute of work — Cycles at
297
+ default samples on a laptop is how a task burns its whole budget on one frame.
298
+
299
+ ## Never infer installation from PATH alone
300
+
301
+ Do NOT run \`which blender\`. It fails on macOS for every ordinary .app install
302
+ because nothing is symlinked onto PATH — Blender can be sitting in /Applications
303
+ and \`which\` will still say "command not found". Acting on that has already cost
304
+ a real user a multi-hundred-megabyte re-download of software they already had.
305
+
306
+ **The same trap applies to every other tool, and \`ffmpeg\` is the one you will
307
+ reach for next.** \`which ffmpeg\` failing does NOT mean this machine cannot
308
+ encode video. It usually means the encoder is bundled inside an application
309
+ rather than installed on PATH.
310
+
311
+ ## You already have an encoder: Blender is one
312
+
313
+ Blender links FFmpeg internally. If you can render frames, you can encode video
314
+ — with no external binary, no install, and no PATH lookup.
315
+
316
+ Use \`blender_act\` with the \`render_animation\` action and an \`.mp4\` target. It
317
+ sets \`image_settings.file_format = 'FFMPEG'\` with an H.264/MPEG4 container, and
318
+ it ALREADY falls back to a PNG sequence plus a probed external encoder if the
319
+ direct path fails. Hand-writing an encode script and shelling out to \`ffmpeg\`
320
+ re-solves a solved problem and, on a machine without it on PATH, fails where the
321
+ tool would have succeeded.
322
+
323
+ Check the exact API against the running build before writing Python by hand —
324
+ Blender renames render and sequencer attributes between major versions, and a
325
+ script written from memory for an older release will exit non-zero.
326
+
327
+ Only when the Blender tool's local executable check reports no valid binary is
328
+ Blender genuinely missing. Then say so plainly, offer the two options — install it, or produce the shot
329
+ with \`execute_procedural_media\` — and let the user choose. Never start a large
330
+ download on your own initiative.
331
+ `,
332
+
333
+ game: `# Sovereign Game Development Cascade & Enforcement Engine
334
+ When the user asks you to build, create, test, expand, or develop ANY game, you MUST strictly follow this mandatory 7-stage production methodology using specialized game tools. YOU MUST NOT SKIP STAGES.
335
+ **CRITICAL MANDATE**: You are building a **FULL PRODUCTION GAME**. It is completely unacceptable to finish a game in under 50-100 steps. A full game requires multiple levels, complex AI states, numerous sound effects, shaders, and rich tilemaps. You MUST iterate heavily and expand the scope to reach production scale.
336
+
337
+ 1. **Stage 1 (Engine Verification)**: Run \`check_system_specs\` (\`engine: 'godot'\`). If valid, use \`godot_act\` for project creation/validation.
338
+ 2. **Stage 2 (Narrative & Worldbuilding)**: Use \`game_lore_forge\`, \`game_dialogue_tree_gen\`, and \`game_quest_arc_builder\`. Expand this stage massively.
339
+ 3. **Stage 3 (Character Rigging & AI)**: Use \`game_animation_rig\`, \`game_ai_behavior_tree\`, and \`game_cinematic_sequencer\`. Create 5+ enemy types.
340
+ 4. **Stage 4 (Harvesting, Audio & VFX)**: Use \`game_asset_harvest\`, \`game_audio_synth\`, and \`game_vfx_emitter_gen\`.
341
+ 5. **Stage 5 (Shaders, Scenes & Spawners)**: Use \`game_shader_forge\`, \`game_scene_build\`, and \`game_entity_spawner\`. Design at least 3 sprawling levels.
342
+ 6. **Stage 6 (Maps, UI & Economy)**: Use \`game_tilemap_gen\`, \`game_ui_hud_builder\`, and \`game_economy_balancer\`.
343
+ 7. **Stage 7 (Assembly & Verification)**: If building a browser/WebGL game, invoke \`game_build_html5\`. **CRITICAL**: You MUST NOT inject 200+ lines of monolithic physics/logic inside \`game_build_html5.customScript\` until you have modularly completed Stages 1-6. After Assembly, you MUST execute \`game_web_runtime_inspector\` and \`game_canvas_audit\` to verify runtime integrity (FPS >= 30, no syntax errors, visual canvas rendering) before declaring Task completed.`
344
+ };
345
+
346
+ // ---------------------------------------------------------------------------
347
+ // Intent routing.
348
+ //
349
+ // The previous implementation used bare substring matching, which produced
350
+ // catastrophic false positives: 'ui' matched "build", "quick" and
351
+ // "requirements"; 'race' matched "trace". Those false positives triggered
352
+ // completion-blocking guards that forced dozens of extra LLM round trips on
353
+ // tasks that had nothing to do with games or web UI.
354
+ //
355
+ // Every matcher below is anchored on word boundaries and tested against the
356
+ // raw prompt only.
357
+ // ---------------------------------------------------------------------------
358
+
359
+ const GAME_INTENT = /\b(game|games|gamedev|permainan|godot|unity|unreal|rpg|platformer|roguelike|arcade|sprite|spritesheet|tilemap|voxel|minecraft|aetheria|voxellia|nes|snes|gameplay|player character|level design|boss fight)\b|\.(tscn|gd)\b/i;
360
+
361
+ const WEB_INTENT = /\b(web|webpage|website|situs|frontend|front-end|landing page|homepage|dashboard|portfolio|html|css|scss|tailwind|bootstrap|react|vue|svelte|angular|next\.?js|nuxt|astro|ui|ux|user interface|web app|webapp|spa|dom|responsive)\b/i;
362
+
363
+ const MEDIA_INTENT = /\b(image|images|picture|photo|gambar|render|3d model|blender|animation|animate|video|movie|film|klip|audio|sound|sfx|music|musik|song|lagu|voice|suara|tts|text.to.speech|narration|podcast|thumbnail|poster|illustration|logo)\b/i;
364
+
365
+ const SYSTEM_INTENT = /\b(process|processes|pid|kill|port|zip|unzip|tar|archive|compress|extract|clipboard|copy to clipboard|print|printer|cetak|open in browser|launch app)\b/i;
366
+
367
+ const ADVANCED_INTENT = /\b(pdf|forge a tool|custom tool|new tool|subagent|sub-agent|parallel agent|swarm|ast|symbols|refactor across)\b/i;
368
+
369
+ const INTENT_MATCHERS = {
370
+ game: GAME_INTENT,
371
+ web: WEB_INTENT,
372
+ media: MEDIA_INTENT,
373
+ system: SYSTEM_INTENT,
374
+ advanced: ADVANCED_INTENT
375
+ };
376
+
377
+ /** True when the prompt genuinely describes game development work. */
378
+ export function isGameIntent(prompt) {
379
+ return GAME_INTENT.test(prompt || '');
380
+ }
381
+
382
+ /** True when the prompt genuinely describes web/UI work. */
383
+ export function isWebIntent(prompt) {
384
+ return WEB_INTENT.test(prompt || '');
385
+ }
386
+
387
+ /**
388
+ * Decide which packs to unlock before the first LLM call, so the common case
389
+ * costs zero discovery round trips.
390
+ *
391
+ * @param {string} prompt The user's task text.
392
+ * @param {object} workspaceContext Optional perception output; its detected
393
+ * frameworks and files are strong signals.
394
+ * @returns {string[]} pack names to unlock
395
+ */
396
+ export function routePacks(prompt, workspaceContext = null) {
397
+ const unlocked = new Set();
398
+ const text = prompt || '';
399
+
400
+ for (const packName of PACK_NAMES) {
401
+ if (INTENT_MATCHERS[packName].test(text)) {
402
+ unlocked.add(packName);
403
+ }
404
+ }
405
+
406
+ // Workspace signals. A Godot project on disk is far more reliable evidence of
407
+ // game work than any keyword, and a detected web framework means the agent
408
+ // will eventually need to look at what it built.
409
+ if (workspaceContext) {
410
+ const signals = [
411
+ workspaceContext.framework,
412
+ workspaceContext.projectType,
413
+ workspaceContext.designSystem,
414
+ Array.isArray(workspaceContext.keyFiles) ? workspaceContext.keyFiles.join(' ') : ''
415
+ ]
416
+ .filter(Boolean)
417
+ .join(' ')
418
+ .toLowerCase();
419
+
420
+ if (/godot|project\.godot|unity|\.tscn/.test(signals)) unlocked.add('game');
421
+ if (/react|vue|svelte|next|nuxt|astro|angular|tailwind|vite|html/.test(signals)) unlocked.add('web');
422
+ }
423
+
424
+ return [...unlocked];
425
+ }
426
+
427
+ /**
428
+ * Resolve the tool schemas that should enter the context window.
429
+ * Core tools always load; pack tools load only once their pack is unlocked.
430
+ *
431
+ * Ordering is APPEND-ONLY and this matters a great deal for cost. Every
432
+ * provider's prompt cache binds on an identical prefix, and tool definitions
433
+ * sit at the very front of that prefix — so if unlocking a pack mid-task
434
+ * reshuffles the array, the cache is invalidated from the first changed byte
435
+ * onward, taking the system prompt and the whole message history with it.
436
+ * Sorting pack tools alphabetically did exactly that: unlocking `system`
437
+ * inserted `archive` ahead of `astryx_oracle` and truncated the stable prefix
438
+ * to 82%. Ordering by unlock sequence instead keeps 100% of the previously
439
+ * cached prefix intact and appends only the new tail.
440
+ *
441
+ * @param {Array} allDefinitions Full TOOL_DEFINITIONS list.
442
+ * @param {Set} unlockedPacks Pack names unlocked for this task.
443
+ * @param {Set} unlockedTools Individually unlocked tool names.
444
+ * @param {Array} unlockOrder Pack names in the order they were unlocked.
445
+ * @returns {Array} filtered definitions, deterministically ordered
446
+ */
447
+ export function resolveActiveTools(allDefinitions, unlockedPacks, unlockedTools, unlockOrder = null, options = {}) {
448
+ const packs = unlockedPacks || new Set();
449
+ const tools = unlockedTools || new Set();
450
+ // Providers with native schema deferral (Anthropic) leave this off — server
451
+ // side deferral is strictly better, since it costs no discovery round trip.
452
+ const residentOnly = options.residentOnly === true;
453
+
454
+ const active = allDefinitions.filter(def => {
455
+ if (isCoreTool(def.name)) {
456
+ if (!residentOnly) return true;
457
+ return isResidentTool(def.name) || tools.has(def.name);
458
+ }
459
+ if (tools.has(def.name)) return true;
460
+ const memberOf = packsForTool(def.name);
461
+ // A tool with no declared pack is a forged/custom tool: always available,
462
+ // because the user explicitly created it for this machine.
463
+ if (!memberOf) return true;
464
+ for (const pack of memberOf) {
465
+ if (packs.has(pack)) return true;
466
+ }
467
+ return false;
468
+ });
469
+
470
+ // The stable head of the array. Without residentOnly that is the whole core;
471
+ // with it, only the resident subset — and that distinction matters for the
472
+ // prompt cache, not just for token count.
473
+ //
474
+ // A core tool unlocked mid-task must NOT be slotted back into its CORE_TOOLS
475
+ // position: that inserts into the middle of the array and invalidates the
476
+ // cached prefix from that point on, every time. It is appended to the tail
477
+ // below instead, so the head stays byte-identical for the whole task.
478
+ const isHead = (name) => !residentOnly || isResidentTool(name);
479
+ const coreFirst = active.filter(d => isCoreTool(d.name) && isHead(d.name));
480
+ coreFirst.sort((a, b) => CORE_TOOLS.indexOf(a.name) - CORE_TOOLS.indexOf(b.name));
481
+
482
+ // Pack tools grouped by unlock sequence, alphabetical only within a group so
483
+ // the tail added by any single unlock is itself deterministic.
484
+ const sequence = Array.isArray(unlockOrder) && unlockOrder.length > 0
485
+ ? unlockOrder
486
+ : [...packs];
487
+ const rankOfPack = new Map(sequence.map((name, index) => [name, index]));
488
+
489
+ const rankOf = (def) => {
490
+ // A non-resident core tool the model discovered via tool_search. Ranked
491
+ // ahead of every pack so the tail stays grouped, but behind the fixed head.
492
+ if (isCoreTool(def.name)) return -1;
493
+ const memberOf = packsForTool(def.name);
494
+ if (!memberOf) return Number.MAX_SAFE_INTEGER; // forged tools last
495
+ let best = Number.MAX_SAFE_INTEGER;
496
+ for (const pack of memberOf) {
497
+ const rank = rankOfPack.get(pack);
498
+ if (rank !== undefined && rank < best) best = rank;
499
+ }
500
+ return best;
501
+ };
502
+
503
+ const rest = active.filter(d => !isCoreTool(d.name) || !isHead(d.name));
504
+ rest.sort((a, b) => {
505
+ const diff = rankOf(a) - rankOf(b);
506
+ return diff !== 0 ? diff : a.name.localeCompare(b.name);
507
+ });
508
+
509
+ return [...coreFirst, ...rest];
510
+ }
511
+
512
+ /** Human-readable catalogue injected into the system prompt so the model knows what it can unlock. */
513
+ export function getPackCatalogue(unlockedPacks) {
514
+ const packs = unlockedPacks || new Set();
515
+ const lines = PACK_NAMES.map(name => {
516
+ const state = packs.has(name) ? 'LOADED' : 'available';
517
+ return `- ${name} (${PACKS[name].length} tools, ${state})`;
518
+ });
519
+
520
+ return `# EXTENDED CAPABILITY PACKS
521
+ Beyond the tools currently in your context you have access to specialised capability packs. Nothing is disabled — packs load on demand to keep your context focused.
522
+ ${lines.join('\n')}
523
+ To load a pack, call \`tool_search\` with a query describing what you need (for example "godot scene", "generate music", "kill process on port"). The matching tool schemas become available immediately on your next turn. Never claim a capability is missing without searching first.`;
524
+ }