@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.
- 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,271 @@
|
|
|
1
|
+
import { failureClass } from './planCache.js';
|
|
2
|
+
|
|
3
|
+
// Step-level uncertainty, computed from signals the loop already holds.
|
|
4
|
+
//
|
|
5
|
+
// WHAT THIS IS NOT, AND WHY.
|
|
6
|
+
//
|
|
7
|
+
// The research spec asked for CATTS-style gating: sample N candidate actions,
|
|
8
|
+
// measure entropy and the top-1/top-2 margin across them, and escalate when the
|
|
9
|
+
// samples disagree. Two findings killed that design.
|
|
10
|
+
//
|
|
11
|
+
// 1. The CATTS and OPPO papers are NOT in the research corpus. Asked
|
|
12
|
+
// directly, it reported their absence three times. The spec cited them
|
|
13
|
+
// second-hand, so their thresholds were never available to us.
|
|
14
|
+
// 2. More decisively: without token logprobs, every method in that family
|
|
15
|
+
// measures disagreement by SAMPLING N completions. Causyne is BYOK across
|
|
16
|
+
// ten providers and several expose no logprobs at all. Sampling N times to
|
|
17
|
+
// decide whether to spend more is self-defeating — the gate would cost
|
|
18
|
+
// more than the thing it gates.
|
|
19
|
+
//
|
|
20
|
+
// Asked which substitutes exist when logprobs are unavailable, the corpus named
|
|
21
|
+
// four: majority voting over N samples, an auxiliary verifier model, cross-tool
|
|
22
|
+
// execution matching, and verbalized self-critique. The first two each cost an
|
|
23
|
+
// extra model call. **The last two do not**, and they are what this module is
|
|
24
|
+
// built from, together with execution signals the loop already tracks for free.
|
|
25
|
+
//
|
|
26
|
+
// WHAT THE CORPUS THEN CONFIRMED (research round 3, questions A1-A7).
|
|
27
|
+
//
|
|
28
|
+
// "Dynamic Adaptive Reasoning: Intent-Aware Scheduling" (IARS) detects its
|
|
29
|
+
// reasoning states — Exploring, Confirming, Stuck/Ambiguous, Near-Answer — with
|
|
30
|
+
// one of two pipelines: a small trained classifier, OR **a text-based heuristic
|
|
31
|
+
// of keyword matching plus structural repetition analysis**, run as a parallel
|
|
32
|
+
// stream analyser with negligible cost and no logprobs. That second pipeline is
|
|
33
|
+
// this module. The thrash detector below is its "structural repetition"; the
|
|
34
|
+
// hedge patterns are its "keyword matching". So this is an implementation of a
|
|
35
|
+
// published detector, not an extrapolation.
|
|
36
|
+
//
|
|
37
|
+
// IARS issues four directives — [DEEPER_THINK], [STREAMLINE_THINK],
|
|
38
|
+
// [CONCLUDE_NOW], [RETRY_SUBPROBLEM] — and couples them to decoding:
|
|
39
|
+
// DEEPER_THINK raises temperature to widen the search; CONCLUDE_NOW drops into
|
|
40
|
+
// fast decoding. The paper states the DIRECTION of those changes and NOT the
|
|
41
|
+
// magnitudes ("present, but not stated"). The delta below is therefore ours.
|
|
42
|
+
//
|
|
43
|
+
// Two things the corpus ruled OUT, so nobody re-proposes them:
|
|
44
|
+
// * REVERSE ("Generate, but Verify") gates on P(</UN>) > tau with tau = 0.003
|
|
45
|
+
// (LLaVA) / 0.01 (Qwen2.5-VL). It needs a model FINE-TUNED to emit that
|
|
46
|
+
// token — unavailable over BYOK — and its measured overhead is 3.05x tokens.
|
|
47
|
+
// A gate that triples the bill is the failure this module exists to avoid.
|
|
48
|
+
// * Verbalized self-confidence (a model rating its own confidence) is NOT
|
|
49
|
+
// evaluated or calibrated anywhere in the corpus. The hedge patterns here
|
|
50
|
+
// are used as IARS-style Stuck/Ambiguous keyword detection, never as a
|
|
51
|
+
// calibrated confidence score, and they cannot cross the gate alone.
|
|
52
|
+
//
|
|
53
|
+
// HONEST LIMITS:
|
|
54
|
+
// * No single signal can move the score past the escalation threshold alone.
|
|
55
|
+
// * The WEIGHTS are engineering judgement, not published constants. The
|
|
56
|
+
// corpus validates the SIGNALS (IARS, OBLIVION's query-to-buffer cosine
|
|
57
|
+
// similarity) but publishes no weights for combining them. They are named
|
|
58
|
+
// constants so measured values can replace them without touching logic.
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @typedef {Object} StepSignals
|
|
62
|
+
* @property {number} [consecutiveFailures] Failures in a row, from the loop.
|
|
63
|
+
* @property {any} [lastResult] The previous tool result, if any.
|
|
64
|
+
* @property {string} [lastTool] Name of the previous tool.
|
|
65
|
+
* @property {string} [pendingTool] The tool about to be called.
|
|
66
|
+
* @property {string[]}[recentTools] Recent tool names, oldest first.
|
|
67
|
+
* @property {number} [planSimilarity] 0..1 plan-cache match, if any.
|
|
68
|
+
* @property {number} [lessonSimilarity] 0..1 Cortex retrieval match, if any.
|
|
69
|
+
* @property {string} [assistantText] The model's own last message.
|
|
70
|
+
* @property {number} [logprobMargin] top1-top2 in nats, when a provider exposes it.
|
|
71
|
+
* @property {boolean} [vfePlateau] Whether variational free energy has plateaued.
|
|
72
|
+
*/
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @typedef {Object} Assessment
|
|
76
|
+
* @property {number} uncertainty 0 (settled) .. 1 (lost).
|
|
77
|
+
* @property {'confident'|'contested'|'stuck'} level
|
|
78
|
+
* @property {string[]} signals Which contributors fired, for the audit trail.
|
|
79
|
+
* @property {string} directive Prompt text to inject, or '' to stay silent.
|
|
80
|
+
* @property {number} temperatureDelta Added to the task temperature for this
|
|
81
|
+
* step. IARS couples its escalation directive to a wider
|
|
82
|
+
* search; the direction is the paper's, the magnitude is
|
|
83
|
+
* ours (see STUCK_TEMPERATURE_DELTA).
|
|
84
|
+
*/
|
|
85
|
+
|
|
86
|
+
/** Above this, the step is contested enough to be worth a reflection directive. */
|
|
87
|
+
export const CONTESTED_AT = 0.45;
|
|
88
|
+
|
|
89
|
+
/** Above this, the agent is not converging and should re-plan rather than retry. */
|
|
90
|
+
export const STUCK_AT = 0.75;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* How much wider the search gets when the agent is stuck.
|
|
94
|
+
*
|
|
95
|
+
* IARS: [DEEPER_THINK] / [RETRY_SUBPROBLEM] raise decoding temperature to
|
|
96
|
+
* escape a local minimum. The paper gives the direction and no number
|
|
97
|
+
* ("present, but not stated"), so this value is unmeasured. Kept small: a
|
|
98
|
+
* stuck agent needs a different route, not a random one.
|
|
99
|
+
*/
|
|
100
|
+
export const STUCK_TEMPERATURE_DELTA = 0.2;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* No single signal may cross CONTESTED_AT alone.
|
|
104
|
+
*
|
|
105
|
+
* This is the rule that keeps a badly-calibrated signal from driving the gate.
|
|
106
|
+
* Verbalized confidence in particular is unreliable enough that acting on it by
|
|
107
|
+
* itself would fire reflection on ordinary hedging language.
|
|
108
|
+
*/
|
|
109
|
+
const MAX_SINGLE_CONTRIBUTION = 0.4;
|
|
110
|
+
|
|
111
|
+
const WEIGHTS = {
|
|
112
|
+
consecutiveFailure: 0.25, // per failure, capped below
|
|
113
|
+
// Weighted ABOVE a bare failure count on purpose. Three failures say
|
|
114
|
+
// "something is wrong"; a `not_found` class says WHAT — and a diagnosis is
|
|
115
|
+
// worth more than a symptom. With this at 0.15, three failures on a wrong
|
|
116
|
+
// route scored 0.60 and never reached the retry directive that exists for
|
|
117
|
+
// exactly that case.
|
|
118
|
+
hardFailureClass: 0.30, // a failure that says the ROUTE is wrong
|
|
119
|
+
thrash: 0.30, // the same tool three times running
|
|
120
|
+
vfePlateau: 0.35, // Active Inference: error plateau with zero variance (stuck in local minimum)
|
|
121
|
+
noPriorArt: 0.05, // nothing in plan cache or Cortex to lean on
|
|
122
|
+
hedging: 0.20, // the model's own language says it is unsure
|
|
123
|
+
logprobMargin: 0.25 // only when a provider actually exposes it
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Phrases in which a model reports its own uncertainty.
|
|
128
|
+
*
|
|
129
|
+
* Deliberately narrow. A broad list turns ordinary careful writing into a
|
|
130
|
+
* signal, and a gate that fires on caution is a gate that fires always.
|
|
131
|
+
* @type {RegExp}
|
|
132
|
+
*/
|
|
133
|
+
const HEDGE = /\b(i am not sure|i'm not sure|not certain|unclear (?:why|what|how)|might be|may be wrong|hard to tell|cannot determine|let me guess|assuming that|if that is the case|unsure)\b/i;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Failure classes that mean the ROUTE is wrong rather than the moment.
|
|
137
|
+
*
|
|
138
|
+
* `network` and `rate_limited` are absent on purpose: retrying is the correct
|
|
139
|
+
* response to those, and treating them as uncertainty would trigger a re-plan
|
|
140
|
+
* for a problem that fixes itself.
|
|
141
|
+
* @type {ReadonlySet<string>}
|
|
142
|
+
*/
|
|
143
|
+
const ROUTE_IS_WRONG = new Set(['not_found', 'permission', 'invalid_args', 'syntax']);
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Is the agent calling the same tool over and over?
|
|
147
|
+
*
|
|
148
|
+
* Repetition is the cheapest honest uncertainty signal there is: a converging
|
|
149
|
+
* agent moves on. Three of the same in a row is the threshold because two is a
|
|
150
|
+
* legitimate read-then-write pair.
|
|
151
|
+
*
|
|
152
|
+
* @param {string[]} recent
|
|
153
|
+
* @param {string} pending
|
|
154
|
+
* @returns {boolean}
|
|
155
|
+
*/
|
|
156
|
+
export function isThrashing(recent, pending) {
|
|
157
|
+
const tail = [...(recent || [])];
|
|
158
|
+
if (pending) tail.push(pending);
|
|
159
|
+
if (tail.length < 3) return false;
|
|
160
|
+
const last3 = tail.slice(-3);
|
|
161
|
+
return last3.every((t) => t && t === last3[0]);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Score a step before committing to it.
|
|
166
|
+
*
|
|
167
|
+
* Every input is something the loop already has. Nothing here calls a model,
|
|
168
|
+
* samples a completion, or touches the network — which is the entire point.
|
|
169
|
+
*
|
|
170
|
+
* @param {StepSignals} signals
|
|
171
|
+
* @returns {Assessment}
|
|
172
|
+
*/
|
|
173
|
+
export function assessStep(signals = {}) {
|
|
174
|
+
const fired = [];
|
|
175
|
+
let score = 0;
|
|
176
|
+
|
|
177
|
+
/** Add a contributor, capped so no single one can decide the outcome. */
|
|
178
|
+
const add = (amount, name) => {
|
|
179
|
+
const capped = Math.min(amount, MAX_SINGLE_CONTRIBUTION);
|
|
180
|
+
if (capped <= 0) return;
|
|
181
|
+
score += capped;
|
|
182
|
+
fired.push(name);
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const failures = Number(signals.consecutiveFailures) || 0;
|
|
186
|
+
if (failures > 0) add(Math.min(failures, 3) * WEIGHTS.consecutiveFailure, `failures:${failures}`);
|
|
187
|
+
|
|
188
|
+
if (signals.lastResult) {
|
|
189
|
+
const why = failureClass(signals.lastResult);
|
|
190
|
+
if (why && ROUTE_IS_WRONG.has(why)) add(WEIGHTS.hardFailureClass, `route_wrong:${why}`);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (isThrashing(signals.recentTools, signals.pendingTool)) {
|
|
194
|
+
add(WEIGHTS.thrash, `thrash:${signals.pendingTool || 'repeat'}`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (signals.vfePlateau) {
|
|
198
|
+
add(WEIGHTS.vfePlateau, 'vfe_plateau');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const bestPrior = Math.max(Number(signals.planSimilarity) || 0, Number(signals.lessonSimilarity) || 0);
|
|
202
|
+
if (bestPrior === 0) add(WEIGHTS.noPriorArt, 'no_prior_art');
|
|
203
|
+
|
|
204
|
+
if (typeof signals.assistantText === 'string' && HEDGE.test(signals.assistantText)) {
|
|
205
|
+
add(WEIGHTS.hedging, 'model_hedged');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Only when the provider genuinely reports it. A missing margin is NOT
|
|
209
|
+
// evidence of confidence, so its absence contributes nothing in either
|
|
210
|
+
// direction.
|
|
211
|
+
if (typeof signals.logprobMargin === 'number' && Number.isFinite(signals.logprobMargin)) {
|
|
212
|
+
if (signals.logprobMargin < 0.5) add(WEIGHTS.logprobMargin, 'logprob_margin_narrow');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const uncertainty = Math.min(1, score);
|
|
216
|
+
const level = uncertainty >= STUCK_AT ? 'stuck' : uncertainty >= CONTESTED_AT ? 'contested' : 'confident';
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
uncertainty,
|
|
220
|
+
level,
|
|
221
|
+
signals: fired,
|
|
222
|
+
directive: directiveFor(level, fired),
|
|
223
|
+
temperatureDelta: level === 'stuck' ? STUCK_TEMPERATURE_DELTA : 0
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* What to say to the model, if anything.
|
|
229
|
+
*
|
|
230
|
+
* Returns '' for a confident step. That empty string is the whole economic
|
|
231
|
+
* argument: reflecting on every step is what the gating literature exists to
|
|
232
|
+
* prevent, and the directive costs tokens on every step it appears in.
|
|
233
|
+
*
|
|
234
|
+
* @param {string} level
|
|
235
|
+
* @param {string[]} fired
|
|
236
|
+
* @returns {string}
|
|
237
|
+
*/
|
|
238
|
+
export function directiveFor(level, fired = []) {
|
|
239
|
+
if (level === 'confident') return '';
|
|
240
|
+
|
|
241
|
+
const because = fired.length ? ` (${fired.join(', ')})` : '';
|
|
242
|
+
|
|
243
|
+
if (level === 'stuck') {
|
|
244
|
+
// Paradigm shift: when VFE plateau occurs, standard re-planning isn't enough;
|
|
245
|
+
// the agent is stuck in an active-inference local minimum.
|
|
246
|
+
if (fired.includes('vfe_plateau')) {
|
|
247
|
+
return `[PARADIGM SHIFT REQUIRED — persistent error plateau with zero convergence${because}. `
|
|
248
|
+
+ 'Do NOT retry previous step variations or tweak command arguments. '
|
|
249
|
+
+ 'Invalidate your operational hypothesis, state in one line what fundamental assumption was wrong, '
|
|
250
|
+
+ 'and switch to a completely different tool, perspective, or strategy.]';
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// IARS separates [RETRY_SUBPROBLEM] from a full re-plan. A wrong ROUTE
|
|
254
|
+
// with no thrashing means this one sub-problem needs a different approach;
|
|
255
|
+
// thrashing means the plan itself is not converging.
|
|
256
|
+
const thrashing = fired.some((f) => f.startsWith('thrash:'));
|
|
257
|
+
if (!thrashing) {
|
|
258
|
+
return `[RETRY_SUBPROBLEM — the last approach to THIS step failed in a way that says the route was wrong${because}. `
|
|
259
|
+
+ 'Keep the overall plan. Solve this one step by a different route. '
|
|
260
|
+
+ 'If a tool reported "not found", that is evidence about the ROUTE, not proof the thing is absent.]';
|
|
261
|
+
}
|
|
262
|
+
return `[RE-PLAN — the last few steps are not converging${because}. `
|
|
263
|
+
+ 'Do NOT retry the same route. State in one line what you now believe is actually true, '
|
|
264
|
+
+ 'name the assumption that turned out to be wrong, and choose a different approach. '
|
|
265
|
+
+ 'If a tool reported "not found", that is evidence about the ROUTE, not proof the thing is absent.]';
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return `[CHECK YOURSELF — this step looks contested${because}. `
|
|
269
|
+
+ 'Before calling the tool, say in one line what you expect it to return. '
|
|
270
|
+
+ 'If the result contradicts that expectation, treat the expectation as the thing that was wrong.]';
|
|
271
|
+
}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { causyneHome } from './utils/paths.js';
|
|
7
|
+
|
|
8
|
+
// Bounds. Every one of these protects the context window, which is the resource
|
|
9
|
+
// a skill library actually spends.
|
|
10
|
+
export const MAX_USER_SKILLS = 200;
|
|
11
|
+
export const MAX_SKILL_BYTES = 32 * 1024;
|
|
12
|
+
export const MAX_DESCRIPTION = 400;
|
|
13
|
+
|
|
14
|
+
// A skill id and a domain are path segments. Lowercase, digits and hyphens is
|
|
15
|
+
// the whole alphabet — which makes traversal, drive letters, device names,
|
|
16
|
+
// leading dots and spaces unrepresentable rather than merely filtered.
|
|
17
|
+
const SEGMENT = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
18
|
+
|
|
19
|
+
// Windows reserves these as device names. A directory called `con` or `aux`
|
|
20
|
+
// cannot be created there, and `nul` silently discards what is written to it —
|
|
21
|
+
// so a skill named after one would work on the author's Mac and fail, or vanish,
|
|
22
|
+
// for a Windows user. Vision section 3: never assume the developer's own OS.
|
|
23
|
+
const RESERVED = new Set([
|
|
24
|
+
'con', 'prn', 'aux', 'nul',
|
|
25
|
+
...Array.from({ length: 9 }, (_, index) => `com${index + 1}`),
|
|
26
|
+
...Array.from({ length: 9 }, (_, index) => `lpt${index + 1}`)
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
export function userSkillsRoot() {
|
|
30
|
+
return path.join(causyneHome(), 'skills');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Is this a usable path segment? Returns a reason when it is not. */
|
|
34
|
+
export function validateSegment(value, what = 'name') {
|
|
35
|
+
const text = String(value == null ? '' : value);
|
|
36
|
+
if (!text) return `${what} is required`;
|
|
37
|
+
if (!SEGMENT.test(text)) {
|
|
38
|
+
return `${what} must be lowercase letters, digits and hyphens, starting with a letter or digit `
|
|
39
|
+
+ `(got ${JSON.stringify(text.slice(0, 40))})`;
|
|
40
|
+
}
|
|
41
|
+
if (RESERVED.has(text)) {
|
|
42
|
+
return `${what} ${JSON.stringify(text)} is a reserved device name on Windows — `
|
|
43
|
+
+ 'a skill by that name would fail there. Pick another.';
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Read `domain` and `name` out of a `domain/name` id.
|
|
50
|
+
*
|
|
51
|
+
* Rejects anything with a different shape rather than guessing, because a
|
|
52
|
+
* guessed split is how a traversal segment reaches a join().
|
|
53
|
+
*/
|
|
54
|
+
export function parseSkillId(id) {
|
|
55
|
+
const parts = String(id == null ? '' : id).split('/');
|
|
56
|
+
if (parts.length !== 2) {
|
|
57
|
+
return { error: 'skill id must be "domain/name", for example team/release-checklist' };
|
|
58
|
+
}
|
|
59
|
+
const [domain, name] = parts;
|
|
60
|
+
const domainError = validateSegment(domain, 'domain');
|
|
61
|
+
if (domainError) return { error: domainError };
|
|
62
|
+
const nameError = validateSegment(name, 'skill name');
|
|
63
|
+
if (nameError) return { error: nameError };
|
|
64
|
+
return { domain, name };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Strip control characters and collapse a description onto one line. */
|
|
68
|
+
function cleanDescription(value) {
|
|
69
|
+
return String(value == null ? '' : value)
|
|
70
|
+
// eslint-disable-next-line no-control-regex
|
|
71
|
+
.replace(/[\x00-\x1f\x7f]/g, ' ')
|
|
72
|
+
.replace(/\s+/g, ' ')
|
|
73
|
+
.trim()
|
|
74
|
+
.slice(0, MAX_DESCRIPTION);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Remove control bytes from a body while keeping newlines and tabs. */
|
|
78
|
+
function cleanBody(value) {
|
|
79
|
+
// eslint-disable-next-line no-control-regex
|
|
80
|
+
return String(value == null ? '' : value).replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, '');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function parseFrontmatter(content) {
|
|
84
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
85
|
+
if (!match) return {};
|
|
86
|
+
const block = match[1];
|
|
87
|
+
const name = block.match(/^name:\s*(.+)$/m);
|
|
88
|
+
const description = block.match(/^description:\s*(.+)$/m);
|
|
89
|
+
return {
|
|
90
|
+
name: name ? name[1].trim() : '',
|
|
91
|
+
description: description ? description[1].trim() : ''
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Every user skill on disk, validated.
|
|
97
|
+
*
|
|
98
|
+
* Never throws: a broken file is skipped and reported, because one bad skill
|
|
99
|
+
* must not stop a session from starting. Returns { skills, problems }.
|
|
100
|
+
*/
|
|
101
|
+
export function loadUserSkills(root = userSkillsRoot()) {
|
|
102
|
+
const skills = [];
|
|
103
|
+
const problems = [];
|
|
104
|
+
|
|
105
|
+
let domains;
|
|
106
|
+
try {
|
|
107
|
+
if (!fs.existsSync(root)) return { skills, problems };
|
|
108
|
+
domains = fs.readdirSync(root, { withFileTypes: true })
|
|
109
|
+
.filter((entry) => entry.isDirectory() && !entry.name.startsWith('.'))
|
|
110
|
+
.map((entry) => entry.name);
|
|
111
|
+
} catch (e) {
|
|
112
|
+
return { skills, problems: [`could not read ${root}: ${e.message}`] };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
for (const domain of domains) {
|
|
116
|
+
if (validateSegment(domain, 'domain')) {
|
|
117
|
+
problems.push(`skipped domain ${JSON.stringify(domain)}: not a valid name`);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
let names;
|
|
121
|
+
try {
|
|
122
|
+
names = fs.readdirSync(path.join(root, domain), { withFileTypes: true })
|
|
123
|
+
.filter((entry) => entry.isDirectory() && !entry.name.startsWith('.'))
|
|
124
|
+
.map((entry) => entry.name);
|
|
125
|
+
} catch (e) { continue; }
|
|
126
|
+
|
|
127
|
+
for (const name of names) {
|
|
128
|
+
if (skills.length >= MAX_USER_SKILLS) {
|
|
129
|
+
problems.push(`stopped at ${MAX_USER_SKILLS} skills — the rest were not loaded`);
|
|
130
|
+
return { skills, problems };
|
|
131
|
+
}
|
|
132
|
+
if (validateSegment(name, 'skill name')) {
|
|
133
|
+
problems.push(`skipped ${domain}/${name}: not a valid name`);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const file = path.join(root, domain, name, 'SKILL.md');
|
|
138
|
+
let content;
|
|
139
|
+
try {
|
|
140
|
+
// Refuse a symlink outright: it is the one way a file inside this
|
|
141
|
+
// directory can point at something outside it.
|
|
142
|
+
const stat = fs.lstatSync(file);
|
|
143
|
+
if (!stat.isFile()) { problems.push(`skipped ${domain}/${name}: not a regular file`); continue; }
|
|
144
|
+
if (stat.size > MAX_SKILL_BYTES) {
|
|
145
|
+
problems.push(`skipped ${domain}/${name}: ${(stat.size / 1024).toFixed(0)} KB exceeds the `
|
|
146
|
+
+ `${MAX_SKILL_BYTES / 1024} KB limit — a skill this large would crowd out the task`);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
content = fs.readFileSync(file, 'utf8');
|
|
150
|
+
} catch (e) { continue; }
|
|
151
|
+
|
|
152
|
+
const meta = parseFrontmatter(content);
|
|
153
|
+
const description = cleanDescription(meta.description);
|
|
154
|
+
if (!description) {
|
|
155
|
+
problems.push(`skipped ${domain}/${name}: no description in the frontmatter — `
|
|
156
|
+
+ 'the description is how the agent decides when the skill applies');
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
skills.push({
|
|
161
|
+
domain,
|
|
162
|
+
id: name,
|
|
163
|
+
name: meta.name || name,
|
|
164
|
+
description,
|
|
165
|
+
content: cleanBody(content),
|
|
166
|
+
source: 'user',
|
|
167
|
+
path: file
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return { skills, problems };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** The starting point a new skill is written from. */
|
|
175
|
+
export function skillTemplate(domain, name, description = '') {
|
|
176
|
+
const title = name.split('-').map((word) => word[0].toUpperCase() + word.slice(1)).join(' ');
|
|
177
|
+
return `---
|
|
178
|
+
name: ${name}
|
|
179
|
+
description: ${description || `TODO — one line: what this does, and WHEN to reach for it.`}
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
# ${title}
|
|
183
|
+
|
|
184
|
+
## Workflow
|
|
185
|
+
|
|
186
|
+
1. First step.
|
|
187
|
+
- What to do, and the detail that is easy to get wrong.
|
|
188
|
+
- **Done when:** something you can actually check.
|
|
189
|
+
|
|
190
|
+
2. Second step.
|
|
191
|
+
- **Done when:** something you can actually check.
|
|
192
|
+
|
|
193
|
+
## Verification
|
|
194
|
+
|
|
195
|
+
- How to confirm the whole thing worked.
|
|
196
|
+
`;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Write a user skill to disk.
|
|
201
|
+
*
|
|
202
|
+
* Refuses to overwrite unless asked, so neither a user nor an agent can silently
|
|
203
|
+
* replace a skill someone spent time on.
|
|
204
|
+
*/
|
|
205
|
+
export function writeUserSkill({ domain, name, content, overwrite = false }, root = userSkillsRoot()) {
|
|
206
|
+
const domainError = validateSegment(domain, 'domain');
|
|
207
|
+
if (domainError) return { ok: false, error: domainError };
|
|
208
|
+
const nameError = validateSegment(name, 'skill name');
|
|
209
|
+
if (nameError) return { ok: false, error: nameError };
|
|
210
|
+
|
|
211
|
+
const body = cleanBody(content);
|
|
212
|
+
if (Buffer.byteLength(body, 'utf8') > MAX_SKILL_BYTES) {
|
|
213
|
+
return { ok: false, error: `skill exceeds the ${MAX_SKILL_BYTES / 1024} KB limit` };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const meta = parseFrontmatter(body);
|
|
217
|
+
if (!cleanDescription(meta.description)) {
|
|
218
|
+
return {
|
|
219
|
+
ok: false,
|
|
220
|
+
error: "the frontmatter needs a `description:` line — it is what the agent reads to decide whether the skill applies, so a skill without one can never be selected"
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const directory = path.join(root, domain, name);
|
|
225
|
+
const file = path.join(directory, 'SKILL.md');
|
|
226
|
+
|
|
227
|
+
// Belt and braces: the resolved path must still be inside the root after every
|
|
228
|
+
// segment has been joined. Validation above already makes this unreachable.
|
|
229
|
+
const resolvedRoot = path.resolve(root);
|
|
230
|
+
const resolvedFile = path.resolve(file);
|
|
231
|
+
if (resolvedFile !== resolvedRoot && !resolvedFile.startsWith(resolvedRoot + path.sep)) {
|
|
232
|
+
return { ok: false, error: 'refusing to write outside the skills directory' };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (!overwrite && fs.existsSync(file)) {
|
|
236
|
+
return { ok: false, error: `${domain}/${name} already exists — pass overwrite to replace it` };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
fs.mkdirSync(directory, { recursive: true });
|
|
241
|
+
fs.writeFileSync(file, body, 'utf8');
|
|
242
|
+
} catch (e) {
|
|
243
|
+
return { ok: false, error: `could not write ${file}: ${e.message}` };
|
|
244
|
+
}
|
|
245
|
+
return { ok: true, path: file };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** Remove a user skill. Compiled skills are not removable — they are the build. */
|
|
249
|
+
export function deleteUserSkill(domain, name, root = userSkillsRoot()) {
|
|
250
|
+
const domainError = validateSegment(domain, 'domain');
|
|
251
|
+
if (domainError) return { ok: false, error: domainError };
|
|
252
|
+
const nameError = validateSegment(name, 'skill name');
|
|
253
|
+
if (nameError) return { ok: false, error: nameError };
|
|
254
|
+
|
|
255
|
+
const directory = path.join(root, domain, name);
|
|
256
|
+
if (!fs.existsSync(path.join(directory, 'SKILL.md'))) {
|
|
257
|
+
return { ok: false, error: `${domain}/${name} is not a user skill` };
|
|
258
|
+
}
|
|
259
|
+
try {
|
|
260
|
+
fs.rmSync(directory, { recursive: true, force: true });
|
|
261
|
+
} catch (e) {
|
|
262
|
+
return { ok: false, error: `could not remove ${directory}: ${e.message}` };
|
|
263
|
+
}
|
|
264
|
+
return { ok: true, path: directory };
|
|
265
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import rawInquirer from 'inquirer';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
const inquirer = rawInquirer?.prompt
|
|
5
|
+
? rawInquirer
|
|
6
|
+
: (rawInquirer?.default?.prompt
|
|
7
|
+
? rawInquirer.default
|
|
8
|
+
: (rawInquirer?.default?.default?.prompt
|
|
9
|
+
? rawInquirer.default.default
|
|
10
|
+
: rawInquirer));
|
|
11
|
+
|
|
12
|
+
export default inquirer;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import os from 'os';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Resolve a path the model supplied, expanding a leading `~`.
|
|
6
|
+
*
|
|
7
|
+
* WHY THIS EXISTS. `path.resolve('~/x')` does NOT expand the tilde — it treats
|
|
8
|
+
* `~` as an ordinary directory name and returns `<cwd>/~/x`. Models write `~/`
|
|
9
|
+
* constantly, because that is how paths are written everywhere they have ever
|
|
10
|
+
* read one, and because Causyne's own transcript SHOWS paths that way.
|
|
11
|
+
*
|
|
12
|
+
* It cost a real run: `view_image ~/Causyne/pen_cinematic/preview.png` failed in
|
|
13
|
+
* 17ms with "Image not found at /Users/example/project/~/project/..."
|
|
14
|
+
* on a file that existed — the agent had just rendered it and was trying to
|
|
15
|
+
* look at its own work, which is the one step that separates a good render from
|
|
16
|
+
* a bad one.
|
|
17
|
+
*
|
|
18
|
+
* @param {string} input A path, possibly starting with `~`.
|
|
19
|
+
* @returns {string} An absolute path.
|
|
20
|
+
*/
|
|
21
|
+
export function resolveUserPath(input) {
|
|
22
|
+
const raw = String(input ?? '').trim();
|
|
23
|
+
if (raw === '~') return os.homedir();
|
|
24
|
+
if (raw.startsWith('~/') || raw.startsWith('~\\')) {
|
|
25
|
+
return path.resolve(path.join(os.homedir(), raw.slice(2)));
|
|
26
|
+
}
|
|
27
|
+
return path.resolve(raw);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
const STATE_ROOT = process.env.CAUSYNE_HOME && String(process.env.CAUSYNE_HOME).trim()
|
|
32
|
+
? path.resolve(String(process.env.CAUSYNE_HOME).trim())
|
|
33
|
+
: path.join(os.homedir(), '.causyne');
|
|
34
|
+
|
|
35
|
+
export function causyneHome() {
|
|
36
|
+
return STATE_ROOT;
|
|
37
|
+
}
|
package/src/version.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
function getDir() {
|
|
5
|
+
if (typeof __dirname !== 'undefined') {
|
|
6
|
+
return __dirname;
|
|
7
|
+
}
|
|
8
|
+
try {
|
|
9
|
+
const metaUrl = typeof import.meta !== 'undefined' && import.meta.url ? import.meta.url : null;
|
|
10
|
+
if (metaUrl && typeof metaUrl === 'string') {
|
|
11
|
+
const url = new URL(metaUrl);
|
|
12
|
+
if (url.protocol === 'file:') {
|
|
13
|
+
return path.dirname(url.pathname);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
} catch (e) {}
|
|
17
|
+
return process.cwd();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getPackageVersion() {
|
|
21
|
+
try {
|
|
22
|
+
const dir = getDir();
|
|
23
|
+
const pkgPath = path.resolve(dir, '../package.json');
|
|
24
|
+
if (fs.existsSync(pkgPath)) {
|
|
25
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
26
|
+
if (pkg && typeof pkg.version === 'string' && pkg.version.trim()) {
|
|
27
|
+
return pkg.version.trim();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
} catch (e) {}
|
|
31
|
+
|
|
32
|
+
if (typeof __PACKAGE_VERSION__ !== 'undefined' && __PACKAGE_VERSION__ && __PACKAGE_VERSION__ !== '1.1.41') {
|
|
33
|
+
return __PACKAGE_VERSION__;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return '1.3.14';
|
|
37
|
+
}
|
|
38
|
+
|