@causyne/agent 0.0.0-stage → 1.10.1

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,437 @@
1
+ import fs from 'fs/promises';
2
+ import fsSync from 'fs';
3
+ import path from 'path';
4
+ import os from 'os';
5
+ import { execFile } from 'child_process';
6
+ import { promisify } from 'util';
7
+
8
+
9
+ const execFileAsync = promisify(execFile);
10
+
11
+ // Helper: Ensure workspace-local buffer directory exists
12
+ function getWorkspaceTempDir() {
13
+ const tmpDir = path.join(process.cwd(), '.tmp_causyne_3d');
14
+ if (!fsSync.existsSync(tmpDir)) {
15
+ fsSync.mkdirSync(tmpDir, { recursive: true });
16
+ }
17
+ return tmpDir;
18
+ }
19
+
20
+ // Helper: Locate Godot 4.x binary across multi-OS paths including sovereign ~/.causyne/engines/
21
+ function getGodotBinary() {
22
+ const isWin = process.platform === 'win32';
23
+ const isMac = process.platform === 'darwin';
24
+ const homeDir = os.homedir();
25
+ const causyneGodotDir = path.join(homeDir, '.causyne', 'engines', 'godot');
26
+
27
+ const candidates = [];
28
+ if (isMac) {
29
+ candidates.push(
30
+ path.join(causyneGodotDir, 'godot'),
31
+ '/Applications/Godot.app/Contents/MacOS/Godot',
32
+ '/Applications/Godot_4.app/Contents/MacOS/Godot',
33
+ '/opt/homebrew/bin/godot',
34
+ '/usr/local/bin/godot'
35
+ );
36
+ } else if (isWin) {
37
+ candidates.push(
38
+ path.join(causyneGodotDir, 'Godot.exe'),
39
+ path.join(causyneGodotDir, 'godot.exe'),
40
+ 'C:\\Program Files\\Godot\\Godot_v4.exe',
41
+ 'C:\\Program Files\\Godot\\godot.exe',
42
+ 'godot.exe'
43
+ );
44
+ } else {
45
+ candidates.push(
46
+ path.join(causyneGodotDir, 'godot'),
47
+ '/usr/bin/godot',
48
+ '/usr/local/bin/godot',
49
+ 'godot'
50
+ );
51
+ }
52
+
53
+ for (const bin of candidates) {
54
+ try {
55
+ if (fsSync.existsSync(bin)) return bin;
56
+ } catch (e) {}
57
+ }
58
+ return null;
59
+ }
60
+
61
+ // Helper: Send JSON-RPC over WebSocket to Godot bridge (Layer 1)
62
+
63
+
64
+ export const godotAutomator = {
65
+ schema: {
66
+ name: 'godot.act',
67
+ description: 'Universal Sovereign Godot 4.x Game Engine Automation with Sovereign Fallback Cascade. Scaffolds projects (`create_project`), validates GDScript syntax (`validate_project`), executes runtime playtest matrices (`test_scene`), executes CLI scripts (`headless_run`), builds export packages (`export_project`), and cascades to HTML5 fallbacks if binary is missing.',
68
+ parameters: {
69
+ type: 'object',
70
+ properties: {
71
+ action: {
72
+ type: 'string',
73
+ enum: ['create_project', 'validate_project', 'check_syntax', 'test_scene', 'headless_run', 'export_project', 'check_status'],
74
+ description: 'The execution layer: create_project (scaffold folder structure & project.godot), validate_project/check_syntax (GDScript AST linter), test_scene (headless runtime assertion), headless_run (CLI GDScript), export_project (headless build), or check_status (binary check).'
75
+ },
76
+ simulateMissing: {
77
+ type: 'boolean',
78
+ description: 'Simulate missing binary to verify sovereign fallback cascade.'
79
+ },
80
+ projectDir: {
81
+ type: 'string',
82
+ description: 'Absolute path to Godot project directory containing project.godot.'
83
+ },
84
+ projectName: {
85
+ type: 'string',
86
+ description: 'Name of the Godot project for create_project.'
87
+ },
88
+ gameType: {
89
+ type: 'string',
90
+ enum: ['2d', '3d', 'rpg'],
91
+ description: 'Type of project template to initialize for create_project (`2d`, `3d`, or `rpg`).'
92
+ },
93
+ targetFile: {
94
+ type: 'string',
95
+ description: 'Specific script or scene file path (`res://...` or absolute) to validate syntax for validate_project.'
96
+ },
97
+ scenePath: {
98
+ type: 'string',
99
+ description: 'Scene file path (`res://scenes/main.tscn` or absolute) to test for test_scene.'
100
+ },
101
+ durationFrames: {
102
+ type: 'number',
103
+ description: 'Number of simulation frames for test_scene before auto-quitting (default 300 frames @ 60fps = 5 seconds).'
104
+ },
105
+ scriptPath: {
106
+ type: 'string',
107
+ description: 'Path or raw GDScript code for headless_run.'
108
+ },
109
+ exportPreset: {
110
+ type: 'string',
111
+ description: 'Export preset name (`Android`, `HTML5`, `Windows Desktop`, `macOS`).'
112
+ },
113
+ outputPath: {
114
+ type: 'string',
115
+ description: 'Absolute path for output binary or package.'
116
+ }
117
+ },
118
+ required: ['action']
119
+ }
120
+ },
121
+
122
+ async execute(args) {
123
+ try {
124
+ const { action, simulateMissing, projectDir, projectName, gameType = '2d', targetFile, scenePath, durationFrames = 300, scriptPath, exportPreset, outputPath } = args;
125
+
126
+ // Action 1: Status & Binary Audit with Sovereign Fallback Cascade
127
+ if (action === 'check_status') {
128
+ const bin = simulateMissing ? null : getGodotBinary();
129
+ return { success: true, output: JSON.stringify({
130
+ godot_binary: bin || 'Not installed / simulateMissing=true',
131
+ status: bin ? 'available_for_local_cli' : 'fallback_required',
132
+ workspace_temp_dir: getWorkspaceTempDir()
133
+ }, null, 2) };
134
+ }
135
+
136
+ // Action 2: Create Project (Scaffold folder structure & project.godot)
137
+ if (action === 'create_project') {
138
+ const finalDir = projectDir || path.join(getWorkspaceTempDir(), projectName || `SovereignProject_${Date.now()}`);
139
+ await fs.mkdir(finalDir, { recursive: true });
140
+
141
+ const subdirs = [
142
+ 'scenes/ui',
143
+ 'scenes/actors',
144
+ 'scenes/world',
145
+ 'scenes/levels',
146
+ 'scripts/core',
147
+ 'scripts/player',
148
+ 'scripts/ai',
149
+ 'data',
150
+ 'audio/sfx',
151
+ 'assets/3d',
152
+ 'assets/2d'
153
+ ];
154
+
155
+ for (const sd of subdirs) {
156
+ await fs.mkdir(path.join(finalDir, sd), { recursive: true });
157
+ }
158
+
159
+ const projectGodotPath = path.join(finalDir, 'project.godot');
160
+ const projectGodotContent = `; Engine configuration file.
161
+ ; Generated by Sovereign Game Automation Studio (v1.3.12)
162
+
163
+ config_version=5
164
+
165
+ [application]
166
+
167
+ config/name="${projectName || 'Sovereign Game Project'}"
168
+ config/features=PackedStringArray("4.3", "Forward Plus")
169
+ run/main_scene="res://scenes/main.tscn"
170
+
171
+ [display]
172
+
173
+ window/size/viewport_width=1280
174
+ window/size/viewport_height=720
175
+ window/stretch/mode="canvas_items"
176
+
177
+ [physics]
178
+
179
+ 2d/run_on_separate_thread=true
180
+ 3d/run_on_separate_thread=true
181
+ `;
182
+ await fs.writeFile(projectGodotPath, projectGodotContent, 'utf8');
183
+
184
+ // Starter scene and script
185
+ const mainGdPath = path.join(finalDir, 'scripts', 'main.gd');
186
+ const mainGdContent = `extends Node
187
+
188
+ func _ready() -> void:
189
+ \tprint("[Sovereign Engine] Project Ready: ${projectName || 'Sovereign Game Project'} (${gameType.toUpperCase()})")
190
+ `;
191
+ await fs.writeFile(mainGdPath, mainGdContent, 'utf8');
192
+
193
+ const nodeType = gameType === '3d' ? 'Node3D' : (gameType === 'rpg' ? 'Node2D' : 'Node2D');
194
+ const mainTscnPath = path.join(finalDir, 'scenes', 'main.tscn');
195
+ const mainTscnContent = `[gd_scene load_steps=2 format=3]
196
+
197
+ [ext_resource type="Script" path="res://scripts/main.gd" id="1_main"]
198
+
199
+ [node name="Main" type="${nodeType}"]
200
+ script = ExtResource("1_main")
201
+ `;
202
+ await fs.writeFile(mainTscnPath, mainTscnContent, 'utf8');
203
+
204
+ return {
205
+ success: true,
206
+ output: JSON.stringify({
207
+ action: 'create_project',
208
+ project_dir: finalDir,
209
+ project_name: projectName || 'Sovereign Game Project',
210
+ game_type: gameType,
211
+ created_files: [
212
+ 'project.godot',
213
+ 'scripts/main.gd',
214
+ 'scenes/main.tscn'
215
+ ],
216
+ created_directories: subdirs,
217
+ status: 'READY_FOR_ASSEMBLY'
218
+ }, null, 2)
219
+ };
220
+ }
221
+
222
+ // Action 3: Validate Project / Check Syntax (Headless GDScript AST check)
223
+ if (action === 'validate_project' || action === 'check_syntax') {
224
+ const bin = simulateMissing ? null : getGodotBinary();
225
+ if (!bin) {
226
+ return {
227
+ success: false,
228
+ status: 'fallback_required',
229
+ error: '[ENGINE MISSING or SIMULATED] Godot binary not found for syntax validation.',
230
+ fallback_guidance: {
231
+ auto_install_option: 'Call manage_local_dependencies (action="check_and_setupEng", engine="godot") to install (~135 MB compressed).',
232
+ instant_execution_option: 'Or verify code using built-in code_verify_syntax tool and build via game.build_html5.'
233
+ }
234
+ };
235
+ }
236
+ if (!projectDir || !fsSync.existsSync(projectDir)) {
237
+ return { success: false, error: `Valid projectDir is required for syntax validation. Found: ${projectDir}` };
238
+ }
239
+
240
+ const cliArgs = ['--headless', '--check-only', '--path', projectDir];
241
+ if (targetFile) {
242
+ cliArgs.push(targetFile);
243
+ }
244
+
245
+ let stdout = '';
246
+ let stderr = '';
247
+ let exitCode = 0;
248
+ try {
249
+ const res = await execFileAsync(bin, cliArgs, { maxBuffer: 15 * 1024 * 1024 });
250
+ stdout = res.stdout;
251
+ stderr = res.stderr;
252
+ } catch (execErr) {
253
+ exitCode = execErr.code || 1;
254
+ stdout = execErr.stdout || '';
255
+ stderr = execErr.stderr || execErr.message || '';
256
+ }
257
+
258
+ // Parse diagnostic lines
259
+ const combined = `${stdout}\n${stderr}`;
260
+ const lines = combined.split('\n');
261
+ const diagnostics = [];
262
+ const errorRegex = /(?:SCRIPT ERROR|ERROR|PARSE ERROR|Line (\d+)): (.*)/i;
263
+ const fileRegex = /at: (.*) \((?:line |Line )?(\d+)\)/i;
264
+
265
+ for (const line of lines) {
266
+ if (line.includes('ERROR') || line.includes('SCRIPT ERROR') || line.includes('Parse Error')) {
267
+ const matchErr = line.match(errorRegex);
268
+ const matchFile = line.match(fileRegex);
269
+ diagnostics.push({
270
+ file: matchFile ? matchFile[1] : (targetFile || 'project'),
271
+ line: matchFile ? parseInt(matchFile[2], 10) : (matchErr && matchErr[1] ? parseInt(matchErr[1], 10) : 1),
272
+ error: matchErr ? matchErr[2] : line.trim()
273
+ });
274
+ }
275
+ }
276
+
277
+ const passed = exitCode === 0 && diagnostics.length === 0;
278
+ return {
279
+ success: true,
280
+ output: JSON.stringify({
281
+ action: 'validate_project',
282
+ project_dir: projectDir,
283
+ target_file: targetFile || 'entire_project',
284
+ status: passed ? 'PASSED' : 'SYNTAX_ERRORS_FOUND',
285
+ exit_code: exitCode,
286
+ error_count: diagnostics.length,
287
+ diagnostics: diagnostics,
288
+ raw_output: (combined.trim().slice(0, 2000))
289
+ }, null, 2)
290
+ };
291
+ }
292
+
293
+ // Action 4: Test Scene (Headless runtime assertion & playtest simulation matrix)
294
+ if (action === 'test_scene') {
295
+ const bin = simulateMissing ? null : getGodotBinary();
296
+ if (!bin) {
297
+ return {
298
+ success: false,
299
+ status: 'fallback_required',
300
+ error: '[ENGINE MISSING or SIMULATED] Godot binary not found for headless playtest.',
301
+ fallback_guidance: {
302
+ auto_install_option: 'Call manage_local_dependencies (action="check_and_setupEng", engine="godot") to install (~135 MB compressed).',
303
+ instant_execution_option: 'Or invoke game.build_html5 to playtest instantly in WebGL / Canvas 2D.'
304
+ }
305
+ };
306
+ }
307
+ if (!projectDir || !fsSync.existsSync(projectDir)) {
308
+ return { success: false, error: `Valid projectDir is required for testing. Found: ${projectDir}` };
309
+ }
310
+
311
+ const targetScene = scenePath || 'res://scenes/main.tscn';
312
+ const cliArgs = ['--headless', '--path', projectDir, targetScene, '--quit-after', String(durationFrames)];
313
+
314
+ let stdout = '';
315
+ let stderr = '';
316
+ let exitCode = 0;
317
+ const startTime = Date.now();
318
+ try {
319
+ const res = await execFileAsync(bin, cliArgs, { maxBuffer: 20 * 1024 * 1024 });
320
+ stdout = res.stdout;
321
+ stderr = res.stderr;
322
+ } catch (execErr) {
323
+ exitCode = execErr.code || 1;
324
+ stdout = execErr.stdout || '';
325
+ stderr = execErr.stderr || execErr.message || '';
326
+ }
327
+ const durationMs = Date.now() - startTime;
328
+
329
+ const combined = `${stdout}\n${stderr}`;
330
+ const hasFatalError = combined.includes('FATAL') || combined.includes('SCRIPT ERROR') || combined.includes('CRITICAL') || combined.includes('Null instance') || exitCode !== 0;
331
+
332
+ return {
333
+ success: true,
334
+ output: JSON.stringify({
335
+ action: 'test_scene',
336
+ project_dir: projectDir,
337
+ scene_tested: targetScene,
338
+ simulated_frames: durationFrames,
339
+ execution_time_ms: durationMs,
340
+ status: !hasFatalError ? 'PASSED_ZERO_CRASHES' : 'RUNTIME_ERRORS_DETECTED',
341
+ exit_code: exitCode,
342
+ logs_sample: combined.trim().split('\n').slice(-30)
343
+ }, null, 2)
344
+ };
345
+ }
346
+
347
+ // Action 5: Socket RPC Bridge / Playtest Simulation
348
+
349
+
350
+ // Action 6: Headless CLI Run with Sovereign Fallback Cascade
351
+ if (action === 'headless_run') {
352
+ const bin = simulateMissing ? null : getGodotBinary();
353
+ if (!bin) {
354
+ return {
355
+ success: false,
356
+ status: 'fallback_required',
357
+ error: '[ENGINE MISSING or SIMULATED] Godot binary not found locally.',
358
+ fallback_guidance: {
359
+ auto_install_option: 'Call manage_local_dependencies (action="check_and_setupEng", engine="godot") to auto-install (~135 MB compressed).',
360
+ instant_execution_option: 'Or immediately invoke game.build_html5 (Three.js WebGL / Canvas 2D + SovereignAudioEngine) + game.asset_fetch to fulfill game request without Godot.'
361
+ }
362
+ };
363
+ }
364
+
365
+ let targetScript = scriptPath;
366
+ if (scriptPath && (scriptPath.includes('func ') || scriptPath.includes('extends '))) {
367
+ const tempScriptFile = path.join(getWorkspaceTempDir(), `auto_run_${Date.now()}.gd`);
368
+ await fs.writeFile(tempScriptFile, scriptPath, 'utf8');
369
+ targetScript = tempScriptFile;
370
+ }
371
+
372
+ const cliArgs = ['--headless'];
373
+ if (projectDir && fsSync.existsSync(projectDir)) {
374
+ cliArgs.push('--path', projectDir);
375
+ }
376
+ if (targetScript) {
377
+ cliArgs.push('-s', targetScript);
378
+ } else {
379
+ return { success: false, error: 'scriptPath (or raw GDScript string) is required for headless_run.' };
380
+ }
381
+
382
+ const { stdout, stderr } = await execFileAsync(bin, cliArgs, { maxBuffer: 15 * 1024 * 1024 });
383
+ return {
384
+ success: true,
385
+ output: JSON.stringify({
386
+ layer: 'Headless CLI Execution',
387
+ binary: bin,
388
+ script: targetScript,
389
+ stdout: stdout.trim(),
390
+ stderr: stderr.trim()
391
+ }, null, 2)
392
+ };
393
+ }
394
+
395
+ // Action 7: Project Export with Sovereign Fallback Cascade
396
+ if (action === 'export_project') {
397
+ const bin = simulateMissing ? null : getGodotBinary();
398
+ if (!bin) {
399
+ return {
400
+ success: false,
401
+ status: 'fallback_required',
402
+ error: '[ENGINE MISSING or SIMULATED] Godot binary not found for project export.',
403
+ fallback_guidance: {
404
+ auto_install_option: 'Call manage_local_dependencies (action="check_and_setupEng", engine="godot") to auto-install (~135 MB compressed).',
405
+ instant_execution_option: 'Or immediately invoke game.build_html5 (Three.js WebGL / Canvas 2D + SovereignAudioEngine) + game.asset_fetch to fulfill game request without Godot.'
406
+ }
407
+ };
408
+ }
409
+ if (!projectDir || !fsSync.existsSync(projectDir)) {
410
+ return { success: false, error: `Valid projectDir containing project.godot is required. Found: ${projectDir}` };
411
+ }
412
+
413
+ const preset = exportPreset || 'HTML5';
414
+ const finalOutput = outputPath || path.join(getWorkspaceTempDir(), `export_${preset.toLowerCase()}_${Date.now()}.${preset === 'Android' ? 'apk' : 'html'}`);
415
+
416
+ const cliArgs = ['--headless', '--path', projectDir, '--export-release', preset, finalOutput];
417
+ const { stdout, stderr } = await execFileAsync(bin, cliArgs, { maxBuffer: 20 * 1024 * 1024 });
418
+
419
+ return {
420
+ success: true,
421
+ output: JSON.stringify({
422
+ layer: 'Headless Export Package',
423
+ preset: preset,
424
+ output_file: finalOutput,
425
+ file_exists: fsSync.existsSync(finalOutput),
426
+ size_bytes: fsSync.existsSync(finalOutput) ? (await fs.stat(finalOutput)).size : 0,
427
+ stdout: stdout.trim()
428
+ }, null, 2)
429
+ };
430
+ }
431
+
432
+ return { success: false, error: `Unknown action: ${action}` };
433
+ } catch (e) {
434
+ return { success: false, error: e.message };
435
+ }
436
+ }
437
+ };
@@ -0,0 +1,220 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { execFile } from 'child_process';
4
+ import util from 'util';
5
+ import os from 'os';
6
+
7
+ const execFilePromise = util.promisify(execFile);
8
+
9
+ // Runtime package installation is intentionally narrow. Accept only an exact
10
+ // PyPI package pin, never an option, URL, VCS reference or local path. argv is
11
+ // then passed directly to pip without a shell.
12
+ const PYPI_PIN = /^[A-Za-z0-9][A-Za-z0-9._-]*(?:\[[A-Za-z0-9_,.-]+\])?==[A-Za-z0-9][A-Za-z0-9._+!-]*$/;
13
+
14
+ export function isExactPyPiPin(value) {
15
+ return typeof value === 'string' && value.length <= 200 && PYPI_PIN.test(value);
16
+ }
17
+
18
+ // Phase 9.3: Check installed engine paths
19
+ async function findInstalledEngine(engine) {
20
+ const homeDir = os.homedir();
21
+ const causyneEngineDir = path.join(homeDir, '.causyne', 'engines', engine);
22
+ const isWindows = process.platform === 'win32';
23
+
24
+ const possiblePaths = engine === 'godot' ? [
25
+ path.join(causyneEngineDir, isWindows ? 'Godot.exe' : 'godot'),
26
+ '/Applications/Godot.app/Contents/MacOS/Godot',
27
+ isWindows ? 'C:\\Program Files\\Godot\\Godot.exe' : '/usr/bin/godot',
28
+ '/usr/local/bin/godot'
29
+ ] : [
30
+ path.join(causyneEngineDir, isWindows ? 'blender.exe' : 'blender'),
31
+ '/Applications/Blender.app/Contents/MacOS/Blender',
32
+ isWindows ? 'C:\\Program Files\\Blender Foundation\\Blender\\blender.exe' : '/usr/bin/blender',
33
+ '/usr/local/bin/blender'
34
+ ];
35
+
36
+ for (const p of possiblePaths) {
37
+ if (fs.existsSync(p)) return p;
38
+ }
39
+
40
+ try {
41
+ const locator = isWindows ? 'where.exe' : 'which';
42
+ const { stdout } = await execFilePromise(locator, [engine], { timeout: 5000 });
43
+ const trimmed = stdout.trim().split('\n')[0];
44
+ if (trimmed && fs.existsSync(trimmed)) return trimmed;
45
+ } catch (e) {}
46
+
47
+ return null;
48
+ }
49
+
50
+ export const manageLocalDependencies = {
51
+ schema: {
52
+ name: 'manage_local_dependencies',
53
+ description: 'Optional local capability manager. It can create a workspace Python environment, install explicitly pinned PyPI packages after approval, check for existing Godot/Blender installations, and grant reviewed capability groups.',
54
+ parameters: {
55
+ type: 'object',
56
+ properties: {
57
+ action: { type: 'string', enum: ['setup_venv', 'pipInst', 'check_and_setupEng', 'grant_capability', 'list_capabilities', 'grant_mcp_server', 'list_mcp_servers'] },
58
+ group: { type: 'string', description: 'Capability group for grant_capability: vision (browser + screen capture), ocr (read text from images), system (process and load inspection), desktop (clipboard, open, recycle bin), archive (zip and unzip).' },
59
+ engine: { type: 'string', enum: ['godot', 'blender'], description: 'Engine to locate when action is check_and_setupEng' },
60
+ simulateMissing: { type: 'boolean', description: 'Simulate missing binary to verify sovereign fallback cascade' },
61
+ packages: { type: 'array', items: { type: 'string' }, description: 'Exact PyPI pins only, for example pillow==11.3.0. URLs, paths, VCS references and pip options are rejected.' }
62
+ },
63
+ required: ['action']
64
+ }
65
+ },
66
+
67
+ execute: async (args, sandbox) => {
68
+ try {
69
+ const { action, engine, simulateMissing, packages, group } = args;
70
+
71
+
72
+ if (action === 'list_capabilities') {
73
+ const { GROUP_NAMES, GROUP_DESCRIPTIONS, groupStatus } = await import('../provisioning/capabilityProvisioner.js');
74
+ return {
75
+ success: true,
76
+ output: JSON.stringify(GROUP_NAMES.map(name => ({
77
+ group: name,
78
+ grants: GROUP_DESCRIPTIONS[name],
79
+ status: groupStatus(name).state
80
+ })), null, 2)
81
+ };
82
+ }
83
+
84
+ // ── MCP servers ──
85
+ //
86
+ // The server list comes from the user's config and from nowhere else.
87
+ // This action can only ENABLE a server that is already configured; it
88
+ // cannot define one, so no model output ever becomes a process launch.
89
+ if (action === 'list_mcp_servers') {
90
+ const { serverStatus } = await import('../mcp/mcpRegistry.js');
91
+ const servers = serverStatus();
92
+ return {
93
+ success: true,
94
+ output: servers.length === 0
95
+ ? 'No MCP servers are configured. The user adds them to the "mcpServers" array in ~/.causyne/config.json.'
96
+ : JSON.stringify(servers, null, 2)
97
+ };
98
+ }
99
+
100
+ if (action === 'grant_mcp_server') {
101
+ const { configuredServers, grantServer, connectServer } = await import('../mcp/mcpRegistry.js');
102
+ const target = group;
103
+ const known = configuredServers().map(s => s.name);
104
+ if (!target || !known.includes(target)) {
105
+ return {
106
+ success: false,
107
+ error: known.length === 0
108
+ ? 'No MCP servers are configured. Only the user can add one, in ~/.causyne/config.json.'
109
+ : `Unknown MCP server "${target}". Configured servers: ${known.join(', ')}.`
110
+ };
111
+ }
112
+ grantServer(target);
113
+ try {
114
+ const client = await connectServer(target);
115
+ return {
116
+ success: true,
117
+ output: `MCP server "${target}" is enabled and exposed ${client.tools.length} tools: ` +
118
+ client.tools.map(t => t.name).join(', ')
119
+ };
120
+ } catch (e) {
121
+ return { success: false, error: `Enabled "${target}" but could not connect: ${e.message}` };
122
+ }
123
+ }
124
+
125
+ if (action === 'grant_capability') {
126
+ const { GROUP_NAMES, GROUP_DESCRIPTIONS, grantGroup, provisionCapabilities, isGroupInstalled } =
127
+ await import('../provisioning/capabilityProvisioner.js');
128
+ if (!group || !GROUP_NAMES.includes(group)) {
129
+ return { success: false, error: `Unknown capability group. Choose one of: ${GROUP_NAMES.join(', ')}.` };
130
+ }
131
+ grantGroup(group);
132
+ try {
133
+ await provisionCapabilities([group]);
134
+ } catch (e) {
135
+ return { success: false, error: `Could not prepare the "${group}" capability: ${e.message}` };
136
+ }
137
+ return isGroupInstalled(group)
138
+ ? { success: true, output: `Capability "${group}" is ready — the agent can now ${GROUP_DESCRIPTIONS[group]}.` }
139
+ : { success: false, error: `The "${group}" capability could not be prepared in this environment.` };
140
+ }
141
+ const workspaceDir = process.cwd();
142
+ const aiToolsDir = path.join(workspaceDir, '.ai_tools');
143
+
144
+ // Engine setup is detection-only. Earlier code wrote a placeholder file
145
+ // and reported it as a verified installation; that was both inaccurate
146
+ // and an unnecessary executable-provisioning surface.
147
+ if (action === 'check_and_setupEng') {
148
+ if (!engine) return { success: false, error: 'Missing parameter: engine must be godot or blender' };
149
+
150
+ if (!simulateMissing) {
151
+ const existingPath = await findInstalledEngine(engine);
152
+ if (existingPath) {
153
+ return {
154
+ success: true,
155
+ status: 'installed',
156
+ engine: engine,
157
+ path: existingPath,
158
+ output: `Sovereign Engine check passed: ${engine} is already installed and ready at ${existingPath}`
159
+ };
160
+ }
161
+ }
162
+
163
+ return {
164
+ success: false,
165
+ status: 'not_installed',
166
+ engine,
167
+ error: `${engine} was not found. Causyne does not download or install engine binaries. Install it from the vendor's official distribution, verify it, and run this check again.`
168
+ };
169
+ }
170
+
171
+ if (action === 'setup_venv') {
172
+ if (!fs.existsSync(aiToolsDir)) fs.mkdirSync(aiToolsDir, { recursive: true });
173
+ const venvPath = path.join(aiToolsDir, 'venv');
174
+ if (!fs.existsSync(venvPath)) {
175
+ const python = process.platform === 'win32' ? 'python.exe' : 'python3';
176
+ await execFilePromise(python, ['-m', 'venv', venvPath], { timeout: 120000 });
177
+ return { success: true, output: `Created Python virtual environment at ${venvPath}` };
178
+ }
179
+ return { success: true, output: 'Virtual environment already exists.' };
180
+ }
181
+
182
+ else if (action === 'pipInst') {
183
+ if (!packages || packages.length === 0) return { success: false, error: 'No packages provided' };
184
+ if (!Array.isArray(packages) || packages.length > 32 || !packages.every(isExactPyPiPin)) {
185
+ return { success: false, error: 'Every Python dependency must be an exact PyPI pin such as pillow==11.3.0. Options, URLs, VCS references, local paths and unpinned requirements are blocked.' };
186
+ }
187
+
188
+ const isWindows = process.platform === 'win32';
189
+ const pipCmd = isWindows ? path.join(aiToolsDir, 'venv', 'Scripts', 'pip.exe') : path.join(aiToolsDir, 'venv', 'bin', 'pip');
190
+
191
+ if (!fs.existsSync(pipCmd)) return { success: false, error: 'Virtual environment not found. Run setup_venv first.' };
192
+
193
+ try {
194
+ const { stdout } = await execFilePromise(
195
+ pipCmd,
196
+ ['install', '--disable-pip-version-check', '--no-input', ...packages],
197
+ { timeout: 10 * 60 * 1000, maxBuffer: 10 * 1024 * 1024 }
198
+ );
199
+ return { success: true, output: `Installed approved Python dependencies: ${packages.join(', ')}`, logs: stdout };
200
+ } catch (pipErr) {
201
+ const errStr = pipErr.message || pipErr.stderr || "";
202
+ if (errStr.includes("Microsoft Visual C++ 14.0 or greater is required")) {
203
+ return { success: false, error: `[AUTO-REMEDIATION REQUIRED] Missing C++ Build Tools. Ask user for permission to install MSVC Build Tools.` };
204
+ }
205
+ if (errStr.includes("No matching distribution found")) {
206
+ return { success: false, error: `[AUTO-REMEDIATION REQUIRED] Package not found. Check Python version compatibility or PyPI spelling.` };
207
+ }
208
+ return { success: false, error: `PIP Install Failed: ${errStr}` };
209
+ }
210
+ }
211
+
212
+ return { success: false, error: `Unknown action: ${action}` };
213
+ } catch (err) {
214
+ return { success: false, error: err.message };
215
+ }
216
+ }
217
+ };
218
+
219
+ // Backward compatibility export
220
+ export const manageLocalAiDependencies = manageLocalDependencies;