@stackmemoryai/stackmemory 1.10.5 → 1.14.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 +131 -64
- package/README.md +107 -24
- package/bin/claude-sm +16 -1
- package/bin/claude-smd +16 -1
- package/bin/codex-smd +16 -1
- package/bin/gemini-sm +16 -1
- package/bin/hermes-sm +21 -0
- package/bin/hermes-smd +21 -0
- package/bin/opencode-sm +16 -1
- package/dist/src/cli/claude-sm.js +266 -84
- package/dist/src/cli/codex-sm.js +225 -33
- package/dist/src/cli/commands/bench.js +209 -2
- package/dist/src/cli/commands/brain.js +206 -0
- package/dist/src/cli/commands/cache.js +126 -0
- package/dist/src/cli/commands/company-os.js +184 -0
- package/dist/src/cli/commands/context.js +5 -0
- package/dist/src/cli/commands/daemon.js +41 -0
- package/dist/src/cli/commands/handoff.js +40 -9
- package/dist/src/cli/commands/onboard.js +70 -3
- package/dist/src/cli/commands/operator.js +127 -0
- package/dist/src/cli/commands/optimize.js +117 -0
- package/dist/src/cli/commands/orchestrate.js +232 -5
- package/dist/src/cli/commands/orchestrator.js +315 -26
- package/dist/src/cli/commands/pack.js +322 -0
- package/dist/src/cli/commands/patterns.js +254 -0
- package/dist/src/cli/commands/portal.js +161 -0
- package/dist/src/cli/commands/scaffold.js +92 -0
- package/dist/src/cli/commands/search.js +40 -1
- package/dist/src/cli/commands/setup.js +178 -11
- package/dist/src/cli/commands/skills.js +10 -1
- package/dist/src/cli/commands/state.js +265 -0
- package/dist/src/cli/commands/sync.js +253 -0
- package/dist/src/cli/commands/tasks.js +130 -1
- package/dist/src/cli/commands/vision.js +221 -0
- package/dist/src/cli/gemini-sm.js +19 -29
- package/dist/src/cli/hermes-sm.js +224 -0
- package/dist/src/cli/index.js +105 -39
- package/dist/src/cli/opencode-sm.js +38 -21
- package/dist/src/cli/utils/determinism-watcher.js +66 -0
- package/dist/src/cli/utils/real-cli-bin.js +116 -0
- package/dist/src/core/brain/brain-store.js +187 -0
- package/dist/src/core/brain/brain-sync.js +193 -0
- package/dist/src/core/brain/index.js +78 -0
- package/dist/src/core/brain/types.js +10 -0
- package/dist/src/core/cache/content-cache.js +238 -0
- package/dist/src/{integrations/diffmem → core/cache}/index.js +5 -5
- package/dist/src/core/cache/token-estimator.js +39 -0
- package/dist/src/core/config/feature-flags.js +2 -6
- package/dist/src/core/context/frame-database.js +79 -27
- package/dist/src/core/context/recursive-context-manager.js +1 -1
- package/dist/src/core/context/rehydration.js +2 -1
- package/dist/src/core/cross-search/cross-project-search.js +269 -0
- package/dist/src/core/cross-search/index.js +10 -0
- package/dist/src/core/database/sqlite-adapter.js +14 -84
- package/dist/src/core/extensions/provider-adapter.js +5 -0
- package/dist/src/core/models/model-router.js +54 -2
- package/dist/src/core/models/provider-pricing.js +58 -4
- package/dist/src/core/monitoring/logger.js +2 -1
- package/dist/src/core/optimization/trace-optimizer.js +413 -0
- package/dist/src/core/patterns/index.js +22 -0
- package/dist/src/core/patterns/pattern-applier.js +39 -0
- package/dist/src/core/patterns/pattern-observer.js +157 -0
- package/dist/src/core/patterns/pattern-store.js +259 -0
- package/dist/src/core/patterns/types.js +19 -0
- package/dist/src/core/provenance/confidence-scorer.js +128 -0
- package/dist/src/core/provenance/index.js +40 -0
- package/dist/src/core/provenance/provenance-store.js +194 -0
- package/dist/src/core/provenance/types.js +82 -0
- package/dist/src/core/retrieval/llm-context-retrieval.js +5 -4
- package/dist/src/core/retrieval/unified-context-assembler.js +11 -66
- package/dist/src/core/session/project-handoff.js +64 -0
- package/dist/src/core/session/session-manager.js +28 -0
- package/dist/src/core/shared-state/canonical-store.js +564 -0
- package/dist/src/core/skill-packs/index.js +18 -0
- package/dist/src/core/skill-packs/parser.js +42 -0
- package/dist/src/core/skill-packs/registry.js +224 -0
- package/dist/src/core/skill-packs/types.js +79 -0
- package/dist/src/core/storage/cloud-sync-manager.js +116 -0
- package/dist/src/core/storage/cloud-sync.js +574 -0
- package/dist/src/core/storage/two-tier-storage.js +5 -1
- package/dist/src/core/tasks/master-tasks-template.js +43 -0
- package/dist/src/core/tasks/md-task-parser.js +138 -0
- package/dist/src/core/trace/trace-event-store.js +282 -0
- package/dist/src/core/vision/index.js +27 -0
- package/dist/src/core/vision/signals.js +79 -0
- package/dist/src/core/vision/types.js +22 -0
- package/dist/src/core/vision/vision-file.js +146 -0
- package/dist/src/core/vision/vision-loop.js +220 -0
- package/dist/src/core/wiki/wiki-compiler.js +103 -1
- package/dist/src/daemon/daemon-config.js +52 -0
- package/dist/src/daemon/services/desire-path-service.js +566 -0
- package/dist/src/daemon/services/github-service.js +126 -0
- package/dist/src/daemon/services/research-stream-service.js +320 -0
- package/dist/src/daemon/services/telemetry-service.js +192 -0
- package/dist/src/daemon/unified-daemon.js +58 -1
- package/dist/src/features/browser/cli-browser-agent.js +417 -0
- package/dist/src/features/browser/stagehand-workflows.js +578 -0
- package/dist/src/features/operator/adapter-factory.js +62 -0
- package/dist/src/features/operator/browser-adapter.js +109 -0
- package/dist/src/features/operator/desktop-adapter.js +125 -0
- package/dist/src/features/operator/index.js +39 -0
- package/dist/src/features/operator/llm-decision.js +137 -0
- package/dist/src/features/operator/operator-logger.js +92 -0
- package/dist/src/features/operator/overnight-runner.js +327 -0
- package/dist/src/features/operator/screen-adapter.js +91 -0
- package/dist/src/features/operator/session-manager.js +127 -0
- package/dist/src/features/operator/state-machine.js +227 -0
- package/dist/src/features/operator/task-queue.js +81 -0
- package/dist/src/features/portal/index.js +26 -0
- package/dist/src/features/portal/server.js +240 -0
- package/dist/src/features/portal/types.js +14 -0
- package/dist/src/features/portal/ui.js +195 -0
- package/dist/src/features/sweep/pty-wrapper.js +13 -5
- package/dist/src/features/tasks/task-aware-context.js +2 -1
- package/dist/src/features/tui/simple-monitor.js +0 -23
- package/dist/src/features/tui/swarm-monitor.js +8 -66
- package/dist/src/features/web/client/hooks/use-socket.js +12 -0
- package/dist/src/{core/merge/index.js → features/web/client/lib/utils.js} +8 -4
- package/dist/src/features/web/client/next-env.d.js +4 -0
- package/dist/src/features/web/client/stores/session-store.js +12 -0
- package/dist/src/features/web/server/gcp-billing.js +76 -0
- package/dist/src/features/web/server/index.js +10 -0
- package/dist/src/features/web/server/spend-calculator.js +228 -0
- package/dist/src/hooks/schemas.js +6 -1
- package/dist/src/integrations/anthropic/client.js +3 -2
- package/dist/src/integrations/claude-code/agent-bridge.js +0 -3
- package/dist/src/integrations/claude-code/subagent-client.js +307 -11
- package/dist/src/integrations/claude-code/task-coordinator.js +2 -1
- package/dist/src/integrations/github/pr-state.js +158 -0
- package/dist/src/integrations/linear/client.js +4 -1
- package/dist/src/integrations/linear/webhook-retry.js +196 -0
- package/dist/src/integrations/linear/webhook-server.js +18 -22
- package/dist/src/integrations/mcp/handlers/cloud-sync-handlers.js +101 -0
- package/dist/src/integrations/mcp/handlers/index.js +40 -84
- package/dist/src/integrations/mcp/server.js +542 -641
- package/dist/src/integrations/mcp/tool-alias-registry.js +297 -0
- package/dist/src/integrations/mcp/tool-definitions.js +152 -682
- package/dist/src/mcp/stackmemory-mcp-server.js +571 -231
- package/dist/src/orchestrators/multimodal/determinism.js +244 -0
- package/dist/src/orchestrators/multimodal/harness.js +149 -78
- package/dist/src/orchestrators/multimodal/providers.js +44 -3
- package/dist/src/skills/recursive-agent-orchestrator.js +2 -4
- package/dist/src/utils/hook-installer.js +0 -8
- package/dist/src/utils/process-cleanup.js +1 -7
- package/docs/README.md +42 -0
- package/docs/guides/README_INSTALL.md +208 -0
- package/package.json +27 -9
- package/packs/coding/python-fastapi/instructions.md +60 -0
- package/packs/coding/python-fastapi/pack.yaml +28 -0
- package/packs/coding/typescript-react/instructions.md +47 -0
- package/packs/coding/typescript-react/pack.yaml +28 -0
- package/packs/core/commands/capture.md +32 -0
- package/packs/core/commands/learn.md +73 -0
- package/packs/core/commands/next.md +36 -0
- package/packs/core/commands/restart.md +58 -0
- package/packs/core/commands/restore.md +29 -0
- package/packs/core/commands/start.md +57 -0
- package/packs/core/commands/stop.md +65 -0
- package/packs/core/commands/summary.md +40 -0
- package/packs/core/manifest.json +24 -0
- package/packs/ops/decision-recovery/instructions.md +65 -0
- package/packs/ops/decision-recovery/pack.yaml +89 -0
- package/scripts/claude-code-wrapper.sh +11 -0
- package/scripts/claude-sm-setup.sh +12 -1
- package/scripts/codex-wrapper.sh +11 -0
- package/scripts/git-hooks/branch-context-manager.sh +11 -0
- package/scripts/git-hooks/post-checkout-stackmemory.sh +11 -0
- package/scripts/git-hooks/post-commit-stackmemory.sh +11 -0
- package/scripts/git-hooks/pre-commit-stackmemory.sh +11 -0
- package/scripts/hooks/cleanup-shell.sh +12 -1
- package/scripts/hooks/task-complete.sh +12 -1
- package/scripts/install-code-execution-hooks.sh +12 -1
- package/scripts/install-sweep-hook.sh +12 -0
- package/scripts/install.sh +11 -0
- package/scripts/opencode-wrapper.sh +11 -0
- package/scripts/portal/cloud-init.yaml +69 -0
- package/scripts/portal/setup.sh +69 -0
- package/scripts/portal/stackmemory-portal.service +34 -0
- package/scripts/setup-claude-integration.sh +12 -1
- package/scripts/smoke-init-db.sh +23 -0
- package/scripts/stackmemory-daemon.sh +11 -0
- package/scripts/verify-dist.cjs +11 -4
- package/dist/src/cli/commands/ralph.js +0 -1053
- package/dist/src/cli/commands/team.js +0 -168
- package/dist/src/core/context/shared-context-layer.js +0 -620
- package/dist/src/core/context/stack-merge-resolver.js +0 -748
- package/dist/src/core/merge/conflict-detector.js +0 -430
- package/dist/src/core/merge/resolution-engine.js +0 -557
- package/dist/src/core/merge/stack-diff.js +0 -531
- package/dist/src/core/merge/unified-merge-resolver.js +0 -302
- package/dist/src/hooks/diffmem-hooks.js +0 -376
- package/dist/src/integrations/diffmem/client.js +0 -208
- package/dist/src/integrations/diffmem/config.js +0 -14
- package/dist/src/integrations/greptile/client.js +0 -101
- package/dist/src/integrations/greptile/config.js +0 -14
- package/dist/src/integrations/greptile/index.js +0 -11
- package/dist/src/integrations/mcp/handlers/cord-handlers.js +0 -397
- package/dist/src/integrations/mcp/handlers/diffmem-handlers.js +0 -455
- package/dist/src/integrations/mcp/handlers/greptile-handlers.js +0 -456
- package/dist/src/integrations/mcp/handlers/provider-handlers.js +0 -227
- package/dist/src/integrations/mcp/handlers/team-handlers.js +0 -211
- package/dist/src/integrations/ralph/bridge/ralph-stackmemory-bridge.js +0 -863
- package/dist/src/integrations/ralph/context/context-budget-manager.js +0 -308
- package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +0 -391
- package/dist/src/integrations/ralph/index.js +0 -17
- package/dist/src/integrations/ralph/learning/pattern-learner.js +0 -435
- package/dist/src/integrations/ralph/lifecycle/iteration-lifecycle.js +0 -448
- package/dist/src/integrations/ralph/loopmax.js +0 -488
- package/dist/src/integrations/ralph/monitoring/swarm-dashboard.js +0 -293
- package/dist/src/integrations/ralph/monitoring/swarm-registry.js +0 -107
- package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +0 -508
- package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +0 -407
- package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +0 -495
- package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js +0 -387
- package/dist/src/integrations/ralph/performance/performance-optimizer.js +0 -357
- package/dist/src/integrations/ralph/recovery/crash-recovery.js +0 -461
- package/dist/src/integrations/ralph/state/state-reconciler.js +0 -420
- package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +0 -444
- package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +0 -1007
- package/dist/src/integrations/ralph/visualization/ralph-debugger.js +0 -635
- package/scripts/ralph-loop-implementation.js +0 -404
- /package/dist/src/core/{merge → cache}/types.js +0 -0
- /package/dist/src/{integrations/diffmem/types.js → core/storage/cloud-sync-types.js} +0 -0
- /package/dist/src/{integrations/greptile/types.js → core/trace/trace-event.js} +0 -0
- /package/dist/src/{integrations/ralph → features/operator}/types.js +0 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
import Database from "better-sqlite3";
|
|
6
|
+
import { existsSync } from "fs";
|
|
7
|
+
import { join } from "path";
|
|
8
|
+
import { homedir } from "os";
|
|
9
|
+
import {
|
|
10
|
+
calculateCost,
|
|
11
|
+
effectiveSpendMultiplier,
|
|
12
|
+
formatCost
|
|
13
|
+
} from "../../../core/models/provider-pricing.js";
|
|
14
|
+
import { getGcpSpend, getGcpSpendEnvFallback } from "./gcp-billing.js";
|
|
15
|
+
const DEFAULT_CONDUCTOR_MODEL = "anthropic/claude-sonnet-4-20250514";
|
|
16
|
+
const DEFAULT_RETRIEVAL_MODEL = "anthropic/claude-haiku-4-5-20251001";
|
|
17
|
+
function resolvePricingKey(model, fallback = DEFAULT_CONDUCTOR_MODEL) {
|
|
18
|
+
if (!model) return fallback;
|
|
19
|
+
const normalized = model.toLowerCase().trim();
|
|
20
|
+
if (normalized.includes("claude-opus-4")) return "anthropic/claude-opus-4-7";
|
|
21
|
+
if (normalized.includes("claude-sonnet-4-5"))
|
|
22
|
+
return "anthropic/claude-sonnet-4-5-20250929";
|
|
23
|
+
if (normalized.includes("claude-sonnet-4"))
|
|
24
|
+
return "anthropic/claude-sonnet-4-20250514";
|
|
25
|
+
if (normalized.includes("claude-haiku"))
|
|
26
|
+
return "anthropic/claude-haiku-4-5-20251001";
|
|
27
|
+
if (normalized.includes("claude-3-5-haiku"))
|
|
28
|
+
return "anthropic/claude-haiku-4-5-20251001";
|
|
29
|
+
if (normalized.includes("gpt-4o")) return "openai/gpt-4o";
|
|
30
|
+
if (normalized.includes("meta-llama/llama-4-scout"))
|
|
31
|
+
return "openrouter/meta-llama/llama-4-scout";
|
|
32
|
+
if (normalized.includes("llama-4-scout-17b"))
|
|
33
|
+
return "cerebras/llama-4-scout-17b-16e-instruct";
|
|
34
|
+
if (normalized.includes("glm-4")) return "deepinfra/THUDM/glm-4-9b-chat";
|
|
35
|
+
return fallback;
|
|
36
|
+
}
|
|
37
|
+
function extractModelFromEventJson(eventJson) {
|
|
38
|
+
if (!eventJson) return void 0;
|
|
39
|
+
try {
|
|
40
|
+
const event = JSON.parse(eventJson);
|
|
41
|
+
const model = event.model ?? event.message?.model ?? event.body?.model ?? event.params?.model;
|
|
42
|
+
return typeof model === "string" ? model : void 0;
|
|
43
|
+
} catch {
|
|
44
|
+
return void 0;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function getConductorTracesDbPath() {
|
|
48
|
+
return join(homedir(), ".stackmemory", "conductor", "traces.db");
|
|
49
|
+
}
|
|
50
|
+
function getProjectContextDbPath() {
|
|
51
|
+
return join(process.cwd(), ".stackmemory", "context.db");
|
|
52
|
+
}
|
|
53
|
+
function readConductorUsage() {
|
|
54
|
+
const dbPath = getConductorTracesDbPath();
|
|
55
|
+
if (!existsSync(dbPath)) return [];
|
|
56
|
+
let db;
|
|
57
|
+
try {
|
|
58
|
+
db = new Database(dbPath, { readonly: true });
|
|
59
|
+
const rows = db.prepare(
|
|
60
|
+
`
|
|
61
|
+
SELECT
|
|
62
|
+
input_tokens as inputTokens,
|
|
63
|
+
output_tokens as outputTokens,
|
|
64
|
+
timestamp,
|
|
65
|
+
event_json as eventJson
|
|
66
|
+
FROM conductor_traces
|
|
67
|
+
WHERE input_tokens > 0 OR output_tokens > 0
|
|
68
|
+
ORDER BY timestamp DESC
|
|
69
|
+
`
|
|
70
|
+
).all();
|
|
71
|
+
return rows.map((r) => ({
|
|
72
|
+
source: "conductor",
|
|
73
|
+
modelKey: resolvePricingKey(
|
|
74
|
+
extractModelFromEventJson(r.eventJson),
|
|
75
|
+
DEFAULT_CONDUCTOR_MODEL
|
|
76
|
+
),
|
|
77
|
+
inputTokens: r.inputTokens || 0,
|
|
78
|
+
outputTokens: r.outputTokens || 0,
|
|
79
|
+
timestamp: r.timestamp
|
|
80
|
+
}));
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.warn("Failed to read conductor traces for spend estimate:", error);
|
|
83
|
+
return [];
|
|
84
|
+
} finally {
|
|
85
|
+
db?.close();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function readRetrievalUsage() {
|
|
89
|
+
const dbPath = getProjectContextDbPath();
|
|
90
|
+
if (!existsSync(dbPath)) return [];
|
|
91
|
+
let db;
|
|
92
|
+
try {
|
|
93
|
+
db = new Database(dbPath, { readonly: true });
|
|
94
|
+
const rows = db.prepare(
|
|
95
|
+
`
|
|
96
|
+
SELECT
|
|
97
|
+
tokens_used as tokensUsed,
|
|
98
|
+
timestamp
|
|
99
|
+
FROM retrieval_audit
|
|
100
|
+
WHERE tokens_used > 0
|
|
101
|
+
ORDER BY timestamp DESC
|
|
102
|
+
`
|
|
103
|
+
).all();
|
|
104
|
+
return rows.map((r) => ({
|
|
105
|
+
source: "retrieval",
|
|
106
|
+
modelKey: DEFAULT_RETRIEVAL_MODEL,
|
|
107
|
+
// Estimate: 80% output, 20% input for the analysis call
|
|
108
|
+
inputTokens: Math.round((r.tokensUsed || 0) * 0.2),
|
|
109
|
+
outputTokens: Math.round((r.tokensUsed || 0) * 0.8),
|
|
110
|
+
timestamp: r.timestamp
|
|
111
|
+
}));
|
|
112
|
+
} catch (error) {
|
|
113
|
+
console.warn("Failed to read retrieval audit for spend estimate:", error);
|
|
114
|
+
return [];
|
|
115
|
+
} finally {
|
|
116
|
+
db?.close();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function dateKey(timestamp) {
|
|
120
|
+
const d = new Date(timestamp);
|
|
121
|
+
return d.toISOString().slice(0, 10);
|
|
122
|
+
}
|
|
123
|
+
function calculateSpendSummary(rows) {
|
|
124
|
+
const now = /* @__PURE__ */ new Date();
|
|
125
|
+
const multiplier = effectiveSpendMultiplier(now);
|
|
126
|
+
const bySource = {};
|
|
127
|
+
const byModel = {};
|
|
128
|
+
const byDayMap = {};
|
|
129
|
+
let totalInputTokens = 0;
|
|
130
|
+
let totalOutputTokens = 0;
|
|
131
|
+
let totalListCost = 0;
|
|
132
|
+
let totalEffectiveCost = 0;
|
|
133
|
+
for (const row of rows) {
|
|
134
|
+
const cost = calculateCost(
|
|
135
|
+
row.modelKey.split("/")[0],
|
|
136
|
+
row.modelKey.split("/").slice(1).join("/"),
|
|
137
|
+
row.inputTokens,
|
|
138
|
+
row.outputTokens
|
|
139
|
+
);
|
|
140
|
+
const listCost = cost?.totalCost ?? 0;
|
|
141
|
+
const effective = listCost * multiplier;
|
|
142
|
+
totalInputTokens += row.inputTokens;
|
|
143
|
+
totalOutputTokens += row.outputTokens;
|
|
144
|
+
totalListCost += listCost;
|
|
145
|
+
totalEffectiveCost += effective;
|
|
146
|
+
const src = bySource[row.source] ?? {
|
|
147
|
+
source: row.source,
|
|
148
|
+
inputTokens: 0,
|
|
149
|
+
outputTokens: 0,
|
|
150
|
+
totalTokens: 0,
|
|
151
|
+
listCostUsd: 0,
|
|
152
|
+
effectiveCostUsd: 0
|
|
153
|
+
};
|
|
154
|
+
src.inputTokens += row.inputTokens;
|
|
155
|
+
src.outputTokens += row.outputTokens;
|
|
156
|
+
src.totalTokens += row.inputTokens + row.outputTokens;
|
|
157
|
+
src.listCostUsd += listCost;
|
|
158
|
+
src.effectiveCostUsd += effective;
|
|
159
|
+
bySource[row.source] = src;
|
|
160
|
+
const model = byModel[row.modelKey] ?? {
|
|
161
|
+
modelKey: row.modelKey,
|
|
162
|
+
displayName: row.modelKey.split("/").slice(1).join("/"),
|
|
163
|
+
inputTokens: 0,
|
|
164
|
+
outputTokens: 0,
|
|
165
|
+
totalTokens: 0,
|
|
166
|
+
listCostUsd: 0,
|
|
167
|
+
effectiveCostUsd: 0
|
|
168
|
+
};
|
|
169
|
+
model.inputTokens += row.inputTokens;
|
|
170
|
+
model.outputTokens += row.outputTokens;
|
|
171
|
+
model.totalTokens += row.inputTokens + row.outputTokens;
|
|
172
|
+
model.listCostUsd += listCost;
|
|
173
|
+
model.effectiveCostUsd += effective;
|
|
174
|
+
byModel[row.modelKey] = model;
|
|
175
|
+
const day = dateKey(row.timestamp);
|
|
176
|
+
const dayEntry = byDayMap[day] ?? {
|
|
177
|
+
date: day,
|
|
178
|
+
inputTokens: 0,
|
|
179
|
+
outputTokens: 0,
|
|
180
|
+
totalTokens: 0,
|
|
181
|
+
listCostUsd: 0,
|
|
182
|
+
effectiveCostUsd: 0
|
|
183
|
+
};
|
|
184
|
+
dayEntry.inputTokens += row.inputTokens;
|
|
185
|
+
dayEntry.outputTokens += row.outputTokens;
|
|
186
|
+
dayEntry.totalTokens += row.inputTokens + row.outputTokens;
|
|
187
|
+
dayEntry.listCostUsd += listCost;
|
|
188
|
+
dayEntry.effectiveCostUsd += effective;
|
|
189
|
+
byDayMap[day] = dayEntry;
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
totalInputTokens,
|
|
193
|
+
totalOutputTokens,
|
|
194
|
+
totalTokens: totalInputTokens + totalOutputTokens,
|
|
195
|
+
listCostUsd: totalListCost,
|
|
196
|
+
effectiveCostUsd: totalEffectiveCost,
|
|
197
|
+
discountMultiplier: multiplier,
|
|
198
|
+
formattedListCost: formatCost(totalListCost),
|
|
199
|
+
formattedEffectiveCost: formatCost(totalEffectiveCost),
|
|
200
|
+
bySource,
|
|
201
|
+
byModel,
|
|
202
|
+
byDay: Object.values(byDayMap).sort((a, b) => a.date.localeCompare(b.date))
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
async function getSpendSummary() {
|
|
206
|
+
const rows = [...readConductorUsage(), ...readRetrievalUsage()];
|
|
207
|
+
const summary = calculateSpendSummary(rows);
|
|
208
|
+
const gcp = await getGcpSpend();
|
|
209
|
+
if (gcp) {
|
|
210
|
+
summary.gcpSpendUsd = gcp.totalCostUsd;
|
|
211
|
+
summary.gcpSpendFormatted = formatCost(gcp.totalCostUsd);
|
|
212
|
+
summary.gcpSource = "bigquery";
|
|
213
|
+
summary.gcpTable = gcp.table;
|
|
214
|
+
summary.gcpDaily = gcp.daily;
|
|
215
|
+
} else {
|
|
216
|
+
const envSpend = getGcpSpendEnvFallback();
|
|
217
|
+
if (envSpend !== void 0) {
|
|
218
|
+
summary.gcpSpendUsd = envSpend;
|
|
219
|
+
summary.gcpSpendFormatted = formatCost(envSpend);
|
|
220
|
+
summary.gcpSource = "env";
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return summary;
|
|
224
|
+
}
|
|
225
|
+
export {
|
|
226
|
+
calculateSpendSummary,
|
|
227
|
+
getSpendSummary
|
|
228
|
+
};
|
|
@@ -15,10 +15,13 @@ const ModelProviderSchema = z.enum([
|
|
|
15
15
|
"anthropic",
|
|
16
16
|
"qwen",
|
|
17
17
|
"openai",
|
|
18
|
+
"xai",
|
|
19
|
+
"deepseek",
|
|
18
20
|
"ollama",
|
|
19
21
|
"cerebras",
|
|
20
22
|
"deepinfra",
|
|
21
23
|
"openrouter",
|
|
24
|
+
"moonshot",
|
|
22
25
|
"anthropic-batch",
|
|
23
26
|
"custom"
|
|
24
27
|
]);
|
|
@@ -40,7 +43,8 @@ const ModelRouterConfigSchema = z.object({
|
|
|
40
43
|
review: ModelProviderSchema.optional(),
|
|
41
44
|
linting: ModelProviderSchema.optional(),
|
|
42
45
|
context: ModelProviderSchema.optional(),
|
|
43
|
-
testing: ModelProviderSchema.optional()
|
|
46
|
+
testing: ModelProviderSchema.optional(),
|
|
47
|
+
design: ModelProviderSchema.optional()
|
|
44
48
|
}).optional().default({}),
|
|
45
49
|
fallback: z.object({
|
|
46
50
|
enabled: z.boolean(),
|
|
@@ -59,6 +63,7 @@ const ModelRouterConfigSchema = z.object({
|
|
|
59
63
|
cerebras: ModelConfigSchema.optional(),
|
|
60
64
|
deepinfra: ModelConfigSchema.optional(),
|
|
61
65
|
openrouter: ModelConfigSchema.optional(),
|
|
66
|
+
moonshot: ModelConfigSchema.optional(),
|
|
62
67
|
"anthropic-batch": ModelConfigSchema.optional(),
|
|
63
68
|
custom: ModelConfigSchema.optional()
|
|
64
69
|
}).optional().default({}),
|
|
@@ -3,6 +3,7 @@ import { dirname as __pathDirname } from 'path';
|
|
|
3
3
|
const __filename = __fileURLToPath(import.meta.url);
|
|
4
4
|
const __dirname = __pathDirname(__filename);
|
|
5
5
|
import { logger } from "../../core/monitoring/logger.js";
|
|
6
|
+
import { estimateTokens } from "../../core/cache/token-estimator.js";
|
|
6
7
|
import { STRUCTURED_RESPONSE_SUFFIX } from "../../orchestrators/multimodal/constants.js";
|
|
7
8
|
class AnthropicClient {
|
|
8
9
|
apiKey;
|
|
@@ -220,8 +221,8 @@ describe('validateInput', () => {
|
|
|
220
221
|
stopReason: "stop_sequence",
|
|
221
222
|
model: request.model,
|
|
222
223
|
usage: {
|
|
223
|
-
inputTokens:
|
|
224
|
-
outputTokens:
|
|
224
|
+
inputTokens: estimateTokens(request.prompt),
|
|
225
|
+
outputTokens: estimateTokens(content)
|
|
225
226
|
}
|
|
226
227
|
};
|
|
227
228
|
}
|
|
@@ -4,9 +4,6 @@ const __filename = __fileURLToPath(import.meta.url);
|
|
|
4
4
|
const __dirname = __pathDirname(__filename);
|
|
5
5
|
import { v4 as uuidv4 } from "uuid";
|
|
6
6
|
import { logger } from "../../core/monitoring/logger.js";
|
|
7
|
-
import {
|
|
8
|
-
OracleWorkerCoordinator
|
|
9
|
-
} from "../ralph/patterns/oracle-worker-pattern.js";
|
|
10
7
|
import { ClaudeCodeTaskCoordinator } from "./task-coordinator.js";
|
|
11
8
|
const CLAUDE_CODE_AGENTS = {
|
|
12
9
|
// Oracle-level agents (strategic, high-level)
|
|
@@ -3,8 +3,9 @@ import { dirname as __pathDirname } from 'path';
|
|
|
3
3
|
const __filename = __fileURLToPath(import.meta.url);
|
|
4
4
|
const __dirname = __pathDirname(__filename);
|
|
5
5
|
import { logger } from "../../core/monitoring/logger.js";
|
|
6
|
+
import { estimateTokens } from "../../core/cache/token-estimator.js";
|
|
6
7
|
import { STRUCTURED_RESPONSE_SUFFIX } from "../../orchestrators/multimodal/constants.js";
|
|
7
|
-
import { spawn } from "child_process";
|
|
8
|
+
import { spawn, execSync } from "child_process";
|
|
8
9
|
import * as fs from "fs";
|
|
9
10
|
import * as path from "path";
|
|
10
11
|
import * as os from "os";
|
|
@@ -17,6 +18,17 @@ import {
|
|
|
17
18
|
createProvider
|
|
18
19
|
} from "../../core/extensions/provider-adapter.js";
|
|
19
20
|
import { AnthropicBatchClient } from "../anthropic/batch-client.js";
|
|
21
|
+
const QUOTA_ERROR_PATTERNS = [
|
|
22
|
+
/rate.?limit/i,
|
|
23
|
+
/quota.?exceeded/i,
|
|
24
|
+
/too many requests/i,
|
|
25
|
+
/429/,
|
|
26
|
+
/capacity/i,
|
|
27
|
+
/billing/i,
|
|
28
|
+
/usage.?limit/i,
|
|
29
|
+
/plan.?limit/i,
|
|
30
|
+
/max.*requests/i
|
|
31
|
+
];
|
|
20
32
|
class ClaudeCodeSubagentClient {
|
|
21
33
|
tempDir;
|
|
22
34
|
mockMode;
|
|
@@ -33,8 +45,12 @@ class ClaudeCodeSubagentClient {
|
|
|
33
45
|
}
|
|
34
46
|
/**
|
|
35
47
|
* Execute a subagent task.
|
|
36
|
-
*
|
|
37
|
-
*
|
|
48
|
+
*
|
|
49
|
+
* Routing priority (subscription-first to minimize API costs):
|
|
50
|
+
* 1. Codex CLI — subagents included in ChatGPT Plus/Pro subscription
|
|
51
|
+
* 2. Grok Build CLI — included in Grok Build subscription ($300/mo)
|
|
52
|
+
* 3. External API providers — when multiProvider enabled (DeepSeek, xAI, etc.)
|
|
53
|
+
* 4. Claude Code CLI — falls back here (uses API tokens, not subscription)
|
|
38
54
|
*/
|
|
39
55
|
async executeSubagent(request) {
|
|
40
56
|
const startTime = Date.now();
|
|
@@ -47,6 +63,43 @@ class ClaudeCodeSubagentClient {
|
|
|
47
63
|
if (this.mockMode) {
|
|
48
64
|
return this.getMockResponse(request, startTime, subagentId);
|
|
49
65
|
}
|
|
66
|
+
if (request.type === "design" || request.forceProvider === "claude") {
|
|
67
|
+
return this.executeSubagentViaCLI(request, startTime, subagentId);
|
|
68
|
+
}
|
|
69
|
+
if (request.forceProvider === "codex" && this.isCodexAvailable()) {
|
|
70
|
+
return this.executeSubagentViaCodex(request, startTime, subagentId);
|
|
71
|
+
}
|
|
72
|
+
if (request.forceProvider === "grok" && this.isGrokBuildAvailable()) {
|
|
73
|
+
return this.executeSubagentViaGrokBuild(request, startTime, subagentId);
|
|
74
|
+
}
|
|
75
|
+
if (this.isCodexAvailable()) {
|
|
76
|
+
try {
|
|
77
|
+
return await this.executeSubagentViaCodex(
|
|
78
|
+
request,
|
|
79
|
+
startTime,
|
|
80
|
+
subagentId
|
|
81
|
+
);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
logger.warn("Codex subagent failed, trying next provider", {
|
|
84
|
+
subagentId,
|
|
85
|
+
error: error.message
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (this.isGrokBuildAvailable()) {
|
|
90
|
+
try {
|
|
91
|
+
return await this.executeSubagentViaGrokBuild(
|
|
92
|
+
request,
|
|
93
|
+
startTime,
|
|
94
|
+
subagentId
|
|
95
|
+
);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
logger.warn("Grok Build subagent failed, trying next provider", {
|
|
98
|
+
subagentId,
|
|
99
|
+
error: error.message
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
50
103
|
if (isFeatureEnabled("multiProvider")) {
|
|
51
104
|
const taskType = request.type;
|
|
52
105
|
const complexity = scoreComplexity(request.task, request.context);
|
|
@@ -108,6 +161,12 @@ class ClaudeCodeSubagentClient {
|
|
|
108
161
|
tokens: result.usage.inputTokens + result.usage.outputTokens
|
|
109
162
|
};
|
|
110
163
|
} catch (error) {
|
|
164
|
+
if (optimal.provider === "anthropic" && this.isQuotaError(error.message)) {
|
|
165
|
+
logger.warn("Anthropic API quota hit, overflowing to Kimi", {
|
|
166
|
+
error: error.message
|
|
167
|
+
});
|
|
168
|
+
return this.executeKimiOverflow(request, startTime, subagentId);
|
|
169
|
+
}
|
|
111
170
|
logger.warn(`Direct API failed for ${optimal.provider}, falling back`, {
|
|
112
171
|
error: error.message
|
|
113
172
|
});
|
|
@@ -173,9 +232,16 @@ Context (JSON): ${JSON.stringify(request.context)}`;
|
|
|
173
232
|
output: result.text,
|
|
174
233
|
duration: Date.now() - startTime,
|
|
175
234
|
subagentType: request.type,
|
|
176
|
-
tokens:
|
|
235
|
+
tokens: estimateTokens(fullPrompt + result.text)
|
|
177
236
|
};
|
|
178
237
|
} catch (error) {
|
|
238
|
+
if (this.isQuotaError(error.message)) {
|
|
239
|
+
logger.warn("Claude quota/rate limit hit, overflowing to Kimi", {
|
|
240
|
+
subagentId,
|
|
241
|
+
error: error.message
|
|
242
|
+
});
|
|
243
|
+
return this.executeKimiOverflow(request, startTime, subagentId);
|
|
244
|
+
}
|
|
179
245
|
logger.error(`Subagent CLI execution failed: ${request.type}`, {
|
|
180
246
|
error,
|
|
181
247
|
subagentId
|
|
@@ -210,6 +276,195 @@ Context (JSON): ${JSON.stringify(request.context)}`;
|
|
|
210
276
|
}
|
|
211
277
|
});
|
|
212
278
|
}
|
|
279
|
+
/**
|
|
280
|
+
* Check if Codex CLI is available (installed + authenticated via ChatGPT subscription)
|
|
281
|
+
*/
|
|
282
|
+
isCodexAvailable() {
|
|
283
|
+
try {
|
|
284
|
+
const result = execSync("which codex 2>/dev/null", {
|
|
285
|
+
encoding: "utf-8",
|
|
286
|
+
timeout: 3e3
|
|
287
|
+
}).trim();
|
|
288
|
+
return !!result;
|
|
289
|
+
} catch {
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Check if Grok Build CLI is available
|
|
295
|
+
*/
|
|
296
|
+
isGrokBuildAvailable() {
|
|
297
|
+
try {
|
|
298
|
+
const result = execSync("which grok 2>/dev/null", {
|
|
299
|
+
encoding: "utf-8",
|
|
300
|
+
timeout: 3e3
|
|
301
|
+
}).trim();
|
|
302
|
+
return !!result;
|
|
303
|
+
} catch {
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Execute subagent via Codex CLI.
|
|
309
|
+
* Subagents run against ChatGPT subscription — no API cost.
|
|
310
|
+
*/
|
|
311
|
+
async executeSubagentViaCodex(request, startTime, subagentId) {
|
|
312
|
+
const prompt = this.buildSubagentPrompt(request);
|
|
313
|
+
logger.info("Routing subagent to Codex CLI (subscription-free)", {
|
|
314
|
+
subagentId,
|
|
315
|
+
type: request.type
|
|
316
|
+
});
|
|
317
|
+
return new Promise((resolve, reject) => {
|
|
318
|
+
const args = ["-q", "--json", "-a", "full-auto", prompt];
|
|
319
|
+
const proc = spawn("codex", args, {
|
|
320
|
+
cwd: process.cwd(),
|
|
321
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
322
|
+
timeout: request.timeout ?? 3e5
|
|
323
|
+
});
|
|
324
|
+
let stdout = "";
|
|
325
|
+
let stderr = "";
|
|
326
|
+
proc.stdout?.on("data", (d) => {
|
|
327
|
+
stdout += d.toString();
|
|
328
|
+
});
|
|
329
|
+
proc.stderr?.on("data", (d) => {
|
|
330
|
+
stderr += d.toString();
|
|
331
|
+
});
|
|
332
|
+
proc.on("close", (code) => {
|
|
333
|
+
if (code !== 0) {
|
|
334
|
+
reject(new Error(`Codex exited ${code}: ${stderr.slice(0, 500)}`));
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
let parsed;
|
|
338
|
+
try {
|
|
339
|
+
parsed = JSON.parse(stdout);
|
|
340
|
+
} catch {
|
|
341
|
+
parsed = { rawOutput: stdout };
|
|
342
|
+
}
|
|
343
|
+
resolve({
|
|
344
|
+
success: true,
|
|
345
|
+
result: parsed,
|
|
346
|
+
output: stdout,
|
|
347
|
+
duration: Date.now() - startTime,
|
|
348
|
+
subagentType: `codex:${request.type}`
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
proc.on("error", reject);
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Execute subagent via Grok Build CLI.
|
|
356
|
+
* Runs against Grok Build subscription — no per-token API cost.
|
|
357
|
+
*/
|
|
358
|
+
async executeSubagentViaGrokBuild(request, startTime, subagentId) {
|
|
359
|
+
const prompt = this.buildSubagentPrompt(request);
|
|
360
|
+
logger.info("Routing subagent to Grok Build CLI (subscription-free)", {
|
|
361
|
+
subagentId,
|
|
362
|
+
type: request.type
|
|
363
|
+
});
|
|
364
|
+
return new Promise((resolve, reject) => {
|
|
365
|
+
const args = ["run", "--quiet", "--json", prompt];
|
|
366
|
+
const proc = spawn("grok", args, {
|
|
367
|
+
cwd: process.cwd(),
|
|
368
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
369
|
+
timeout: request.timeout ?? 3e5
|
|
370
|
+
});
|
|
371
|
+
let stdout = "";
|
|
372
|
+
let stderr = "";
|
|
373
|
+
proc.stdout?.on("data", (d) => {
|
|
374
|
+
stdout += d.toString();
|
|
375
|
+
});
|
|
376
|
+
proc.stderr?.on("data", (d) => {
|
|
377
|
+
stderr += d.toString();
|
|
378
|
+
});
|
|
379
|
+
proc.on("close", (code) => {
|
|
380
|
+
if (code !== 0) {
|
|
381
|
+
reject(
|
|
382
|
+
new Error(`Grok Build exited ${code}: ${stderr.slice(0, 500)}`)
|
|
383
|
+
);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
let parsed;
|
|
387
|
+
try {
|
|
388
|
+
parsed = JSON.parse(stdout);
|
|
389
|
+
} catch {
|
|
390
|
+
parsed = { rawOutput: stdout };
|
|
391
|
+
}
|
|
392
|
+
resolve({
|
|
393
|
+
success: true,
|
|
394
|
+
result: parsed,
|
|
395
|
+
output: stdout,
|
|
396
|
+
duration: Date.now() - startTime,
|
|
397
|
+
subagentType: `grok-build:${request.type}`
|
|
398
|
+
});
|
|
399
|
+
});
|
|
400
|
+
proc.on("error", reject);
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Check if an error message indicates quota/rate limit exhaustion
|
|
405
|
+
*/
|
|
406
|
+
isQuotaError(message) {
|
|
407
|
+
return QUOTA_ERROR_PATTERNS.some((pattern) => pattern.test(message));
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Execute via Kimi/Moonshot API as overflow when Claude quota is exhausted.
|
|
411
|
+
* Uses OpenAI-compatible API at api.moonshot.ai/v1.
|
|
412
|
+
*/
|
|
413
|
+
async executeKimiOverflow(request, startTime, subagentId) {
|
|
414
|
+
const apiKey = process.env["MOONSHOT_API_KEY"] || "";
|
|
415
|
+
if (!apiKey) {
|
|
416
|
+
logger.warn("No MOONSHOT_API_KEY set, cannot overflow to Kimi");
|
|
417
|
+
return {
|
|
418
|
+
success: false,
|
|
419
|
+
result: null,
|
|
420
|
+
error: "Claude quota exceeded and no MOONSHOT_API_KEY configured",
|
|
421
|
+
duration: Date.now() - startTime,
|
|
422
|
+
subagentType: request.type
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
try {
|
|
426
|
+
const adapter = createProvider("moonshot", {
|
|
427
|
+
apiKey,
|
|
428
|
+
baseUrl: "https://api.moonshot.ai/v1"
|
|
429
|
+
});
|
|
430
|
+
const prompt = this.buildSubagentPrompt(request);
|
|
431
|
+
const result = await adapter.complete(
|
|
432
|
+
[{ role: "user", content: prompt }],
|
|
433
|
+
{ model: "kimi-k2.6", maxTokens: 8192 }
|
|
434
|
+
);
|
|
435
|
+
const text = result.content.filter((c) => c.type === "text").map((c) => c.text).join("");
|
|
436
|
+
let parsed;
|
|
437
|
+
try {
|
|
438
|
+
parsed = JSON.parse(text);
|
|
439
|
+
} catch {
|
|
440
|
+
parsed = { rawOutput: text };
|
|
441
|
+
}
|
|
442
|
+
logger.info("Kimi overflow completed", {
|
|
443
|
+
subagentId,
|
|
444
|
+
tokens: result.usage.inputTokens + result.usage.outputTokens
|
|
445
|
+
});
|
|
446
|
+
return {
|
|
447
|
+
success: true,
|
|
448
|
+
result: parsed,
|
|
449
|
+
output: text,
|
|
450
|
+
duration: Date.now() - startTime,
|
|
451
|
+
subagentType: request.type,
|
|
452
|
+
tokens: result.usage.inputTokens + result.usage.outputTokens
|
|
453
|
+
};
|
|
454
|
+
} catch (kimiError) {
|
|
455
|
+
logger.error("Kimi overflow also failed", {
|
|
456
|
+
subagentId,
|
|
457
|
+
error: kimiError.message
|
|
458
|
+
});
|
|
459
|
+
return {
|
|
460
|
+
success: false,
|
|
461
|
+
result: null,
|
|
462
|
+
error: `Claude quota exceeded, Kimi fallback failed: ${kimiError.message}`,
|
|
463
|
+
duration: Date.now() - startTime,
|
|
464
|
+
subagentType: request.type
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
}
|
|
213
468
|
/**
|
|
214
469
|
* Build subagent prompt based on type
|
|
215
470
|
*/
|
|
@@ -319,6 +574,23 @@ Context (JSON): ${JSON.stringify(request.context)}`;
|
|
|
319
574
|
Search parameters are in the context file.
|
|
320
575
|
|
|
321
576
|
Output relevant context snippets.`,
|
|
577
|
+
design: `You are a Frontend Design Subagent. You make creative, opinionated UI/UX decisions while implementing exactly what's scoped.
|
|
578
|
+
|
|
579
|
+
Task: ${request.task}
|
|
580
|
+
|
|
581
|
+
Instructions:
|
|
582
|
+
1. Make strong design decisions \u2014 pick colors, spacing, layout, typography, interactions
|
|
583
|
+
2. Write production-ready code (React/TSX + Tailwind preferred, adapt to project stack)
|
|
584
|
+
3. Favor distinctive, polished UI over generic/boilerplate aesthetics
|
|
585
|
+
4. Ensure responsive design and accessibility basics (contrast, focus, semantic HTML)
|
|
586
|
+
5. Include hover states, transitions, and micro-interactions where appropriate
|
|
587
|
+
6. Match existing design system if one exists in the project, otherwise create cohesive choices
|
|
588
|
+
|
|
589
|
+
The prompt is well-scoped but you own the UI/UX decisions. Don't ask \u2014 decide.
|
|
590
|
+
|
|
591
|
+
Context and requirements are in the provided file.
|
|
592
|
+
|
|
593
|
+
Output the implementation code with brief notes on design choices made.`,
|
|
322
594
|
publish: `You are a Publishing Subagent handling releases and deployments.
|
|
323
595
|
|
|
324
596
|
Task: ${request.task}
|
|
@@ -504,6 +776,15 @@ function greetUser(name: string): string {
|
|
|
504
776
|
changelog: "Initial release",
|
|
505
777
|
published: false,
|
|
506
778
|
reason: "Mock mode - no actual publishing"
|
|
779
|
+
},
|
|
780
|
+
design: {
|
|
781
|
+
implementation: '<div className="flex flex-col gap-4 p-6">...</div>',
|
|
782
|
+
files_modified: ["src/components/Example.tsx"],
|
|
783
|
+
design_choices: [
|
|
784
|
+
"Dark mode default",
|
|
785
|
+
"Space Grotesk typography",
|
|
786
|
+
"8px grid"
|
|
787
|
+
]
|
|
507
788
|
}
|
|
508
789
|
};
|
|
509
790
|
const result = mockResponses[request.type] || {};
|
|
@@ -513,15 +794,9 @@ function greetUser(name: string): string {
|
|
|
513
794
|
output: `Mock ${request.type} subagent completed successfully`,
|
|
514
795
|
duration: Date.now() - startTime,
|
|
515
796
|
subagentType: request.type,
|
|
516
|
-
tokens:
|
|
797
|
+
tokens: estimateTokens(JSON.stringify(result))
|
|
517
798
|
};
|
|
518
799
|
}
|
|
519
|
-
/**
|
|
520
|
-
* Estimate token usage
|
|
521
|
-
*/
|
|
522
|
-
estimateTokens(text) {
|
|
523
|
-
return Math.ceil(text.length / 4);
|
|
524
|
-
}
|
|
525
800
|
/**
|
|
526
801
|
* Cleanup temporary files
|
|
527
802
|
*/
|
|
@@ -541,6 +816,27 @@ function greetUser(name: string): string {
|
|
|
541
816
|
}
|
|
542
817
|
}
|
|
543
818
|
}
|
|
819
|
+
/**
|
|
820
|
+
* Delegate a design/frontend task to Claude.
|
|
821
|
+
* Always routes to Claude CLI regardless of subscription-first ordering,
|
|
822
|
+
* because Claude is the design specialist.
|
|
823
|
+
*
|
|
824
|
+
* Usage from any wrapper (codex-sm, opencode-sm, etc.):
|
|
825
|
+
* const client = new ClaudeCodeSubagentClient();
|
|
826
|
+
* const result = await client.delegateDesign(
|
|
827
|
+
* 'Build a settings page with dark/light toggle, user avatar, and notification preferences',
|
|
828
|
+
* { stack: 'react+tailwind', designSystem: 'nothing' }
|
|
829
|
+
* );
|
|
830
|
+
*/
|
|
831
|
+
async delegateDesign(task, context = {}, options) {
|
|
832
|
+
return this.executeSubagent({
|
|
833
|
+
type: "design",
|
|
834
|
+
task,
|
|
835
|
+
context,
|
|
836
|
+
forceProvider: "claude",
|
|
837
|
+
...options
|
|
838
|
+
});
|
|
839
|
+
}
|
|
544
840
|
/**
|
|
545
841
|
* Get active subagent statistics
|
|
546
842
|
*/
|
|
@@ -5,6 +5,7 @@ const __dirname = __pathDirname(__filename);
|
|
|
5
5
|
import { v4 as uuidv4 } from "uuid";
|
|
6
6
|
import { spawn } from "child_process";
|
|
7
7
|
import { logger } from "../../core/monitoring/logger.js";
|
|
8
|
+
import { estimateTokens } from "../../core/cache/token-estimator.js";
|
|
8
9
|
class ClaudeCodeTaskCoordinator {
|
|
9
10
|
activeTasks = /* @__PURE__ */ new Map();
|
|
10
11
|
completedTasks = [];
|
|
@@ -347,7 +348,7 @@ class ClaudeCodeTaskCoordinator {
|
|
|
347
348
|
* Estimate token usage
|
|
348
349
|
*/
|
|
349
350
|
estimateTokenUsage(prompt, response) {
|
|
350
|
-
return
|
|
351
|
+
return estimateTokens(prompt + response);
|
|
351
352
|
}
|
|
352
353
|
/**
|
|
353
354
|
* Calculate actual task cost
|