@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,99 @@
1
+ import { getMemory, saveMemory } from '../memory.js';
2
+ import { cortexDb } from '../cortex/vectorDb.js';
3
+
4
+ // Persistent memory write.
5
+ //
6
+ // This tool used to append facts to `{cwd}/.causyne_memory_store.json` — a file
7
+ // that nothing in the codebase ever reads. The facts the agent is actually shown
8
+ // come from `getMemoryContextString()`, which reads `~/.causyne/memory.json`, and
9
+
10
+ // never connected, so every fact the agent chose to remember was written into a
11
+ // per-project dead end and silently lost. It now writes where the agent reads.
12
+
13
+ const MAX_FACTS = 100;
14
+
15
+ export const memoryWrite = {
16
+ schema: {
17
+ name: 'memory_write',
18
+ description: "Save a durable fact about the user, their machine, or their project into persistent memory. Facts saved here are injected into your system context on every future task, so record only things worth remembering across sessions (preferences, conventions, paths, stack choices). After completing a web or UI design, also pass creativePattern so you do not repeat the same aesthetic next time.",
19
+ parameters: {
20
+ type: 'object',
21
+ properties: {
22
+ fact: {
23
+ type: 'string',
24
+ description: 'The concise, durable fact to remember. One fact per call.'
25
+ },
26
+ creativePattern: {
27
+ type: 'object',
28
+ description: 'Optional. Record a completed design so future projects deliberately differ from it.',
29
+ properties: {
30
+ project: { type: 'string', description: 'What was built, e.g. "coffee roaster landing page".' },
31
+ designApproach: { type: 'string', description: 'The design movement committed to, e.g. "Swiss International", "Brutalism".' },
32
+ colorPalette: { type: 'string', description: 'The palette used, e.g. "warm neutrals + oxblood accent".' },
33
+ fonts: { type: 'string', description: 'Typefaces used, e.g. "Space Grotesk / Inter".' }
34
+ }
35
+ }
36
+ },
37
+ required: ['fact']
38
+ }
39
+ },
40
+
41
+ execute: async (args, dependencies) => {
42
+ if (!args || !args.fact) {
43
+ return { success: false, error: "Missing 'fact' parameter." };
44
+ }
45
+
46
+ try {
47
+ const memory = getMemory();
48
+ if (!Array.isArray(memory.facts)) memory.facts = [];
49
+
50
+ const fact = String(args.fact).trim();
51
+ const alreadyKnown = memory.facts.includes(fact);
52
+
53
+ if (!alreadyKnown) {
54
+ memory.facts.push(fact);
55
+ // Self-culling: keep the store bounded so it cannot grow into the
56
+ // system prompt without limit.
57
+ while (memory.facts.length > MAX_FACTS) {
58
+ memory.facts.shift();
59
+ }
60
+ saveMemory(memory);
61
+ }
62
+
63
+ // Anti-slop creative memory. saveCreativePattern() existed but had no
64
+ // caller anywhere, so getRecentCreativePatterns() always returned empty
65
+ // and the "DO NOT REPEAT RECENT PATTERNS" directive never once fired.
66
+ let creativeSaved = false;
67
+ if (args.creativePattern && args.creativePattern.designApproach) {
68
+ try {
69
+ await cortexDb.saveCreativePattern({
70
+ project: args.creativePattern.project || 'untitled project',
71
+ designApproach: args.creativePattern.designApproach,
72
+ colorPalette: args.creativePattern.colorPalette || 'unspecified',
73
+ fonts: args.creativePattern.fonts || 'unspecified'
74
+ });
75
+ creativeSaved = true;
76
+ } catch (e) {
77
+ console.error('[CORTEX] Creative pattern save failed:', e.message);
78
+ }
79
+ }
80
+
81
+ if (dependencies && dependencies.broadcast) {
82
+ dependencies.broadcast({
83
+ type: 'agent_message',
84
+ content: `🧠 Persistent memory updated: "${fact}"`
85
+ });
86
+ }
87
+
88
+ const notes = [
89
+ alreadyKnown ? 'Fact already known, not duplicated.' : 'Fact saved.',
90
+ `Total persistent facts: ${memory.facts.length}.`,
91
+ creativeSaved ? 'Creative pattern recorded in the V5 Cortex.' : ''
92
+ ].filter(Boolean);
93
+
94
+ return { success: true, output: notes.join(' ') };
95
+ } catch (e) {
96
+ return { success: false, error: `Failed to save memory: ${e.message}` };
97
+ }
98
+ }
99
+ };
@@ -0,0 +1,107 @@
1
+ import fs from 'fs/promises';
2
+ import os from 'os';
3
+
4
+ // Phase 3 AGI Upgrade: Fast Levenshtein Distance for Fuzzy Matching
5
+ function getLevenshteinDistance(a, b) {
6
+ const matrix = Array(a.length + 1).fill(null).map(() => Array(b.length + 1).fill(null));
7
+
8
+ for (let i = 0; i <= a.length; i++) matrix[i][0] = i;
9
+ for (let j = 0; j <= b.length; j++) matrix[0][j] = j;
10
+
11
+ for (let i = 1; i <= a.length; i++) {
12
+ for (let j = 1; j <= b.length; j++) {
13
+ const indicator = a[i - 1] === b[j - 1] ? 0 : 1;
14
+ matrix[i][j] = Math.min(
15
+ matrix[i - 1][j] + 1, // deletion
16
+ matrix[i][j - 1] + 1, // insertion
17
+ matrix[i - 1][j - 1] + indicator // substitution
18
+ );
19
+ }
20
+ }
21
+ return matrix[a.length][b.length];
22
+ }
23
+
24
+ function calculateConfidence(original, match) {
25
+ const dist = getLevenshteinDistance(original, match);
26
+ const maxLen = Math.max(original.length, match.length);
27
+ if (maxLen === 0) return 100;
28
+ return ((maxLen - dist) / maxLen) * 100;
29
+ }
30
+
31
+ export const multiEdit = {
32
+ schema: {
33
+ name: 'MultiEdit',
34
+ description: 'Make multiple edits to a single file atomically. Replaces old_string with new_string. If exact match fails, uses fuzzy matching (requires 90% confidence). Includes atomic rollback.',
35
+ parameters: {
36
+ type: 'object',
37
+ properties: {
38
+ file_path: { type: 'string' },
39
+ edits: {
40
+ type: 'array',
41
+ items: {
42
+ type: 'object',
43
+ properties: {
44
+ old_string: { type: 'string' },
45
+ new_string: { type: 'string' }
46
+ },
47
+ required: ['old_string', 'new_string']
48
+ },
49
+ minItems: 1
50
+ }
51
+ },
52
+ required: ['file_path', 'edits']
53
+ }
54
+ },
55
+ async execute(args) {
56
+ try {
57
+ const target = args.file_path.replace(/^~\//, os.homedir() + '/');
58
+ let originalContent = await fs.readFile(target, 'utf8');
59
+ let workingContent = originalContent; // Phase 3 AGI Upgrade: Atomic Rollback prep
60
+
61
+ let replacements = 0;
62
+ for (let i = 0; i < args.edits.length; i++) {
63
+ const edit = args.edits[i];
64
+
65
+ // Try exact match first
66
+ if (workingContent.includes(edit.old_string)) {
67
+ workingContent = workingContent.replace(edit.old_string, edit.new_string);
68
+ replacements++;
69
+ continue;
70
+ }
71
+
72
+ // Phase 3 AGI Upgrade: Fuzzy Match Fallback
73
+ // Find the best sliding window match (simplified for performance: split by line, scan chunks)
74
+ const lines = workingContent.split('\n');
75
+ const oldLines = edit.old_string.split('\n');
76
+ let bestMatch = null;
77
+ let highestConfidence = 0;
78
+
79
+ for (let j = 0; j <= lines.length - oldLines.length; j++) {
80
+ const chunk = lines.slice(j, j + oldLines.length).join('\n');
81
+ const conf = calculateConfidence(edit.old_string, chunk);
82
+ if (conf > highestConfidence) {
83
+ highestConfidence = conf;
84
+ bestMatch = chunk;
85
+ }
86
+ }
87
+
88
+ if (highestConfidence >= 90 && bestMatch) {
89
+ workingContent = workingContent.replace(bestMatch, edit.new_string);
90
+ replacements++;
91
+ } else {
92
+ // Atomic Rollback
93
+ return {
94
+ success: false,
95
+ error: `ATOMIC ROLLBACK: Edit index ${i} failed. Exact match not found, and fuzzy match confidence was too low (${highestConfidence.toFixed(2)}% < 90%). Previous edits in this batch were rolled back.`
96
+ };
97
+ }
98
+ }
99
+
100
+ // Commit the transaction
101
+ await fs.writeFile(target, workingContent, 'utf8');
102
+ return { success: true, output: `Transaction Success: ${replacements} atomic edits applied to ${args.file_path}` };
103
+ } catch (e) {
104
+ return { success: false, error: e.message };
105
+ }
106
+ }
107
+ };
@@ -0,0 +1,104 @@
1
+ import os from 'os';
2
+ import fs from 'fs';
3
+
4
+ // MCP server URL for Palmier Pro
5
+ const MCP_URL = 'http://127.0.0.1:19789/mcp';
6
+
7
+ export const palmierAutomator = {
8
+ schema: {
9
+ name: 'palmier_act',
10
+ description: 'Automate an existing Palmier Pro installation through its local MCP server. Causyne does not download or install the application.',
11
+ parameters: {
12
+ type: 'object',
13
+ properties: {
14
+ action: {
15
+ type: 'string',
16
+ enum: ['checkConnection', 'importAndAssemble']
17
+ },
18
+ mediaPath: {
19
+ type: 'string',
20
+ description: 'Absolute path to media directory to import (for importAndAssemble)'
21
+ },
22
+ fps: {
23
+ type: 'string',
24
+ description: 'Timeline framerate (e.g. 23.976) or "auto"'
25
+ },
26
+ resolution_x: {
27
+ type: 'string',
28
+ description: 'Timeline width or "auto"'
29
+ },
30
+ resolution_y: {
31
+ type: 'string',
32
+ description: 'Timeline height or "auto"'
33
+ }
34
+ },
35
+ required: ['action']
36
+ }
37
+ },
38
+ async execute(args) {
39
+ if (os.platform() !== 'darwin') {
40
+ return { success: false, error: 'Palmier Pro is a macOS native application. Not supported on this OS.' };
41
+ }
42
+
43
+ const { action } = args;
44
+
45
+ // JSON-RPC to MCP server
46
+ const sendMcpRequest = async (method, params) => {
47
+ try {
48
+ const response = await fetch(MCP_URL, {
49
+ method: 'POST',
50
+ headers: { 'Content-Type': 'application/json' },
51
+ body: JSON.stringify({
52
+ jsonrpc: '2.0',
53
+ id: Date.now(),
54
+ method: method,
55
+ params: params
56
+ })
57
+ });
58
+
59
+ if (!response.ok) {
60
+ throw new Error(`HTTP Error: ${response.status} ${response.statusText}`);
61
+ }
62
+
63
+ const data = await response.json();
64
+ if (data.error) throw new Error(data.error.message || JSON.stringify(data.error));
65
+ return data.result;
66
+ } catch (err) {
67
+ throw new Error(`MCP Connection Error (Is Palmier Pro running?): ${err.message}`);
68
+ }
69
+ };
70
+
71
+ try {
72
+ if (action === 'checkConnection') {
73
+ const res = await sendMcpRequest('tools/list', {});
74
+ return {
75
+ success: true,
76
+ message: 'Successfully connected to Palmier Pro MCP Server.',
77
+ tools: res.tools
78
+ };
79
+ }
80
+
81
+ if (action === 'importAndAssemble') {
82
+ if (!args.mediaPath || !fs.existsSync(args.mediaPath)) {
83
+ return { success: false, error: `Media path not found: ${args.mediaPath}` };
84
+ }
85
+ // Call the MCP tool exposed by Palmier Pro
86
+ const res = await sendMcpRequest('tools/call', {
87
+ name: 'import_and_assemble',
88
+ arguments: {
89
+ mediaPath: args.mediaPath,
90
+ fps: args.fps || 'auto',
91
+ resolution_x: args.resolution_x || 'auto',
92
+ resolution_y: args.resolution_y || 'auto'
93
+ }
94
+ });
95
+
96
+ return { success: true, message: 'Successfully imported and assembled media in Palmier Pro via MCP.', result: res };
97
+ }
98
+
99
+ return { success: false, error: `Unknown action: ${action}` };
100
+ } catch (err) {
101
+ return { success: false, error: err.message };
102
+ }
103
+ }
104
+ };
@@ -0,0 +1,112 @@
1
+ import fs from 'fs/promises';
2
+ import fsSync from 'fs';
3
+ import path from 'path';
4
+ import os from 'os';
5
+
6
+ // Lightweight, zero-dependency PDF text extractor
7
+ // Extracts printable ASCII/UTF-8 text streams and string literals inside PDF objects () and []
8
+ // This allows Causyne Agent to read documents like resumes, theses, and scripts without heavy C++/binary dependencies.
9
+ function extractTextFromPdfBuffer(buffer) {
10
+ const textChunks = [];
11
+ const content = buffer.toString('binary');
12
+
13
+ // 1. Look for text objects inside BT (Begin Text) ... ET (End Text) blocks
14
+ const btEtRegex = /BT[\s\S]*?ET/g;
15
+ let match;
16
+
17
+ while ((match = btEtRegex.exec(content)) !== null) {
18
+ const block = match[0];
19
+ // Match string literals inside parentheses (e.g., (Hello World) Tj or [(Hello) -10 (World)] TJ)
20
+ const strRegex = /\(([^)]+)\)/g;
21
+ let strMatch;
22
+ while ((strMatch = strRegex.exec(block)) !== null) {
23
+ let rawText = strMatch[1];
24
+ // Basic PDF octal/escape decoding
25
+ rawText = rawText
26
+ .replace(/\\n/g, '\n')
27
+ .replace(/\\r/g, '\r')
28
+ .replace(/\\t/g, '\t')
29
+ .replace(/\\\(/g, '(')
30
+ .replace(/\\\)/g, ')')
31
+ .replace(/\\\\/g, '\\')
32
+ .replace(/\\(\d{1,3})/g, (m, oct) => String.fromCharCode(parseInt(oct, 8)));
33
+
34
+ // Filter out raw font names / binary junk
35
+ if (rawText && rawText.trim().length > 0 && !rawText.startsWith('F') && !rawText.includes('\x00')) {
36
+ textChunks.push(rawText);
37
+ }
38
+ }
39
+ }
40
+
41
+ // 2. Fallback if PDF is uncompressed or structured differently (e.g. standard printable strings > 4 chars)
42
+ if (textChunks.length < 5) {
43
+ const printableRegex = /[\x20-\x7E\r\n\t]{5,}/g;
44
+ const matches = content.match(printableRegex) || [];
45
+ const filtered = matches.filter(s => {
46
+ const trimmed = s.trim();
47
+ if (trimmed.length < 5) return false;
48
+ // Skip common PDF internal keywords
49
+ const keywords = ['obj', 'endobj', 'stream', 'endstream', 'xref', 'trailer', 'startxref', 'Catalog', 'Pages', 'Font', 'XObject', 'ProcSet', 'MediaBox'];
50
+ if (keywords.some(k => trimmed === k || trimmed.startsWith('/') || trimmed.includes('<<') || trimmed.includes('>>'))) return false;
51
+ return true;
52
+ });
53
+ if (filtered.length > textChunks.length) {
54
+ return filtered.join('\n');
55
+ }
56
+ }
57
+
58
+ return textChunks.join(' ');
59
+ }
60
+
61
+ export const pdfRead = {
62
+ schema: {
63
+ name: 'pdf_read',
64
+ description: 'Extract text content from a local PDF document file. Ideal for reading attached PDFs, reports, scripts, and theses.',
65
+ parameters: {
66
+ type: 'object',
67
+ properties: {
68
+ path: {
69
+ type: 'string',
70
+ description: 'Absolute path to the .pdf file.'
71
+ },
72
+ maxChars: {
73
+ type: 'number',
74
+ description: 'Maximum characters to return (defaults to 30000 to protect context window).'
75
+ }
76
+ },
77
+ required: ['path']
78
+ }
79
+ },
80
+ async execute(args) {
81
+ try {
82
+ const target = args.path.replace(/^~\//, os.homedir() + '/');
83
+
84
+ if (!fsSync.existsSync(target)) {
85
+ return { success: false, error: `PDF file does not exist at path: ${target}` };
86
+ }
87
+
88
+ const buffer = await fs.readFile(target);
89
+ let extractedText = extractTextFromPdfBuffer(buffer);
90
+
91
+ // Clean up whitespace
92
+ extractedText = extractedText.replace(/\s{3,}/g, '\n\n').trim();
93
+
94
+ if (!extractedText || extractedText.length < 20) {
95
+ return {
96
+ success: true,
97
+ output: `[NOTICE: Only minimal text extracted (${extractedText.length} chars). The PDF might be scanned/image-based or heavily FlateDecode-compressed. For image-based PDFs, try converting pages to images or using OCR.]`,
98
+ rawExtracted: extractedText
99
+ };
100
+ }
101
+
102
+ const maxLimit = args.maxChars || 30000;
103
+ if (extractedText.length > maxLimit) {
104
+ extractedText = extractedText.substring(0, maxLimit) + `\n\n[... PDF TEXT TRUNCATED TO ${maxLimit} CHARACTERS ...]`;
105
+ }
106
+
107
+ return { success: true, output: extractedText, charCount: extractedText.length };
108
+ } catch (e) {
109
+ return { success: false, error: `Failed to read PDF: ${e.message}` };
110
+ }
111
+ }
112
+ };
@@ -0,0 +1,148 @@
1
+ import { spawn } from 'child_process';
2
+ import os from 'os';
3
+ import path from 'path';
4
+
5
+ // Singleton map of active persistent shell sessions
6
+ const sessions = new Map();
7
+
8
+ export function cleanupPersistentShells() {
9
+ for (const [id, session] of sessions.entries()) {
10
+ try {
11
+ session.process.kill('SIGTERM');
12
+ } catch (e) {}
13
+ }
14
+ sessions.clear();
15
+ }
16
+
17
+ // Ensure cleanup on process exit
18
+ process.on('exit', cleanupPersistentShells);
19
+ process.on('SIGINT', () => { cleanupPersistentShells(); process.exit(); });
20
+ process.on('SIGTERM', () => { cleanupPersistentShells(); process.exit(); });
21
+
22
+ export const persistentShell = {
23
+ schema: {
24
+ name: 'terminal.persistent_shell',
25
+ description: 'Executes shell commands in a stateful, persistent REPL session where working directory (cd), virtual environment activation, and environment variables persist across sequential calls. Surpasses stateless execution.',
26
+ parameters: {
27
+ type: 'object',
28
+ properties: {
29
+ command: { type: 'string', description: 'Shell command line string to execute inside the persistent session.' },
30
+ sessionId: { type: 'string', description: 'Unique identifier for the shell session. Defaults to "default".' },
31
+ reset: { type: 'boolean', description: 'Set to true to kill the existing session and spawn a fresh terminal shell.' },
32
+ timeoutMs: { type: 'integer', description: 'Maximum execution time in milliseconds before timing out. Defaults to 30000.' }
33
+ },
34
+ required: ['command']
35
+ }
36
+ },
37
+
38
+ async execute(args, context = {}) {
39
+ try {
40
+ const command = args.command;
41
+ if (!command) return { success: false, error: 'Command parameter is required.' };
42
+
43
+ const sessionId = args.sessionId || 'default';
44
+ const reset = args.reset === true;
45
+ const timeoutMs = args.timeoutMs || 30000;
46
+
47
+ if (reset && sessions.has(sessionId)) {
48
+ try { sessions.get(sessionId).process.kill('SIGTERM'); } catch (e) {}
49
+ sessions.delete(sessionId);
50
+ }
51
+
52
+ let session = sessions.get(sessionId);
53
+ if (!session) {
54
+ const isWin = process.platform === 'win32';
55
+ const shellBin = isWin ? 'powershell.exe' : (process.env.SHELL || '/bin/zsh');
56
+ const shellArgs = isWin ? ['-NoLogo', '-NoExit', '-Command', '-'] : ['-l'];
57
+
58
+ const proc = spawn(shellBin, shellArgs, {
59
+ cwd: process.cwd(),
60
+ env: { ...process.env, CAUSYNE_PERSISTENT_SHELL: '1', PS1: '' },
61
+ stdio: ['pipe', 'pipe', 'pipe']
62
+ });
63
+
64
+ proc.on('error', (err) => {
65
+ sessions.delete(sessionId);
66
+ });
67
+
68
+ proc.on('exit', () => {
69
+ sessions.delete(sessionId);
70
+ });
71
+
72
+ session = {
73
+ process: proc,
74
+ currentCwd: process.cwd()
75
+ };
76
+ sessions.set(sessionId, session);
77
+ }
78
+
79
+ return new Promise((resolve) => {
80
+ const markerToken = `__CAUSYNE_PTY_DONE_${Date.now()}_${Math.random().toString(36).substr(2, 6)}__`;
81
+ let outputBuffer = '';
82
+ let errorBuffer = '';
83
+ let isDone = false;
84
+
85
+ const onData = (data) => {
86
+ const str = data.toString('utf8');
87
+ outputBuffer += str;
88
+ if (outputBuffer.includes(markerToken)) {
89
+ finish();
90
+ }
91
+ };
92
+
93
+ const onErrorData = (data) => {
94
+ errorBuffer += data.toString('utf8');
95
+ };
96
+
97
+ const timer = setTimeout(() => {
98
+ if (!isDone) {
99
+ isDone = true;
100
+ cleanupListeners();
101
+ resolve({
102
+ success: false,
103
+ error: `Command timed out after ${timeoutMs}ms. Output captured so far:\n${outputBuffer}\n${errorBuffer}`
104
+ });
105
+ }
106
+ }, timeoutMs);
107
+
108
+ const cleanupListeners = () => {
109
+ session.process.stdout.removeListener('data', onData);
110
+ session.process.stderr.removeListener('data', onErrorData);
111
+ clearTimeout(timer);
112
+ };
113
+
114
+ const finish = () => {
115
+ if (isDone) return;
116
+ isDone = true;
117
+ cleanupListeners();
118
+
119
+ // Clean marker token from output buffer
120
+ const markerIndex = outputBuffer.indexOf(markerToken);
121
+ let cleanOutput = markerIndex !== -1 ? outputBuffer.substring(0, markerIndex) : outputBuffer;
122
+ cleanOutput = cleanOutput.replace(new RegExp(`echo\\s+"?${markerToken}"?`, 'g'), '').trim();
123
+
124
+ // Try to query current cwd asynchronously for status info
125
+ resolve({
126
+ success: true,
127
+ output: JSON.stringify({
128
+ sessionId: sessionId,
129
+ status: 'Executed cleanly in stateful session',
130
+ stdout: cleanOutput || (errorBuffer ? '' : '[No output]'),
131
+ stderr: errorBuffer.trim() || undefined
132
+ }, null, 2)
133
+ });
134
+ };
135
+
136
+ session.process.stdout.on('data', onData);
137
+ session.process.stderr.on('data', onErrorData);
138
+
139
+ // Send command followed by echo marker
140
+ const isWin = process.platform === 'win32';
141
+ const markerCmd = isWin ? `echo "${markerToken}"` : `echo "${markerToken}"`;
142
+ session.process.stdin.write(`${command}\n${markerCmd}\n`);
143
+ });
144
+ } catch (e) {
145
+ return { success: false, error: e.message };
146
+ }
147
+ }
148
+ };