@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
package/src/reporting.js
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
// Local terminal reports for usage, context, checkpoints and status.
|
|
2
|
+
|
|
3
|
+
import { TOOL_DEFINITIONS } from './tools/registry.js';
|
|
4
|
+
import { CORE_TOOLS, PACKS, PACK_PLAYBOOKS } from './toolPacks.js';
|
|
5
|
+
import { resolveCharPool } from './contextManager.js';
|
|
6
|
+
import { TokenTracker } from './tokenTracker.js';
|
|
7
|
+
import { loadConfig } from './config.js';
|
|
8
|
+
import { listSpills, getOutputDir } from './outputStore.js';
|
|
9
|
+
import { listGroups, undoLast, undoTask, undoStep, getStoreStats } from './checkpoints.js';
|
|
10
|
+
import { panel } from './tui/block.js';
|
|
11
|
+
import { terminalWidth, readableWidth } from './tui/theme.js';
|
|
12
|
+
|
|
13
|
+
// The same divisor scripts/measure_context.js uses, so the CLI and the dev
|
|
14
|
+
// harness never disagree about what a token costs.
|
|
15
|
+
const APPROX_CHARS_PER_TOKEN = 4;
|
|
16
|
+
|
|
17
|
+
function tokensOf(value) {
|
|
18
|
+
const text = typeof value === 'string' ? value : JSON.stringify(value);
|
|
19
|
+
return Math.round(text.length / APPROX_CHARS_PER_TOKEN);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function formatBytes(bytes) {
|
|
23
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
24
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
25
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function formatNumber(value) {
|
|
29
|
+
return Number(value || 0).toLocaleString('en-US');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function formatAge(ts) {
|
|
33
|
+
const seconds = Math.max(0, Math.floor((Date.now() - ts) / 1000));
|
|
34
|
+
if (seconds < 60) return `${seconds}s ago`;
|
|
35
|
+
if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
|
|
36
|
+
if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`;
|
|
37
|
+
return `${Math.floor(seconds / 86400)}d ago`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function schemaTokens(names) {
|
|
41
|
+
const set = new Set(names);
|
|
42
|
+
return TOOL_DEFINITIONS
|
|
43
|
+
.filter(tool => set.has(tool.name))
|
|
44
|
+
.reduce((sum, tool) => sum + tokensOf(tool), 0);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
// Context accounting
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
export function buildContextReport() {
|
|
52
|
+
const config = loadConfig();
|
|
53
|
+
const tier = config.tier || 'premium';
|
|
54
|
+
const charPool = resolveCharPool(tier, config.contextCharPool);
|
|
55
|
+
|
|
56
|
+
const packs = Object.entries(PACKS).map(([name, tools]) => ({
|
|
57
|
+
name,
|
|
58
|
+
tools: tools.length,
|
|
59
|
+
tokens: schemaTokens(tools),
|
|
60
|
+
playbookTokens: PACK_PLAYBOOKS[name] ? tokensOf(PACK_PLAYBOOKS[name]) : 0
|
|
61
|
+
}));
|
|
62
|
+
|
|
63
|
+
const spills = listSpills(1000);
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
tier,
|
|
67
|
+
provider: config.provider || 'default',
|
|
68
|
+
model: config.model || 'default',
|
|
69
|
+
charPool,
|
|
70
|
+
approxTokenPool: Math.round(charPool / APPROX_CHARS_PER_TOKEN),
|
|
71
|
+
core: { tools: CORE_TOOLS.length, tokens: schemaTokens(CORE_TOOLS) },
|
|
72
|
+
packs,
|
|
73
|
+
allTools: TOOL_DEFINITIONS.length,
|
|
74
|
+
allToolTokens: TOOL_DEFINITIONS.reduce((sum, tool) => sum + tokensOf(tool), 0),
|
|
75
|
+
spill: {
|
|
76
|
+
dir: getOutputDir(),
|
|
77
|
+
files: spills.length,
|
|
78
|
+
bytes: spills.reduce((sum, item) => sum + item.bytes, 0),
|
|
79
|
+
newest: spills.length > 0 ? new Date(spills[0].modified).toISOString() : null
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function buildContextText() {
|
|
85
|
+
const report = buildContextReport();
|
|
86
|
+
const lines = [];
|
|
87
|
+
|
|
88
|
+
lines.push('Context accounting');
|
|
89
|
+
lines.push(` Provider / model : ${report.provider} / ${report.model}`);
|
|
90
|
+
lines.push(` Tier : ${report.tier}`);
|
|
91
|
+
lines.push(` Window pool : ${formatNumber(report.charPool)} chars (~${formatNumber(report.approxTokenPool)} tokens)`);
|
|
92
|
+
lines.push('');
|
|
93
|
+
lines.push('Tool schemas in context');
|
|
94
|
+
lines.push(` core (every step): ${String(report.core.tools).padStart(3)} tools ${String(formatNumber(report.core.tokens)).padStart(7)} tok`);
|
|
95
|
+
for (const pack of report.packs) {
|
|
96
|
+
const playbook = pack.playbookTokens > 0 ? ` + ${formatNumber(pack.playbookTokens)} tok playbook` : '';
|
|
97
|
+
lines.push(` ${pack.name.padEnd(16)}: ${String(pack.tools).padStart(3)} tools ${String(formatNumber(pack.tokens)).padStart(7)} tok${playbook}`);
|
|
98
|
+
}
|
|
99
|
+
lines.push(` ${'all'.padEnd(16)}: ${String(report.allTools).padStart(3)} tools ${String(formatNumber(report.allToolTokens)).padStart(7)} tok (only unlocked packs are ever sent)`);
|
|
100
|
+
lines.push('');
|
|
101
|
+
lines.push('Spilled tool output');
|
|
102
|
+
lines.push(` Directory : ${report.spill.dir}`);
|
|
103
|
+
lines.push(` Saved outputs : ${report.spill.files} file(s), ${formatBytes(report.spill.bytes)}`);
|
|
104
|
+
lines.push(' These hold the full text of results too large for the window, so the');
|
|
105
|
+
lines.push(' agent can read them back instead of re-running the tool. Kept 7 days.');
|
|
106
|
+
|
|
107
|
+
return lines.join('\n');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function printContextReport() {
|
|
111
|
+
printReport(buildContextText(), { title: 'Context accounting' });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
// Token usage
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
|
|
118
|
+
export async function buildUsageText() {
|
|
119
|
+
const tracker = new TokenTracker();
|
|
120
|
+
const usage = await tracker.getReportedUsage();
|
|
121
|
+
const config = loadConfig();
|
|
122
|
+
const budget = config.tokenBudget || {};
|
|
123
|
+
const lines = [];
|
|
124
|
+
|
|
125
|
+
lines.push('Token usage (BYOK — billed by your provider, not by Causyne)');
|
|
126
|
+
lines.push(` Provider / model : ${config.provider || 'default'} / ${config.model || 'default'}`);
|
|
127
|
+
lines.push(` This task : ${formatNumber(usage.taskTokens)} tokens $${Number(usage.taskCostUsd || 0).toFixed(4)}`);
|
|
128
|
+
lines.push(` Today : ${formatNumber(usage.dailyTokens)} tokens $${Number(usage.costUsd || 0).toFixed(4)}`);
|
|
129
|
+
lines.push(` Lifetime : ${formatNumber(usage.lifetimeTokens)} tokens $${Number(usage.lifetimeCostUsd || 0).toFixed(4)}`);
|
|
130
|
+
|
|
131
|
+
if (budget.enabled) {
|
|
132
|
+
const limit = budget.maxTokensPerDay || 0;
|
|
133
|
+
const pct = limit > 0 ? ((usage.dailyTokens / limit) * 100).toFixed(1) : '0.0';
|
|
134
|
+
lines.push(` Daily budget : ${formatNumber(limit)} tokens (${pct}% used)`);
|
|
135
|
+
if (budget.maxCostUsd) {
|
|
136
|
+
lines.push(` Cost ceiling : $${Number(budget.maxCostUsd).toFixed(2)}${budget.hardStop ? ' (hard stop)' : ''}`);
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
lines.push(' Daily budget : disabled');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return lines.join('\n');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export async function printUsageReport() {
|
|
146
|
+
printReport(await buildUsageText(), { title: 'Token usage' });
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
// Checkpoints
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
|
|
153
|
+
export function buildCheckpointsText(limit = 15) {
|
|
154
|
+
const groups = listGroups(limit);
|
|
155
|
+
const stats = getStoreStats();
|
|
156
|
+
const lines = [];
|
|
157
|
+
|
|
158
|
+
lines.push('Checkpoints');
|
|
159
|
+
lines.push(` Store: ${stats.dir} (${stats.entries} entries, ${formatBytes(stats.bytes)})`);
|
|
160
|
+
lines.push('');
|
|
161
|
+
|
|
162
|
+
if (groups.length === 0) {
|
|
163
|
+
lines.push(' No checkpoints recorded yet. They are captured automatically before');
|
|
164
|
+
lines.push(' the agent writes, edits, moves or deletes a file.');
|
|
165
|
+
return lines.join('\n');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
for (const group of groups) {
|
|
169
|
+
const label = group.taskId.startsWith('undo_') ? ' (undo restore point)' : '';
|
|
170
|
+
lines.push(` ${group.taskId} ${formatAge(group.ts)}${label}`);
|
|
171
|
+
if (group.prompt) lines.push(` task : ${group.prompt}`);
|
|
172
|
+
lines.push(` files : ${group.fileEntries.length} change(s) checkpointed`);
|
|
173
|
+
lines.push(` history : ${group.hasHistory ? 'snapshotted (undo rewinds the conversation too)' : 'not captured'}`);
|
|
174
|
+
|
|
175
|
+
for (const tree of group.trees) {
|
|
176
|
+
lines.push(tree.skipped
|
|
177
|
+
? ` tree : ${tree.root} — SKIPPED (${tree.skipped})`
|
|
178
|
+
: ` tree : ${tree.root} — ${tree.fileCount} files, ${formatBytes(tree.totalBytes)}`);
|
|
179
|
+
}
|
|
180
|
+
for (const entry of group.fileEntries.slice(0, 6)) {
|
|
181
|
+
const verb = entry.before === null ? 'created' : 'modified';
|
|
182
|
+
lines.push(` ${entry.tool.padEnd(10)} ${verb.padEnd(9)} ${entry.path}`);
|
|
183
|
+
}
|
|
184
|
+
if (group.fileEntries.length > 6) {
|
|
185
|
+
lines.push(` ... and ${group.fileEntries.length - 6} more`);
|
|
186
|
+
}
|
|
187
|
+
lines.push('');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
lines.push(' Roll back with: undo (the last task)');
|
|
191
|
+
lines.push(' undo --step (just the last file change)');
|
|
192
|
+
lines.push(' undo <taskId> (a specific task)');
|
|
193
|
+
lines.push(' Preview first: undo --dry-run');
|
|
194
|
+
|
|
195
|
+
return lines.join('\n');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function printCheckpoints(limit = 15) {
|
|
199
|
+
printReport(buildCheckpointsText(limit), { title: 'Checkpoints' });
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
// Undo
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Perform the requested rollback and hand back the RAW result.
|
|
208
|
+
*
|
|
209
|
+
* Split out of buildUndoText because there are now two consumers and a restore
|
|
210
|
+
* must happen exactly once per request. Calling buildUndoText and then
|
|
211
|
+
* buildUndoPayload for the same request would roll back twice — the second call
|
|
212
|
+
* would "restore" the state the first call just wrote. So the dispatch lives
|
|
213
|
+
* here, in one place, and each surface renders the single result it is given.
|
|
214
|
+
*/
|
|
215
|
+
function runUndo(taskId, options = {}) {
|
|
216
|
+
const opts = { dryRun: options.dryRun, full: options.full };
|
|
217
|
+
return options.step
|
|
218
|
+
? undoStep({ dryRun: options.dryRun })
|
|
219
|
+
: (taskId ? undoTask(taskId, opts) : undoLast(opts));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function buildUndoText(taskId, options = {}) {
|
|
223
|
+
const result = runUndo(taskId, options);
|
|
224
|
+
if (!result.ok) return { ok: false, text: `[UNDO] ${result.error}` };
|
|
225
|
+
return renderUndo(result);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** Render a completed undo result as the plain text both terminals expect. */
|
|
229
|
+
function renderUndo(result) {
|
|
230
|
+
const lines = [];
|
|
231
|
+
lines.push(`${result.dryRun ? 'Undo preview (nothing written)' : 'Undo'}${result.taskId ? ` — task ${result.taskId}` : ''}`);
|
|
232
|
+
lines.push('');
|
|
233
|
+
|
|
234
|
+
if (result.results.length === 0) {
|
|
235
|
+
// Distinguish "nothing was watched" from "nothing changed". They look the
|
|
236
|
+
// same from here and mean opposite things: the first is a gap in coverage,
|
|
237
|
+
// the second is a clean answer.
|
|
238
|
+
const drifted = (result.drift || []).reduce((total, item) => total + item.files.length, 0);
|
|
239
|
+
if (drifted > 0) {
|
|
240
|
+
lines.push(' No tool announced a file change, but the workspace tree did move —');
|
|
241
|
+
lines.push(' see below. That is what a terminal command looks like to undo.');
|
|
242
|
+
} else if ((result.skippedTrees || []).length > 0) {
|
|
243
|
+
lines.push(' Nothing was checkpointed for this task: the workspace tree could not be');
|
|
244
|
+
lines.push(' snapshotted, so changes made here are not covered.');
|
|
245
|
+
} else {
|
|
246
|
+
lines.push(' Nothing to roll back — this task changed no files.');
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
let failures = 0;
|
|
251
|
+
for (const item of result.results) {
|
|
252
|
+
if (String(item.action).startsWith('FAILED')) failures++;
|
|
253
|
+
lines.push(` ${item.action} ${item.path}`);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (!result.dryRun) {
|
|
257
|
+
if (result.historyRestored) {
|
|
258
|
+
lines.push('');
|
|
259
|
+
lines.push(' Conversation rewound to the state it had when the task began.');
|
|
260
|
+
} else if (result.historyAvailable) {
|
|
261
|
+
lines.push('');
|
|
262
|
+
lines.push(' Conversation could NOT be rewound — files and chat history may now disagree.');
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
for (const item of result.drift || []) {
|
|
267
|
+
lines.push('');
|
|
268
|
+
lines.push(` ${item.files.length} file(s) under ${item.root} also changed during this task`);
|
|
269
|
+
lines.push(' but were not announced by a tool (typically a terminal command):');
|
|
270
|
+
for (const file of item.files.slice(0, 8)) lines.push(` ${file}`);
|
|
271
|
+
if (item.files.length > 8) lines.push(` ... and ${item.files.length - 8} more`);
|
|
272
|
+
lines.push(' Add --full to roll those back too (this also reverts edits you made');
|
|
273
|
+
lines.push(' by hand while the task ran, so review the list first).');
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
for (const tree of result.skippedTrees || []) {
|
|
277
|
+
lines.push('');
|
|
278
|
+
lines.push(` Note: the workspace tree at ${tree.root} was not snapshotted (${tree.reason}),`);
|
|
279
|
+
lines.push(' so changes made there by terminal commands are not covered by this undo.');
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (!result.dryRun) {
|
|
283
|
+
lines.push('');
|
|
284
|
+
lines.push(' This undo was itself checkpointed — run `checkpoints` to reverse it.');
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return { ok: failures === 0, text: lines.join('\n') };
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export function printUndo(taskId, options = {}) {
|
|
291
|
+
const { ok, text } = buildUndoText(taskId, options);
|
|
292
|
+
printReport(text, { title: 'Undo' });
|
|
293
|
+
return ok;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** `3d 4h 12m` — uptime a human reads at a glance. */
|
|
297
|
+
export function formatUptime(ms) {
|
|
298
|
+
const totalSeconds = Math.floor(ms / 1000);
|
|
299
|
+
const days = Math.floor(totalSeconds / 86400);
|
|
300
|
+
const hours = Math.floor((totalSeconds % 86400) / 3600);
|
|
301
|
+
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
302
|
+
const seconds = totalSeconds % 60;
|
|
303
|
+
if (days > 0) return `${days}d ${hours}h ${minutes}m`;
|
|
304
|
+
if (hours > 0) return `${hours}h ${minutes}m`;
|
|
305
|
+
if (minutes > 0) return `${minutes}m ${seconds}s`;
|
|
306
|
+
return `${seconds}s`;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Local runtime status.
|
|
311
|
+
*
|
|
312
|
+
* @param {object} opts
|
|
313
|
+
* @param {object|null} opts.loop the live AgentLoop, for workspace scope
|
|
314
|
+
*/
|
|
315
|
+
export function buildStatusText({ loop = null } = {}) {
|
|
316
|
+
const config = loadConfig();
|
|
317
|
+
const lines = ['Status'];
|
|
318
|
+
|
|
319
|
+
lines.push(' Runtime : local desktop');
|
|
320
|
+
|
|
321
|
+
lines.push(` Provider / model : ${config.provider || 'default'} / ${config.model || 'default'}`);
|
|
322
|
+
lines.push(` Autonomy mode : ${config.autonomyMode || 'C'}`);
|
|
323
|
+
lines.push(` Tier : ${config.tier || 'premium'}`);
|
|
324
|
+
|
|
325
|
+
if (loop && loop.sandbox) {
|
|
326
|
+
const sandbox = loop.sandbox;
|
|
327
|
+
lines.push(` Workspace scope : ${sandbox.grantAllAccess ? 'ALL ACCESS' : (sandbox.workspacePaths || []).join(', ')}`);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (!config.apiKey && config.provider !== 'ollama') {
|
|
331
|
+
lines.push('');
|
|
332
|
+
lines.push(' No API key is set. Run `causyne config`.');
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return lines.join('\n');
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// ---------------------------------------------------------------------------
|
|
339
|
+
// One report, two renderings
|
|
340
|
+
//
|
|
341
|
+
// Same split the undo payloads already use: the builders above produce plain
|
|
342
|
+
// text, and this decides how it reaches the surface. A terminal gets a framed
|
|
343
|
+
// panel wrapped to a readable measure; a pipe gets the bytes and nothing else,
|
|
344
|
+
// because `causyne usage | grep` must not have to strip a box.
|
|
345
|
+
// ---------------------------------------------------------------------------
|
|
346
|
+
|
|
347
|
+
/** True when stdout is a terminal a panel would look right in. */
|
|
348
|
+
export function canDrawPanel() {
|
|
349
|
+
return Boolean(process.stdout && process.stdout.isTTY);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Render a report for whichever surface is listening.
|
|
354
|
+
*
|
|
355
|
+
* The panel is measured with readableWidth() rather than the full terminal:
|
|
356
|
+
* these reports are prose-shaped, and a 220-column line loses the eye's return
|
|
357
|
+
* sweep. Chrome follows the terminal, text does not.
|
|
358
|
+
*/
|
|
359
|
+
export function renderReport(text, { title = '', force = null } = {}) {
|
|
360
|
+
const draw = force === null ? canDrawPanel() : force;
|
|
361
|
+
if (!draw) return String(text);
|
|
362
|
+
const width = Math.min(readableWidth() + 4, terminalWidth());
|
|
363
|
+
const rows = String(text).split('\n');
|
|
364
|
+
// The builder already put the report name on row 0; the panel title would say
|
|
365
|
+
// it a second time.
|
|
366
|
+
const body = title && rows[0] === title ? rows.slice(1) : rows;
|
|
367
|
+
return panel(body, { width, title: title || undefined }).join('\n');
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/** Print a report to stdout, framed when a terminal is watching. */
|
|
371
|
+
export function printReport(text, options = {}) {
|
|
372
|
+
console.log('\n' + renderReport(text, options) + '\n');
|
|
373
|
+
}
|