expxagents 0.17.4 → 0.17.6
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/assets/core/solution-architect.agent.md +5 -0
- package/dist/cli/src/commands/reorganize.js +85 -42
- package/dist/cli/src/commands/run.js +34 -2
- package/dist/cli/src/runners/cost-tracker.d.ts +7 -0
- package/dist/cli/src/runners/cost-tracker.js +40 -0
- package/dist/core/squad-loader.d.ts +10 -0
- package/dist/core/squad-loader.js +12 -0
- package/dist/server/api/webhook-routes.d.ts.map +1 -1
- package/dist/server/api/webhook-routes.js +36 -0
- package/dist/server/api/webhook-routes.js.map +1 -1
- package/dist/server/bridge/claude-bridge.d.ts.map +1 -1
- package/dist/server/bridge/claude-bridge.js +12 -2
- package/dist/server/bridge/claude-bridge.js.map +1 -1
- package/dist/server/bridge/session-journal.d.ts +25 -0
- package/dist/server/bridge/session-journal.d.ts.map +1 -0
- package/dist/server/bridge/session-journal.js +69 -0
- package/dist/server/bridge/session-journal.js.map +1 -0
- package/package.json +1 -1
|
@@ -77,6 +77,11 @@ squad:
|
|
|
77
77
|
schedule:
|
|
78
78
|
enabled: true
|
|
79
79
|
|
|
80
|
+
# Optional: prevent cost overruns on long-running pipelines
|
|
81
|
+
# spendControl:
|
|
82
|
+
# maxCostPerRun: 2.00 # USD estimate (based on ~4 chars/token)
|
|
83
|
+
# mode: graceful # graceful (warn+continue) | strict (halt)
|
|
84
|
+
|
|
80
85
|
skills:
|
|
81
86
|
- web_search
|
|
82
87
|
- web_fetch
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { spawn } from 'child_process';
|
|
4
3
|
import * as readline from 'readline';
|
|
5
4
|
import yaml from 'js-yaml';
|
|
6
5
|
// Walk squadsDir recursively, return all squad summaries
|
|
@@ -51,56 +50,100 @@ function discoverAllSquads(squadsDir) {
|
|
|
51
50
|
walk(squadsDir);
|
|
52
51
|
return result;
|
|
53
52
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const squadList = squads
|
|
53
|
+
function buildClassificationPrompt(squads) {
|
|
54
|
+
const list = squads
|
|
57
55
|
.map((s) => `- code: ${s.code}, name: "${s.name}", description: "${s.description}"`)
|
|
58
56
|
.join('\n');
|
|
59
|
-
|
|
57
|
+
return `Você é um classificador de squads de IA para uma software house.
|
|
60
58
|
Para cada squad abaixo, identifique o Setor, Grupo e Sessão mais adequados.
|
|
61
59
|
|
|
62
|
-
Setores comuns: marketing, comercial, desenvolvimento, suporte, financeiro, rh,
|
|
60
|
+
Setores comuns: marketing, comercial, desenvolvimento, suporte, financeiro, rh, operacoes, estrategia, design, juridico, administrativo
|
|
63
61
|
|
|
64
62
|
Retorne APENAS um JSON válido (sem markdown, sem explicações), no seguinte formato:
|
|
65
|
-
[
|
|
66
|
-
{
|
|
67
|
-
"code": "<squad-code>",
|
|
68
|
-
"setor": "<setor-em-lowercase-sem-acentos>",
|
|
69
|
-
"grupo": "<grupo-em-lowercase-sem-acentos>",
|
|
70
|
-
"sessao": "<sessao-em-lowercase-sem-acentos>",
|
|
71
|
-
"confidence": "high"
|
|
72
|
-
}
|
|
73
|
-
]
|
|
63
|
+
[{"code":"<squad-code>","setor":"<setor>","grupo":"<grupo>","sessao":"<sessao>","confidence":"high"}]
|
|
74
64
|
|
|
75
|
-
Use "confidence":
|
|
76
|
-
Use hifens em vez de espaços (ex: "redes-sociais"
|
|
65
|
+
Use "confidence":"low" quando não tiver certeza.
|
|
66
|
+
Use hifens em vez de espaços (ex: "redes-sociais").
|
|
77
67
|
|
|
78
|
-
Squads
|
|
79
|
-
${
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
try {
|
|
91
|
-
// Extract JSON from output (Claude may include reasoning text)
|
|
92
|
-
const jsonMatch = output.match(/\[[\s\S]*\]/);
|
|
93
|
-
if (!jsonMatch) {
|
|
94
|
-
reject(new Error('No JSON array found in Claude output'));
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
resolve(JSON.parse(jsonMatch[0]));
|
|
98
|
-
}
|
|
99
|
-
catch (err) {
|
|
100
|
-
reject(new Error(`Failed to parse Claude output: ${err.message}`));
|
|
101
|
-
}
|
|
102
|
-
});
|
|
68
|
+
Squads:
|
|
69
|
+
${list}`;
|
|
70
|
+
}
|
|
71
|
+
// Call Anthropic API directly — fast, no interactive session needed
|
|
72
|
+
async function inferBatchWithApi(squads) {
|
|
73
|
+
const { default: Anthropic } = await import('@anthropic-ai/sdk');
|
|
74
|
+
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
|
|
75
|
+
const response = await client.messages.create({
|
|
76
|
+
model: 'claude-haiku-4-5-20251001',
|
|
77
|
+
max_tokens: 2048,
|
|
78
|
+
messages: [{ role: 'user', content: buildClassificationPrompt(squads) }],
|
|
103
79
|
});
|
|
80
|
+
const text = response.content
|
|
81
|
+
.map((b) => (b.type === 'text' ? b.text : ''))
|
|
82
|
+
.join('');
|
|
83
|
+
const jsonMatch = text.match(/\[[\s\S]*\]/);
|
|
84
|
+
if (!jsonMatch)
|
|
85
|
+
throw new Error('No JSON array in API response');
|
|
86
|
+
return JSON.parse(jsonMatch[0]);
|
|
87
|
+
}
|
|
88
|
+
// Heuristic fallback — keyword-based, no AI required
|
|
89
|
+
function inferHeuristic(squad) {
|
|
90
|
+
const text = `${squad.code} ${squad.name} ${squad.description}`.toLowerCase();
|
|
91
|
+
const RULES = [
|
|
92
|
+
{ setor: 'marketing', grupo: 'redes-sociais', sessao: 'instagram', keywords: ['instagram', 'reels', 'stories'] },
|
|
93
|
+
{ setor: 'marketing', grupo: 'redes-sociais', sessao: 'linkedin', keywords: ['linkedin'] },
|
|
94
|
+
{ setor: 'marketing', grupo: 'redes-sociais', sessao: 'twitter', keywords: ['twitter', 'x.com', 'tweet'] },
|
|
95
|
+
{ setor: 'marketing', grupo: 'conteudo', sessao: 'blog', keywords: ['blog', 'artigo', 'post'] },
|
|
96
|
+
{ setor: 'marketing', grupo: 'email', sessao: 'campanhas', keywords: ['email', 'newsletter', 'campanha'] },
|
|
97
|
+
{ setor: 'marketing', grupo: 'ads', sessao: 'google', keywords: ['google ads', 'adwords', 'sem'] },
|
|
98
|
+
{ setor: 'marketing', grupo: 'ads', sessao: 'meta', keywords: ['meta ads', 'facebook ads'] },
|
|
99
|
+
{ setor: 'comercial', grupo: 'vendas', sessao: 'prospecao', keywords: ['sdr', 'prospeccao', 'leads', 'prospect'] },
|
|
100
|
+
{ setor: 'comercial', grupo: 'vendas', sessao: 'proposta', keywords: ['proposta', 'cotacao', 'orcamento'] },
|
|
101
|
+
{ setor: 'comercial', grupo: 'crm', sessao: 'gestao', keywords: ['crm', 'pipeline', 'funil'] },
|
|
102
|
+
{ setor: 'desenvolvimento', grupo: 'produto', sessao: 'frontend', keywords: ['frontend', 'react', 'vue', 'angular', 'ui'] },
|
|
103
|
+
{ setor: 'desenvolvimento', grupo: 'produto', sessao: 'backend', keywords: ['backend', 'api', 'server', 'banco de dados'] },
|
|
104
|
+
{ setor: 'desenvolvimento', grupo: 'produto', sessao: 'devops', keywords: ['devops', 'ci/cd', 'deploy', 'docker', 'kubernetes'] },
|
|
105
|
+
{ setor: 'desenvolvimento', grupo: 'qualidade', sessao: 'qa', keywords: ['qa', 'teste', 'qualidade', 'bug'] },
|
|
106
|
+
{ setor: 'suporte', grupo: 'atendimento', sessao: 'l1', keywords: ['suporte', 'atendimento', 'helpdesk', 'l1', 'l2'] },
|
|
107
|
+
{ setor: 'rh', grupo: 'recrutamento', sessao: 'selecao', keywords: ['recrutamento', 'selecao', 'entrevista', 'vaga'] },
|
|
108
|
+
{ setor: 'rh', grupo: 'pessoas', sessao: 'cultura', keywords: ['cultura', 'onboarding', 'treinamento', 'clima'] },
|
|
109
|
+
{ setor: 'financeiro', grupo: 'contabilidade', sessao: 'fiscal', keywords: ['financeiro', 'contabil', 'fiscal', 'nfe', 'fatura'] },
|
|
110
|
+
{ setor: 'juridico', grupo: 'contratos', sessao: 'analise', keywords: ['juridico', 'contrato', 'legal', 'compliance'] },
|
|
111
|
+
{ setor: 'design', grupo: 'visual', sessao: 'identidade', keywords: ['design', 'branding', 'identidade', 'logo', 'marca'] },
|
|
112
|
+
{ setor: 'estrategia', grupo: 'planejamento', sessao: 'okr', keywords: ['estrategia', 'okr', 'planejamento', 'kpi', 'meta'] },
|
|
113
|
+
{ setor: 'operacoes', grupo: 'processos', sessao: 'gestao', keywords: ['operacoes', 'processo', 'workflow', 'automacao'] },
|
|
114
|
+
];
|
|
115
|
+
for (const rule of RULES) {
|
|
116
|
+
if (rule.keywords.some((kw) => text.includes(kw))) {
|
|
117
|
+
return { code: squad.code, setor: rule.setor, grupo: rule.grupo, sessao: rule.sessao, confidence: 'low' };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return { code: squad.code, setor: 'geral', grupo: 'squads', sessao: 'default', confidence: 'low' };
|
|
121
|
+
}
|
|
122
|
+
// Classify all squads: try Anthropic API in batches, fall back to heuristic
|
|
123
|
+
async function inferHierarchyWithClaude(squads) {
|
|
124
|
+
const hasApiKey = !!process.env.ANTHROPIC_API_KEY;
|
|
125
|
+
if (!hasApiKey) {
|
|
126
|
+
console.log(' ANTHROPIC_API_KEY não definida — usando classificação heurística.\n');
|
|
127
|
+
return squads.map(inferHeuristic);
|
|
128
|
+
}
|
|
129
|
+
const BATCH_SIZE = 10;
|
|
130
|
+
const results = [];
|
|
131
|
+
const batches = Math.ceil(squads.length / BATCH_SIZE);
|
|
132
|
+
for (let i = 0; i < squads.length; i += BATCH_SIZE) {
|
|
133
|
+
const batch = squads.slice(i, i + BATCH_SIZE);
|
|
134
|
+
const batchNum = Math.floor(i / BATCH_SIZE) + 1;
|
|
135
|
+
process.stdout.write(` Classificando batch ${batchNum}/${batches} (${batch.length} squads)...`);
|
|
136
|
+
try {
|
|
137
|
+
const batchResults = await inferBatchWithApi(batch);
|
|
138
|
+
results.push(...batchResults);
|
|
139
|
+
process.stdout.write(' ✓\n');
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
process.stdout.write(` falhou (${err.message}) — usando heurística para este batch.\n`);
|
|
143
|
+
results.push(...batch.map(inferHeuristic));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return results;
|
|
104
147
|
}
|
|
105
148
|
function ask(rl, question) {
|
|
106
149
|
return new Promise((resolve) => rl.question(question, resolve));
|
|
@@ -6,6 +6,7 @@ import { createInitialState, readState, writeState, updateAgentStatus, updateSte
|
|
|
6
6
|
import { loadSkills } from '../../../core/skills-loader.js';
|
|
7
7
|
import { getCoreAsset } from '../utils/config.js';
|
|
8
8
|
import { runWithProvider } from '../runners/provider-runner.js';
|
|
9
|
+
import { estimateCost, formatCost } from '../runners/cost-tracker.js';
|
|
9
10
|
function delay(ms) {
|
|
10
11
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
11
12
|
}
|
|
@@ -163,13 +164,35 @@ export async function runCommand(name) {
|
|
|
163
164
|
state = setSquadStatus(state, 'running');
|
|
164
165
|
state = { ...state, startedAt: new Date().toISOString() };
|
|
165
166
|
writeState(squadDir, state);
|
|
166
|
-
|
|
167
|
-
|
|
167
|
+
const spendControl = config.squad.spendControl;
|
|
168
|
+
let totalCost = 0;
|
|
169
|
+
let spendLimitHit = false;
|
|
170
|
+
if (spendControl?.maxCostPerRun) {
|
|
171
|
+
console.log(`\nRunning squad "${config.squad.name}" (${config.squad.code})`);
|
|
172
|
+
console.log(`Pipeline: ${config.squad.pipeline.steps.length} steps | Spend limit: ${formatCost(spendControl.maxCostPerRun)} (${spendControl.mode ?? 'graceful'})\n`);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
console.log(`\nRunning squad "${config.squad.name}" (${config.squad.code})`);
|
|
176
|
+
console.log(`Pipeline: ${config.squad.pipeline.steps.length} steps\n`);
|
|
177
|
+
}
|
|
168
178
|
const steps = config.squad.pipeline.steps;
|
|
169
179
|
for (let i = 0; i < steps.length; i++) {
|
|
170
180
|
const step = steps[i];
|
|
171
181
|
const stepNumber = i + 1;
|
|
172
182
|
const agent = config.squad.agents.find(a => a.id === step.agent);
|
|
183
|
+
// Spend control — check before running the step
|
|
184
|
+
if (spendControl?.maxCostPerRun && totalCost >= spendControl.maxCostPerRun) {
|
|
185
|
+
if (spendControl.mode === 'strict') {
|
|
186
|
+
console.error(`\n[spend-control] Limit ${formatCost(spendControl.maxCostPerRun)} reached (used ${formatCost(totalCost)}). Halting (strict mode).`);
|
|
187
|
+
state = setSquadStatus(state, 'idle');
|
|
188
|
+
writeState(squadDir, state);
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
else if (!spendLimitHit) {
|
|
192
|
+
spendLimitHit = true;
|
|
193
|
+
console.warn(`\n[spend-control] ⚠ Limit ${formatCost(spendControl.maxCostPerRun)} reached (used ${formatCost(totalCost)}). Continuing in graceful mode.`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
173
196
|
console.log(`--- Step ${stepNumber}/${steps.length}: ${step.label} (${agent.name}) ---\n`);
|
|
174
197
|
state = updateAgentStatus(state, step.agent, 'working');
|
|
175
198
|
state = updateStep(state, stepNumber, step.label);
|
|
@@ -186,6 +209,12 @@ export async function runCommand(name) {
|
|
|
186
209
|
writeState(squadDir, state);
|
|
187
210
|
process.exit(1);
|
|
188
211
|
}
|
|
212
|
+
// Track estimated cost for this step
|
|
213
|
+
const stepCost = estimateCost(prompt, output, agent.model);
|
|
214
|
+
totalCost += stepCost;
|
|
215
|
+
if (spendControl?.maxCostPerRun) {
|
|
216
|
+
console.log(`[spend-control] Step cost: ${formatCost(stepCost)} | Run total: ${formatCost(totalCost)} / ${formatCost(spendControl.maxCostPerRun)}`);
|
|
217
|
+
}
|
|
189
218
|
const outputPath = path.join(outputDir, `step-${String(stepNumber).padStart(2, '0')}.md`);
|
|
190
219
|
fs.writeFileSync(outputPath, output, 'utf-8');
|
|
191
220
|
const outputLines = output.trim().split('\n').filter(l => l.trim().length > 0);
|
|
@@ -225,4 +254,7 @@ export async function runCommand(name) {
|
|
|
225
254
|
writeState(squadDir, state);
|
|
226
255
|
console.log(`\nSquad "${config.squad.name}" pipeline completed!`);
|
|
227
256
|
console.log(`Output files saved to squads/${name}/output/v${version}/`);
|
|
257
|
+
if (totalCost > 0) {
|
|
258
|
+
console.log(`Estimated run cost: ${formatCost(totalCost)}`);
|
|
259
|
+
}
|
|
228
260
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Estimates the USD cost of an LLM call using character-based token approximation.
|
|
3
|
+
* Rule of thumb: ~4 characters per token. Actual costs may vary.
|
|
4
|
+
* Use for guardrail enforcement, not billing.
|
|
5
|
+
*/
|
|
6
|
+
export declare function estimateCost(inputText: string, outputText: string, model?: string): number;
|
|
7
|
+
export declare function formatCost(usd: number): string;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Rates as of 2026-Q1 — update periodically
|
|
2
|
+
const MODEL_RATES = {
|
|
3
|
+
// Anthropic
|
|
4
|
+
'claude-opus-4-6': { input: 15.00, output: 75.00 },
|
|
5
|
+
'claude-sonnet-4-6': { input: 3.00, output: 15.00 },
|
|
6
|
+
'claude-haiku-4-5': { input: 0.25, output: 1.25 },
|
|
7
|
+
'claude-haiku-4-5-20251001': { input: 0.25, output: 1.25 },
|
|
8
|
+
// OpenAI
|
|
9
|
+
'gpt-4o': { input: 2.50, output: 10.00 },
|
|
10
|
+
'gpt-4o-mini': { input: 0.15, output: 0.60 },
|
|
11
|
+
'gpt-4-turbo': { input: 10.00, output: 30.00 },
|
|
12
|
+
'o1': { input: 15.00, output: 60.00 },
|
|
13
|
+
'o3-mini': { input: 1.10, output: 4.40 },
|
|
14
|
+
// DeepSeek
|
|
15
|
+
'deepseek-chat': { input: 0.27, output: 1.10 },
|
|
16
|
+
'deepseek-reasoner': { input: 0.55, output: 2.19 },
|
|
17
|
+
// Google
|
|
18
|
+
'gemini-2.5-flash': { input: 0.15, output: 0.60 },
|
|
19
|
+
'gemini-2.5-pro': { input: 1.25, output: 5.00 },
|
|
20
|
+
};
|
|
21
|
+
// Default: Claude Sonnet — used for 'claude-code' provider or unknown models
|
|
22
|
+
const DEFAULT_RATE = MODEL_RATES['claude-sonnet-4-6'];
|
|
23
|
+
/**
|
|
24
|
+
* Estimates the USD cost of an LLM call using character-based token approximation.
|
|
25
|
+
* Rule of thumb: ~4 characters per token. Actual costs may vary.
|
|
26
|
+
* Use for guardrail enforcement, not billing.
|
|
27
|
+
*/
|
|
28
|
+
export function estimateCost(inputText, outputText, model) {
|
|
29
|
+
const rate = (model && MODEL_RATES[model]) ? MODEL_RATES[model] : DEFAULT_RATE;
|
|
30
|
+
const inputTokens = Math.ceil(inputText.length / 4);
|
|
31
|
+
const outputTokens = Math.ceil(outputText.length / 4);
|
|
32
|
+
return (inputTokens * rate.input + outputTokens * rate.output) / 1_000_000;
|
|
33
|
+
}
|
|
34
|
+
export function formatCost(usd) {
|
|
35
|
+
if (usd === 0)
|
|
36
|
+
return '$0.0000';
|
|
37
|
+
if (usd < 0.0001)
|
|
38
|
+
return `<$0.0001`;
|
|
39
|
+
return `$${usd.toFixed(4)}`;
|
|
40
|
+
}
|
|
@@ -22,6 +22,15 @@ export interface ScheduleConfig {
|
|
|
22
22
|
timeout_minutes?: number;
|
|
23
23
|
retry_on_failure?: number;
|
|
24
24
|
}
|
|
25
|
+
export interface SpendControlConfig {
|
|
26
|
+
/** Maximum estimated USD cost for a single run. Enforcement depends on mode. */
|
|
27
|
+
maxCostPerRun?: number;
|
|
28
|
+
/**
|
|
29
|
+
* - graceful: warn and continue when limit is hit (default)
|
|
30
|
+
* - strict: halt the run immediately when limit is exceeded
|
|
31
|
+
*/
|
|
32
|
+
mode?: 'graceful' | 'strict';
|
|
33
|
+
}
|
|
25
34
|
export interface SquadConfig {
|
|
26
35
|
squad: {
|
|
27
36
|
code: string;
|
|
@@ -43,6 +52,7 @@ export interface SquadConfig {
|
|
|
43
52
|
webhooks?: {
|
|
44
53
|
secret?: string;
|
|
45
54
|
};
|
|
55
|
+
spendControl?: SpendControlConfig;
|
|
46
56
|
};
|
|
47
57
|
}
|
|
48
58
|
export declare function loadSquad(squadDir: string): SquadConfig;
|
|
@@ -84,6 +84,17 @@ export function loadSquad(squadDir) {
|
|
|
84
84
|
const sessao = squad.sessao;
|
|
85
85
|
const rawWebhooks = squad.webhooks;
|
|
86
86
|
const webhooks = rawWebhooks ? { secret: rawWebhooks.secret } : undefined;
|
|
87
|
+
const rawSpend = squad.spendControl;
|
|
88
|
+
let spendControl;
|
|
89
|
+
if (rawSpend) {
|
|
90
|
+
spendControl = {
|
|
91
|
+
maxCostPerRun: typeof rawSpend.maxCostPerRun === 'number' ? rawSpend.maxCostPerRun : undefined,
|
|
92
|
+
mode: (rawSpend.mode === 'strict' ? 'strict' : 'graceful'),
|
|
93
|
+
};
|
|
94
|
+
if (spendControl.maxCostPerRun !== undefined && spendControl.maxCostPerRun <= 0) {
|
|
95
|
+
throw new Error('spendControl.maxCostPerRun must be a positive number');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
87
98
|
return {
|
|
88
99
|
squad: {
|
|
89
100
|
code: squad.code,
|
|
@@ -100,6 +111,7 @@ export function loadSquad(squadDir) {
|
|
|
100
111
|
schedule,
|
|
101
112
|
chain,
|
|
102
113
|
webhooks,
|
|
114
|
+
spendControl,
|
|
103
115
|
},
|
|
104
116
|
};
|
|
105
117
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook-routes.d.ts","sourceRoot":"","sources":["../../src/api/webhook-routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"webhook-routes.d.ts","sourceRoot":"","sources":["../../src/api/webhook-routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAKrE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAE5D,UAAU,oBAAqB,SAAQ,oBAAoB;IACzD,SAAS,EAAE,SAAS,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AA0CD,wBAAsB,aAAa,CACjC,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,IAAI,CAAC,CA8Cf"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import crypto from 'node:crypto';
|
|
3
4
|
import YAML from 'yaml';
|
|
4
5
|
function readWebhookSecret(squadsDir, squadName) {
|
|
5
6
|
try {
|
|
@@ -13,6 +14,32 @@ function readWebhookSecret(squadsDir, squadName) {
|
|
|
13
14
|
return undefined;
|
|
14
15
|
}
|
|
15
16
|
}
|
|
17
|
+
// Deduplication — prevents double-spawn when callers retry on slow 202 responses
|
|
18
|
+
const DEDUP_WINDOW_MS = 30_000;
|
|
19
|
+
const pendingRequests = new Map();
|
|
20
|
+
function deduplicationHash(squadName, body) {
|
|
21
|
+
// Normalize body: remove timestamp-like fields that vary between retries
|
|
22
|
+
const clean = {};
|
|
23
|
+
const raw = (body ?? {});
|
|
24
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
25
|
+
if (/timestamp|time|date|_at$/i.test(k))
|
|
26
|
+
continue;
|
|
27
|
+
clean[k] = v;
|
|
28
|
+
}
|
|
29
|
+
return crypto
|
|
30
|
+
.createHash('sha256')
|
|
31
|
+
.update(`${squadName}:${JSON.stringify(clean)}`)
|
|
32
|
+
.digest('hex')
|
|
33
|
+
.slice(0, 16);
|
|
34
|
+
}
|
|
35
|
+
function pruneExpired() {
|
|
36
|
+
const now = Date.now();
|
|
37
|
+
for (const [hash, entry] of pendingRequests) {
|
|
38
|
+
if (now - entry.triggeredAt > DEDUP_WINDOW_MS) {
|
|
39
|
+
pendingRequests.delete(hash);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
16
43
|
export async function webhookRoutes(app, opts) {
|
|
17
44
|
const { jobRunner, squadsDir } = opts;
|
|
18
45
|
app.post('/api/webhooks/:squadName', async (request, reply) => {
|
|
@@ -27,6 +54,15 @@ export async function webhookRoutes(app, opts) {
|
|
|
27
54
|
return reply.code(401).send({ error: 'Invalid or missing webhook secret' });
|
|
28
55
|
}
|
|
29
56
|
}
|
|
57
|
+
// Deduplication: return 202 immediately if an identical request is in-flight
|
|
58
|
+
pruneExpired();
|
|
59
|
+
const hash = deduplicationHash(squadName, request.body);
|
|
60
|
+
if (pendingRequests.has(hash)) {
|
|
61
|
+
return reply.code(202).send({ status: 'already_triggered', squadName, deduplicated: true });
|
|
62
|
+
}
|
|
63
|
+
pendingRequests.set(hash, { squadName, triggeredAt: Date.now() });
|
|
64
|
+
// Auto-remove after the dedup window
|
|
65
|
+
setTimeout(() => pendingRequests.delete(hash), DEDUP_WINDOW_MS);
|
|
30
66
|
const prompt = request.body?.prompt ?? `Execute the "${squadName}" squad pipeline`;
|
|
31
67
|
// Fire and forget — don't await
|
|
32
68
|
jobRunner.run({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook-routes.js","sourceRoot":"","sources":["../../src/api/webhook-routes.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,IAAI,MAAM,MAAM,CAAC;AAQxB,SAAS,iBAAiB,CAAC,SAAiB,EAAE,SAAiB;IAC7D,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;QAC/D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO,SAAS,CAAC;QAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAwB,CAAC;QACrF,OAAO,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAA4B,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,GAAoB,EACpB,IAA0B;IAE1B,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IAEtC,GAAG,CAAC,IAAI,CAGL,0BAA0B,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QACtD,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;QAErC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC;YAClE,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,UAAU,SAAS,aAAa,EAAE,CAAC,CAAC;QAC3E,CAAC;QAED,MAAM,gBAAgB,GAAG,iBAAiB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QACjE,IAAI,gBAAgB,EAAE,CAAC;YACrB,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;YACrD,IAAI,QAAQ,KAAK,gBAAgB,EAAE,CAAC;gBAClC,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mCAAmC,EAAE,CAAC,CAAC;YAC9E,CAAC;QACH,CAAC;QAED,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,MAAM,IAAI,gBAAgB,SAAS,kBAAkB,CAAC;QAEnF,gCAAgC;QAChC,SAAS,CAAC,GAAG,CAAC;YACZ,SAAS;YACT,MAAM;YACN,cAAc,EAAE,EAAE;YAClB,cAAc,EAAE,CAAC;YACjB,OAAO,EAAE,CAAC;SACX,CAAC,CAAC,KAAK,CAAC,CAAC,GAAU,EAAE,EAAE;YACtB,OAAO,CAAC,KAAK,CAAC,oBAAoB,SAAS,eAAe,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;QAC3E,CAAC,CAAC,CAAC;QAEH,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
1
|
+
{"version":3,"file":"webhook-routes.js","sourceRoot":"","sources":["../../src/api/webhook-routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-bridge.d.ts","sourceRoot":"","sources":["../../src/bridge/claude-bridge.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"claude-bridge.d.ts","sourceRoot":"","sources":["../../src/bridge/claude-bridge.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kDAAkD;IAClD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5E,8EAA8E;IAC9E,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,OAAO,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;CACvC;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;CACnB;AAmDD,qBAAa,YAAY;IACvB,OAAO,CAAC,eAAe,CAAwC;IAC/D,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,YAAY,CAAS;gBAEjB,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC;IAM1C,IAAI,CAAC,IAAI,EAAE,iBAAiB,GAAG,IAAI;IAiMnC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAIpC,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;CAM9B"}
|
|
@@ -2,6 +2,7 @@ import { spawn, execSync } from 'child_process';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import fs from 'node:fs';
|
|
5
|
+
import { sessionJournal } from './session-journal.js';
|
|
5
6
|
/** Summarise a tool_use input into a short human-readable string */
|
|
6
7
|
function summariseToolInput(name, input) {
|
|
7
8
|
switch (name) {
|
|
@@ -105,9 +106,16 @@ export class ClaudeBridge {
|
|
|
105
106
|
return;
|
|
106
107
|
}
|
|
107
108
|
this.activeProcesses.set(key, proc);
|
|
109
|
+
// Inject session journal context for fresh sessions (not --continue / --resume)
|
|
110
|
+
let finalPrompt = opts.prompt;
|
|
111
|
+
if (!opts.continueSession && !opts.resumeSessionId) {
|
|
112
|
+
const injection = sessionJournal.buildInjection(key);
|
|
113
|
+
if (injection)
|
|
114
|
+
finalPrompt = injection + opts.prompt;
|
|
115
|
+
}
|
|
108
116
|
// Write prompt via stdin (handles long prompts safely)
|
|
109
|
-
console.log(`[bridge] spawn claude PID=${proc.pid} key="${key}" prompt=${
|
|
110
|
-
proc.stdin?.write(
|
|
117
|
+
console.log(`[bridge] spawn claude PID=${proc.pid} key="${key}" prompt=${finalPrompt.length} chars`);
|
|
118
|
+
proc.stdin?.write(finalPrompt);
|
|
111
119
|
proc.stdin?.end();
|
|
112
120
|
let stderrBuffer = '';
|
|
113
121
|
let fullText = '';
|
|
@@ -220,6 +228,8 @@ export class ClaudeBridge {
|
|
|
220
228
|
}
|
|
221
229
|
else {
|
|
222
230
|
console.log(`[bridge] SUCCESS: ${fullText.slice(0, 200)}`);
|
|
231
|
+
// Record action statements into the session journal for future context injection
|
|
232
|
+
sessionJournal.record(key, fullText);
|
|
223
233
|
opts.onDone(fullText);
|
|
224
234
|
}
|
|
225
235
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-bridge.js","sourceRoot":"","sources":["../../src/bridge/claude-bridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAqB,MAAM,eAAe,CAAC;AACnE,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AA+
|
|
1
|
+
{"version":3,"file":"claude-bridge.js","sourceRoot":"","sources":["../../src/bridge/claude-bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionJournal — compact action memory between bridge calls.
|
|
3
|
+
*
|
|
4
|
+
* After each completed Claude response, extracts "action statements" (created,
|
|
5
|
+
* fixed, updated, etc.) and stores them per session key. Before the next call,
|
|
6
|
+
* the journal can be injected as a context prefix so the model doesn't lose track
|
|
7
|
+
* of what it has already done — especially in long multi-step workflows.
|
|
8
|
+
*/
|
|
9
|
+
declare class SessionJournalStore {
|
|
10
|
+
private sessions;
|
|
11
|
+
/** Extract action lines from a response and append to this session's journal. */
|
|
12
|
+
record(key: string, responseText: string): void;
|
|
13
|
+
/**
|
|
14
|
+
* Build a compact context string to prepend to the next prompt.
|
|
15
|
+
* Returns empty string if no journal exists or session has expired.
|
|
16
|
+
*/
|
|
17
|
+
buildInjection(key: string): string;
|
|
18
|
+
/** Clear journal for a session (e.g., after a run completes). */
|
|
19
|
+
clear(key: string): void;
|
|
20
|
+
/** Number of sessions currently tracked. */
|
|
21
|
+
get size(): number;
|
|
22
|
+
}
|
|
23
|
+
export declare const sessionJournal: SessionJournalStore;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=session-journal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-journal.d.ts","sourceRoot":"","sources":["../../src/bridge/session-journal.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAwBH,cAAM,mBAAmB;IACvB,OAAO,CAAC,QAAQ,CAAkC;IAElD,iFAAiF;IACjF,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI;IAuB/C;;;OAGG;IACH,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAWnC,iEAAiE;IACjE,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIxB,4CAA4C;IAC5C,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF;AAED,eAAO,MAAM,cAAc,qBAA4B,CAAC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionJournal — compact action memory between bridge calls.
|
|
3
|
+
*
|
|
4
|
+
* After each completed Claude response, extracts "action statements" (created,
|
|
5
|
+
* fixed, updated, etc.) and stores them per session key. Before the next call,
|
|
6
|
+
* the journal can be injected as a context prefix so the model doesn't lose track
|
|
7
|
+
* of what it has already done — especially in long multi-step workflows.
|
|
8
|
+
*/
|
|
9
|
+
const MAX_ENTRIES = 20;
|
|
10
|
+
const SESSION_TTL_MS = 60 * 60 * 1_000; // 1 hour inactivity
|
|
11
|
+
const ACTION_PATTERNS = [
|
|
12
|
+
// "I created/wrote/added/…"
|
|
13
|
+
/^I (?:created?|wrote|added|updated?|modified|deleted?|fixed|implemented|built|configured|installed|refactored?|moved?|renamed?) .{10,120}/gim,
|
|
14
|
+
// "Successfully created/…"
|
|
15
|
+
/^Successfully (?:created?|wrote|added|updated?|modified|fixed|implemented|built|configured|installed) .{10,120}/gim,
|
|
16
|
+
// Leading past-tense verbs
|
|
17
|
+
/^(?:Created?|Wrote|Added|Updated?|Modified|Fixed|Implemented|Built|Installed) .{10,120}/gim,
|
|
18
|
+
];
|
|
19
|
+
class SessionJournalStore {
|
|
20
|
+
sessions = new Map();
|
|
21
|
+
/** Extract action lines from a response and append to this session's journal. */
|
|
22
|
+
record(key, responseText) {
|
|
23
|
+
const found = new Set();
|
|
24
|
+
for (const pattern of ACTION_PATTERNS) {
|
|
25
|
+
// reset lastIndex for global patterns
|
|
26
|
+
pattern.lastIndex = 0;
|
|
27
|
+
let match;
|
|
28
|
+
while ((match = pattern.exec(responseText)) !== null) {
|
|
29
|
+
const line = match[0].trim().replace(/\s+/g, ' ').slice(0, 120);
|
|
30
|
+
found.add(line);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (found.size === 0)
|
|
34
|
+
return;
|
|
35
|
+
const existing = this.sessions.get(key) ?? { entries: [], updatedAt: Date.now() };
|
|
36
|
+
const newEntries = [...existing.entries, ...[...found].map(text => ({
|
|
37
|
+
timestamp: Date.now(),
|
|
38
|
+
text,
|
|
39
|
+
}))];
|
|
40
|
+
// Keep only the last MAX_ENTRIES
|
|
41
|
+
const trimmed = newEntries.slice(-MAX_ENTRIES);
|
|
42
|
+
this.sessions.set(key, { entries: trimmed, updatedAt: Date.now() });
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Build a compact context string to prepend to the next prompt.
|
|
46
|
+
* Returns empty string if no journal exists or session has expired.
|
|
47
|
+
*/
|
|
48
|
+
buildInjection(key) {
|
|
49
|
+
const session = this.sessions.get(key);
|
|
50
|
+
if (!session || session.entries.length === 0)
|
|
51
|
+
return '';
|
|
52
|
+
if (Date.now() - session.updatedAt > SESSION_TTL_MS) {
|
|
53
|
+
this.sessions.delete(key);
|
|
54
|
+
return '';
|
|
55
|
+
}
|
|
56
|
+
const lines = session.entries.map(e => `- ${e.text}`).join('\n');
|
|
57
|
+
return `[Context — actions already completed in this session:\n${lines}]\n\n`;
|
|
58
|
+
}
|
|
59
|
+
/** Clear journal for a session (e.g., after a run completes). */
|
|
60
|
+
clear(key) {
|
|
61
|
+
this.sessions.delete(key);
|
|
62
|
+
}
|
|
63
|
+
/** Number of sessions currently tracked. */
|
|
64
|
+
get size() {
|
|
65
|
+
return this.sessions.size;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export const sessionJournal = new SessionJournalStore();
|
|
69
|
+
//# sourceMappingURL=session-journal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-journal.js","sourceRoot":"","sources":["../../src/bridge/session-journal.ts"],"names":[],"mappings":"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"}
|