@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.
- package/LICENSE +102 -0
- package/README.md +31 -2
- package/SECURITY.md +24 -0
- package/bin/causyne.js +13 -0
- package/docs/ARCHITECTURE.md +63 -0
- package/docs/PROVENANCE.md +29 -0
- package/docs/SECURITY_REVIEW.md +89 -0
- package/docs/THIRD_PARTY_NOTICES.md +49 -0
- package/native-skills/ai-engineering/context-window-management/SKILL.md +43 -0
- package/native-skills/ai-engineering/llm-evaluation/SKILL.md +46 -0
- package/native-skills/ai-engineering/model-cost-and-latency/SKILL.md +46 -0
- package/native-skills/ai-engineering/prompt-construction/SKILL.md +42 -0
- package/native-skills/ai-engineering/retrieval-augmented-generation/SKILL.md +42 -0
- package/native-skills/ai-engineering/structured-output-and-schemas/SKILL.md +45 -0
- package/native-skills/ai-engineering/tool-calling-agents/SKILL.md +43 -0
- package/native-skills/android/app-performance-profiling/SKILL.md +42 -0
- package/native-skills/android/background-work-scheduling/SKILL.md +47 -0
- package/native-skills/android/compose-state-and-recomposition/SKILL.md +43 -0
- package/native-skills/android/gradle-build-configuration/SKILL.md +41 -0
- package/native-skills/android/lifecycle-and-leaks/SKILL.md +46 -0
- package/native-skills/android/play-store-release/SKILL.md +43 -0
- package/native-skills/android/room-and-persistence/SKILL.md +40 -0
- package/native-skills/backend/authentication-and-sessions/SKILL.md +45 -0
- package/native-skills/backend/background-jobs-and-queues/SKILL.md +42 -0
- package/native-skills/backend/caching-strategy/SKILL.md +44 -0
- package/native-skills/backend/graceful-shutdown/SKILL.md +42 -0
- package/native-skills/backend/rate-limiting-and-quotas/SKILL.md +40 -0
- package/native-skills/backend/rest-api-design/SKILL.md +40 -0
- package/native-skills/backend/webhook-delivery-and-receipt/SKILL.md +43 -0
- package/native-skills/cloud/cost-optimisation/SKILL.md +46 -0
- package/native-skills/cloud/iam-least-privilege/SKILL.md +43 -0
- package/native-skills/cloud/infrastructure-as-code/SKILL.md +43 -0
- package/native-skills/cloud/multi-region-and-failover/SKILL.md +43 -0
- package/native-skills/cloud/serverless-patterns/SKILL.md +47 -0
- package/native-skills/data/data-quality-monitoring/SKILL.md +51 -0
- package/native-skills/data/etl-pipeline-reliability/SKILL.md +43 -0
- package/native-skills/data/exploratory-analysis/SKILL.md +43 -0
- package/native-skills/data/metric-definition/SKILL.md +42 -0
- package/native-skills/data/notebook-to-production/SKILL.md +43 -0
- package/native-skills/database/backup-and-recovery/SKILL.md +43 -0
- package/native-skills/database/connection-pooling/SKILL.md +42 -0
- package/native-skills/database/data-integrity-and-constraints/SKILL.md +42 -0
- package/native-skills/database/query-optimisation/SKILL.md +38 -0
- package/native-skills/database/safe-migrations/SKILL.md +47 -0
- package/native-skills/database/schema-design/SKILL.md +38 -0
- package/native-skills/database/transactions-and-isolation/SKILL.md +41 -0
- package/native-skills/design/colour-system-construction/SKILL.md +38 -0
- package/native-skills/design/design-direction-brief/SKILL.md +41 -0
- package/native-skills/design/motion-and-transitions/SKILL.md +38 -0
- package/native-skills/design/responsive-strategy/SKILL.md +39 -0
- package/native-skills/design/spacing-and-rhythm/SKILL.md +38 -0
- package/native-skills/design/type-scale-and-hierarchy/SKILL.md +39 -0
- package/native-skills/design/visual-qa-loop/SKILL.md +42 -0
- package/native-skills/devops/docker-multi-stage-builds/SKILL.md +60 -0
- package/native-skills/devops/github-actions-ci/SKILL.md +60 -0
- package/native-skills/devops/incident-triage/SKILL.md +38 -0
- package/native-skills/devops/kubernetes-deployment-hardening/SKILL.md +64 -0
- package/native-skills/devops/observability-baseline/SKILL.md +38 -0
- package/native-skills/devops/secret-management/SKILL.md +40 -0
- package/native-skills/devops/terraform-state-and-modules/SKILL.md +36 -0
- package/native-skills/devops/zero-downtime-deploys/SKILL.md +40 -0
- package/native-skills/git/bisect-regression-hunt/SKILL.md +43 -0
- package/native-skills/git/branch-strategy/SKILL.md +39 -0
- package/native-skills/git/commit-hygiene/SKILL.md +41 -0
- package/native-skills/git/history-recovery/SKILL.md +44 -0
- package/native-skills/git/large-repository-surgery/SKILL.md +46 -0
- package/native-skills/git/merge-conflict-resolution/SKILL.md +42 -0
- package/native-skills/media/cinematic-3d-shot/SKILL.md +104 -0
- package/native-skills/mobile/app-startup-optimisation/SKILL.md +41 -0
- package/native-skills/mobile/cross-platform-tradeoffs/SKILL.md +42 -0
- package/native-skills/mobile/deep-linking/SKILL.md +39 -0
- package/native-skills/mobile/mobile-storage-security/SKILL.md +45 -0
- package/native-skills/mobile/offline-first-sync/SKILL.md +42 -0
- package/native-skills/mobile/push-notifications/SKILL.md +38 -0
- package/native-skills/performance/algorithmic-complexity/SKILL.md +43 -0
- package/native-skills/performance/concurrency-and-parallelism/SKILL.md +42 -0
- package/native-skills/performance/load-testing/SKILL.md +42 -0
- package/native-skills/performance/memory-and-leak-hunting/SKILL.md +41 -0
- package/native-skills/performance/network-and-payload-efficiency/SKILL.md +42 -0
- package/native-skills/performance/profiling-methodology/SKILL.md +46 -0
- package/native-skills/python/async-correctness/SKILL.md +40 -0
- package/native-skills/python/cli-tool-construction/SKILL.md +44 -0
- package/native-skills/python/data-processing-pipelines/SKILL.md +38 -0
- package/native-skills/python/environment-isolation/SKILL.md +41 -0
- package/native-skills/python/packaging-and-distribution/SKILL.md +56 -0
- package/native-skills/python/pytest-suite-design/SKILL.md +39 -0
- package/native-skills/python/type-hints-and-static-checking/SKILL.md +42 -0
- package/native-skills/security/authorization-model/SKILL.md +42 -0
- package/native-skills/security/crypto-usage/SKILL.md +48 -0
- package/native-skills/security/dependency-supply-chain/SKILL.md +42 -0
- package/native-skills/security/input-validation-and-injection/SKILL.md +44 -0
- package/native-skills/security/secure-file-uploads/SKILL.md +46 -0
- package/native-skills/security/security-headers-and-csp/SKILL.md +44 -0
- package/native-skills/security/threat-modelling/SKILL.md +46 -0
- package/native-skills/testing/end-to-end-test-stability/SKILL.md +42 -0
- package/native-skills/testing/property-based-testing/SKILL.md +46 -0
- package/native-skills/testing/regression-test-from-a-bug/SKILL.md +41 -0
- package/native-skills/testing/snapshot-and-visual-regression/SKILL.md +42 -0
- package/native-skills/testing/test-doubles-and-mocking/SKILL.md +43 -0
- package/native-skills/testing/test-strategy-and-pyramid/SKILL.md +39 -0
- package/native-skills/testing/writing-good-assertions/SKILL.md +42 -0
- package/native-skills/web/accessible-components/SKILL.md +45 -0
- package/native-skills/web/api-client-resilience/SKILL.md +39 -0
- package/native-skills/web/bundle-size-reduction/SKILL.md +38 -0
- package/native-skills/web/core-web-vitals/SKILL.md +40 -0
- package/native-skills/web/css-layout-systems/SKILL.md +41 -0
- package/native-skills/web/form-validation-ux/SKILL.md +39 -0
- package/native-skills/web/react-state-architecture/SKILL.md +41 -0
- package/native-skills/web/ssr-hydration-correctness/SKILL.md +45 -0
- package/package.json +68 -4
- package/src/agentLoop.js +2458 -0
- package/src/appChannel.js +109 -0
- package/src/approvalGrants.js +66 -0
- package/src/auditLog.js +52 -0
- package/src/autoUpdate.js +12 -0
- package/src/capabilities.js +36 -0
- package/src/checkpoints.js +1199 -0
- package/src/cliMode.js +638 -0
- package/src/commands.js +805 -0
- package/src/compiledSkills.js +540 -0
- package/src/config.js +130 -0
- package/src/contextManager.js +498 -0
- package/src/cortex/autoCondenser.js +192 -0
- package/src/cortex/provisioner.js +27 -0
- package/src/cortex/vectorDb.js +447 -0
- package/src/designPalettes.js +99 -0
- package/src/devMode.js +6 -0
- package/src/embodimentSession.js +180 -0
- package/src/entrypoint.js +194 -0
- package/src/execSafe.js +21 -0
- package/src/gitSnapshot.js +378 -0
- package/src/mcp/jsonRpc.js +165 -0
- package/src/mcp/mcpClient.js +136 -0
- package/src/mcp/mcpRegistry.js +207 -0
- package/src/memory.js +62 -0
- package/src/memoryManager.js +124 -0
- package/src/modelCatalogue.js +132 -0
- package/src/modelIds.js +42 -0
- package/src/modelRouter.js +145 -0
- package/src/outputStore.js +246 -0
- package/src/perception.js +124 -0
- package/src/planCache.js +437 -0
- package/src/procSignal.js +32 -0
- package/src/providers/anthropic.js +408 -0
- package/src/providers/base.js +328 -0
- package/src/providers/dashscope-byok.js +86 -0
- package/src/providers/deepseek.js +80 -0
- package/src/providers/factory.js +37 -0
- package/src/providers/gemini.js +346 -0
- package/src/providers/moonshot.js +73 -0
- package/src/providers/muse.js +169 -0
- package/src/providers/ollama.js +104 -0
- package/src/providers/openai-compatible.js +84 -0
- package/src/providers/openai.js +20 -0
- package/src/providers/qwen-byok.js +76 -0
- package/src/providers/xai.js +73 -0
- package/src/provisioning/capabilityConsent.js +24 -0
- package/src/provisioning/capabilityProvisioner.js +271 -0
- package/src/provisioning/capabilityTools.js +29 -0
- package/src/reporting.js +373 -0
- package/src/sandbox.js +529 -0
- package/src/sessionStore.js +279 -0
- package/src/skillsManager.js +288 -0
- package/src/slopDetector.js +307 -0
- package/src/taskBuffer.js +45 -0
- package/src/taskShape.js +241 -0
- package/src/textDiff.js +150 -0
- package/src/tokenTracker.js +560 -0
- package/src/tool-scripts/ax_snapshot.applescript +39 -0
- package/src/tool-scripts/ax_snapshot.ps1 +10 -0
- package/src/tool-scripts/desktop_act.applescript +58 -0
- package/src/tool-scripts/resolve_automator.py +119 -0
- package/src/toolPacks.js +524 -0
- package/src/tools/archive.js +63 -0
- package/src/tools/astryxOracle.js +78 -0
- package/src/tools/axSnapshot.js +165 -0
- package/src/tools/blenderAutomator.js +349 -0
- package/src/tools/browserAutomator.js +252 -0
- package/src/tools/checkSystemSpecs.js +64 -0
- package/src/tools/clipboard.js +36 -0
- package/src/tools/codeSearchAst.js +101 -0
- package/src/tools/codeSearcher.js +230 -0
- package/src/tools/codeTools.js +262 -0
- package/src/tools/codebaseSearch.js +75 -0
- package/src/tools/createSkill.js +129 -0
- package/src/tools/deferredMcpSearch.js +246 -0
- package/src/tools/desktopAct.js +208 -0
- package/src/tools/desktopOpen.js +34 -0
- package/src/tools/dynamicLoader.js +44 -0
- package/src/tools/executeProceduralMedia.js +93 -0
- package/src/tools/fsDelete.js +51 -0
- package/src/tools/fsEdit.js +105 -0
- package/src/tools/fsFind.js +128 -0
- package/src/tools/fsList.js +77 -0
- package/src/tools/fsMove.js +57 -0
- package/src/tools/fsRead.js +87 -0
- package/src/tools/fsSearch.js +198 -0
- package/src/tools/fsWrite.js +41 -0
- package/src/tools/gameAiBehaviorTree.js +208 -0
- package/src/tools/gameAnimationRig.js +194 -0
- package/src/tools/gameAssetFetcher.js +372 -0
- package/src/tools/gameAssetHarvester.js +210 -0
- package/src/tools/gameAudioSynth.js +267 -0
- package/src/tools/gameBuilder.js +886 -0
- package/src/tools/gameCanvasAudit.js +101 -0
- package/src/tools/gameCinematicSequencer.js +151 -0
- package/src/tools/gameDataForge.js +124 -0
- package/src/tools/gameDialogueTreeGen.js +166 -0
- package/src/tools/gameEconomyBalancer.js +161 -0
- package/src/tools/gameEntitySpawner.js +207 -0
- package/src/tools/gameLoreForge.js +153 -0
- package/src/tools/gameQuestArcBuilder.js +152 -0
- package/src/tools/gameSceneBuilder.js +158 -0
- package/src/tools/gameShaderForge.js +181 -0
- package/src/tools/gameTilemapGenerator.js +164 -0
- package/src/tools/gameUiHudBuilder.js +292 -0
- package/src/tools/gameVfxEmitterGen.js +108 -0
- package/src/tools/gameWebRuntimeInspector.js +91 -0
- package/src/tools/generateMediaViaApi.js +358 -0
- package/src/tools/gitExec.js +54 -0
- package/src/tools/godotAutomator.js +437 -0
- package/src/tools/manageLocalAiDependencies.js +220 -0
- package/src/tools/memoryWrite.js +99 -0
- package/src/tools/multiEdit.js +107 -0
- package/src/tools/palmierAutomator.js +104 -0
- package/src/tools/pdfReader.js +112 -0
- package/src/tools/persistentShell.js +148 -0
- package/src/tools/printer.js +316 -0
- package/src/tools/procTerm.js +91 -0
- package/src/tools/processList.js +52 -0
- package/src/tools/readNativeSkill.js +39 -0
- package/src/tools/registry.js +277 -0
- package/src/tools/resolveAutomator.js +102 -0
- package/src/tools/screenshot.js +75 -0
- package/src/tools/subagentOrchestrator.js +305 -0
- package/src/tools/sysRun.js +125 -0
- package/src/tools/todoWrite.js +75 -0
- package/src/tools/toolDispatch.js +127 -0
- package/src/tools/toolForge.js +124 -0
- package/src/tools/updatePlan.js +88 -0
- package/src/tools/viewImage.js +82 -0
- package/src/tools/visionInspector.js +154 -0
- package/src/tools/webFetch.js +74 -0
- package/src/tools/webSearch.js +82 -0
- package/src/tui/ask.js +164 -0
- package/src/tui/banner.js +158 -0
- package/src/tui/block.js +152 -0
- package/src/tui/chatRenderer.js +953 -0
- package/src/tui/completion.js +424 -0
- package/src/tui/dialogs.js +606 -0
- package/src/tui/diff.js +296 -0
- package/src/tui/history.js +135 -0
- package/src/tui/home.js +217 -0
- package/src/tui/invocation.js +105 -0
- package/src/tui/keys.js +443 -0
- package/src/tui/lineEditor.js +256 -0
- package/src/tui/overlay.js +256 -0
- package/src/tui/prompt.js +1258 -0
- package/src/tui/selectList.js +335 -0
- package/src/tui/stage.js +330 -0
- package/src/tui/stageSurface.js +609 -0
- package/src/tui/statusLine.js +254 -0
- package/src/tui/text.js +345 -0
- package/src/tui/theme.js +393 -0
- package/src/tui/toolDisplay.js +176 -0
- package/src/tui/transcript.js +235 -0
- package/src/tui/viewport.js +337 -0
- package/src/uncertainty.js +271 -0
- package/src/userSkills.js +265 -0
- package/src/utils/cli_ui.js +0 -0
- package/src/utils/inquirer.js +12 -0
- package/src/utils/paths.js +37 -0
- package/src/version.js +38 -0
- 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
|
+
};
|