@stackmemoryai/stackmemory 1.0.1 → 1.2.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/dist/src/cli/claude-sm.js +65 -0
- package/dist/src/cli/commands/audit.js +134 -0
- package/dist/src/cli/commands/bench.js +252 -0
- package/dist/src/cli/commands/dashboard.js +2 -1
- package/dist/src/cli/commands/stats.js +118 -0
- package/dist/src/cli/index.js +6 -0
- package/dist/src/core/config/feature-flags.js +7 -1
- package/dist/src/core/context/enhanced-rehydration.js +24 -5
- package/dist/src/core/extensions/cerebras-adapter.js +28 -0
- package/dist/src/core/extensions/deepinfra-adapter.js +32 -0
- package/dist/src/core/extensions/provider-adapter.js +33 -240
- package/dist/src/core/models/complexity-scorer.js +154 -0
- package/dist/src/core/models/model-router.js +230 -36
- package/dist/src/core/models/provider-pricing.js +63 -0
- package/dist/src/core/models/sensitive-guard.js +112 -0
- package/dist/src/core/monitoring/feedback-loops.js +88 -0
- package/dist/src/hooks/schemas.js +12 -1
- package/dist/src/integrations/anthropic/batch-client.js +256 -0
- package/dist/src/integrations/anthropic/client.js +87 -72
- package/dist/src/integrations/claude-code/subagent-client.js +133 -12
- package/dist/src/integrations/graphiti/client.js +16 -4
- package/dist/src/integrations/mcp/handlers/index.js +25 -1
- package/dist/src/integrations/mcp/handlers/provider-handlers.js +227 -0
- package/dist/src/integrations/mcp/server.js +316 -1
- package/dist/src/integrations/mcp/tool-definitions.js +90 -1
- package/dist/src/orchestrators/multimodal/baselines.js +128 -0
- package/dist/src/orchestrators/multimodal/constants.js +9 -1
- package/dist/src/orchestrators/multimodal/harness.js +86 -6
- package/dist/src/orchestrators/multimodal/providers.js +113 -2
- package/dist/src/skills/recursive-agent-orchestrator.js +15 -10
- package/dist/src/utils/fuzzy-edit.js +162 -0
- package/dist/src/utils/hook-installer.js +155 -0
- package/package.json +6 -2
- package/scripts/gepa/.before-optimize.md +159 -0
- package/scripts/gepa/generations/gen-000/baseline.md +159 -124
- package/scripts/gepa/generations/gen-001/baseline.md +159 -0
- package/scripts/gepa/generations/gen-001/variant-a.md +166 -0
- package/scripts/gepa/generations/gen-001/variant-b.md +237 -0
- package/scripts/gepa/generations/gen-001/variant-c.md +61 -0
- package/scripts/gepa/generations/gen-001/variant-d.md +119 -0
- package/scripts/gepa/results/eval-1-baseline.json +41 -0
- package/scripts/gepa/results/eval-1-variant-a.json +41 -0
- package/scripts/gepa/results/eval-1-variant-b.json +41 -0
- package/scripts/gepa/results/eval-1-variant-c.json +41 -0
- package/scripts/gepa/results/eval-1-variant-d.json +41 -0
- package/scripts/gepa/state.json +41 -2
- package/scripts/install-claude-hooks-auto.js +176 -44
- package/templates/claude-hooks/auto-checkpoint.js +174 -0
- package/templates/claude-hooks/chime-on-stop.sh +22 -0
- package/templates/claude-hooks/session-rescue.sh +15 -0
- package/templates/claude-hooks/stop-checkpoint.js +120 -0
|
@@ -32,12 +32,19 @@ class GraphitiClient {
|
|
|
32
32
|
const res = await fetch(`${this.endpoint}${path}`, {
|
|
33
33
|
...options,
|
|
34
34
|
signal: controller.signal,
|
|
35
|
-
headers: {
|
|
35
|
+
headers: {
|
|
36
|
+
"Content-Type": "application/json",
|
|
37
|
+
...options.headers || {}
|
|
38
|
+
}
|
|
36
39
|
});
|
|
37
40
|
clearTimeout(timeoutId);
|
|
38
41
|
if (!res.ok) {
|
|
39
42
|
const msg = await res.text().catch(() => res.statusText);
|
|
40
|
-
throw new GraphitiClientError(
|
|
43
|
+
throw new GraphitiClientError(
|
|
44
|
+
`Request failed: ${msg}`,
|
|
45
|
+
"HTTP_ERROR",
|
|
46
|
+
res.status
|
|
47
|
+
);
|
|
41
48
|
}
|
|
42
49
|
return await res.json();
|
|
43
50
|
} catch (err) {
|
|
@@ -53,7 +60,10 @@ class GraphitiClient {
|
|
|
53
60
|
}
|
|
54
61
|
}
|
|
55
62
|
clearTimeout(timeoutId);
|
|
56
|
-
throw new GraphitiClientError(
|
|
63
|
+
throw new GraphitiClientError(
|
|
64
|
+
lastError?.message || "Network error",
|
|
65
|
+
"NETWORK_ERROR"
|
|
66
|
+
);
|
|
57
67
|
}
|
|
58
68
|
// Episodes
|
|
59
69
|
async upsertEpisode(episode) {
|
|
@@ -91,7 +101,9 @@ class GraphitiClient {
|
|
|
91
101
|
// Health/status
|
|
92
102
|
async getStatus() {
|
|
93
103
|
try {
|
|
94
|
-
return await this.request(
|
|
104
|
+
return await this.request(
|
|
105
|
+
`/status?namespace=${encodeURIComponent(this.namespace)}`
|
|
106
|
+
);
|
|
95
107
|
} catch {
|
|
96
108
|
return { connected: false };
|
|
97
109
|
}
|
|
@@ -15,6 +15,9 @@ import {
|
|
|
15
15
|
import {
|
|
16
16
|
DiscoveryHandlers
|
|
17
17
|
} from "./discovery-handlers.js";
|
|
18
|
+
import {
|
|
19
|
+
ProviderHandlers
|
|
20
|
+
} from "./provider-handlers.js";
|
|
18
21
|
import {
|
|
19
22
|
ContextHandlers as ContextHandlers2
|
|
20
23
|
} from "./context-handlers.js";
|
|
@@ -23,11 +26,13 @@ import {
|
|
|
23
26
|
LinearHandlers as LinearHandlers2
|
|
24
27
|
} from "./linear-handlers.js";
|
|
25
28
|
import { TraceHandlers as TraceHandlers2 } from "./trace-handlers.js";
|
|
29
|
+
import { ProviderHandlers as ProviderHandlers2 } from "./provider-handlers.js";
|
|
26
30
|
class MCPHandlerFactory {
|
|
27
31
|
contextHandlers;
|
|
28
32
|
taskHandlers;
|
|
29
33
|
linearHandlers;
|
|
30
34
|
traceHandlers;
|
|
35
|
+
providerHandlers;
|
|
31
36
|
constructor(deps) {
|
|
32
37
|
this.contextHandlers = new ContextHandlers2({
|
|
33
38
|
frameManager: deps.frameManager
|
|
@@ -45,6 +50,7 @@ class MCPHandlerFactory {
|
|
|
45
50
|
traceDetector: deps.traceDetector,
|
|
46
51
|
browserMCP: deps.browserMCP
|
|
47
52
|
});
|
|
53
|
+
this.providerHandlers = new ProviderHandlers2();
|
|
48
54
|
}
|
|
49
55
|
/**
|
|
50
56
|
* Get handler for a specific tool
|
|
@@ -111,6 +117,19 @@ class MCPHandlerFactory {
|
|
|
111
117
|
return this.traceHandlers.handleStopBrowserDebug.bind(
|
|
112
118
|
this.traceHandlers
|
|
113
119
|
);
|
|
120
|
+
// Provider handlers
|
|
121
|
+
case "delegate_to_model":
|
|
122
|
+
return this.providerHandlers.handleDelegateToModel.bind(
|
|
123
|
+
this.providerHandlers
|
|
124
|
+
);
|
|
125
|
+
case "batch_submit":
|
|
126
|
+
return this.providerHandlers.handleBatchSubmit.bind(
|
|
127
|
+
this.providerHandlers
|
|
128
|
+
);
|
|
129
|
+
case "batch_check":
|
|
130
|
+
return this.providerHandlers.handleBatchCheck.bind(
|
|
131
|
+
this.providerHandlers
|
|
132
|
+
);
|
|
114
133
|
default:
|
|
115
134
|
throw new Error(`Unknown tool: ${toolName}`);
|
|
116
135
|
}
|
|
@@ -144,7 +163,11 @@ class MCPHandlerFactory {
|
|
|
144
163
|
"start_browser_debug",
|
|
145
164
|
"take_screenshot",
|
|
146
165
|
"execute_script",
|
|
147
|
-
"stop_browser_debug"
|
|
166
|
+
"stop_browser_debug",
|
|
167
|
+
// Provider tools (conditionally active)
|
|
168
|
+
"delegate_to_model",
|
|
169
|
+
"batch_submit",
|
|
170
|
+
"batch_check"
|
|
148
171
|
];
|
|
149
172
|
}
|
|
150
173
|
/**
|
|
@@ -159,6 +182,7 @@ export {
|
|
|
159
182
|
DiscoveryHandlers,
|
|
160
183
|
LinearHandlers,
|
|
161
184
|
MCPHandlerFactory,
|
|
185
|
+
ProviderHandlers,
|
|
162
186
|
TaskHandlers,
|
|
163
187
|
TraceHandlers
|
|
164
188
|
};
|
|
@@ -0,0 +1,227 @@
|
|
|
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 { logger } from "../../../core/monitoring/logger.js";
|
|
6
|
+
import { isFeatureEnabled } from "../../../core/config/feature-flags.js";
|
|
7
|
+
import {
|
|
8
|
+
createProvider
|
|
9
|
+
} from "../../../core/extensions/provider-adapter.js";
|
|
10
|
+
import {
|
|
11
|
+
getOptimalProvider
|
|
12
|
+
} from "../../../core/models/model-router.js";
|
|
13
|
+
import { scoreComplexity } from "../../../core/models/complexity-scorer.js";
|
|
14
|
+
import {
|
|
15
|
+
AnthropicBatchClient
|
|
16
|
+
} from "../../anthropic/batch-client.js";
|
|
17
|
+
function errorResponse(err) {
|
|
18
|
+
return {
|
|
19
|
+
content: [{ type: "text", text: JSON.stringify(err, null, 2) }]
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function classifyApiError(error, provider) {
|
|
23
|
+
const msg = error.message || String(error);
|
|
24
|
+
const status = error.status || (msg.match(/(\d{3})/)?.[1] ? parseInt(msg.match(/(\d{3})/)[1]) : void 0);
|
|
25
|
+
if (status === 429) {
|
|
26
|
+
return {
|
|
27
|
+
errorType: "rate_limit",
|
|
28
|
+
message: msg,
|
|
29
|
+
recommendation: `Rate limited by ${provider}. Retry after a delay or switch to a different provider.`,
|
|
30
|
+
provider
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (status && status >= 500) {
|
|
34
|
+
return {
|
|
35
|
+
errorType: "server_error",
|
|
36
|
+
message: msg,
|
|
37
|
+
recommendation: `${provider} returned a server error. Try a different provider or retry later.`,
|
|
38
|
+
provider
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
errorType: "api_error",
|
|
43
|
+
message: msg,
|
|
44
|
+
recommendation: `API call to ${provider} failed. Check the model name, API key, and base URL.`,
|
|
45
|
+
provider
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
class ProviderHandlers {
|
|
49
|
+
batchClient;
|
|
50
|
+
constructor(_deps) {
|
|
51
|
+
}
|
|
52
|
+
getBatchClient() {
|
|
53
|
+
if (!this.batchClient) {
|
|
54
|
+
this.batchClient = new AnthropicBatchClient();
|
|
55
|
+
}
|
|
56
|
+
return this.batchClient;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* delegate_to_model — route a prompt to a specific provider+model
|
|
60
|
+
*/
|
|
61
|
+
async handleDelegateToModel(args) {
|
|
62
|
+
if (!isFeatureEnabled("multiProvider")) {
|
|
63
|
+
return errorResponse({
|
|
64
|
+
errorType: "feature_disabled",
|
|
65
|
+
message: "Multi-provider routing is disabled.",
|
|
66
|
+
recommendation: "Set STACKMEMORY_MULTI_PROVIDER=true to enable multi-provider routing."
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
const taskType = args.taskType || "default";
|
|
70
|
+
const preference = args.provider;
|
|
71
|
+
const complexity = scoreComplexity(args.prompt);
|
|
72
|
+
const optimal = getOptimalProvider(taskType, preference, {
|
|
73
|
+
task: args.prompt
|
|
74
|
+
});
|
|
75
|
+
logger.info("delegate_to_model routing", {
|
|
76
|
+
taskType,
|
|
77
|
+
complexity: complexity.tier,
|
|
78
|
+
score: complexity.score,
|
|
79
|
+
provider: optimal.provider
|
|
80
|
+
});
|
|
81
|
+
const providerModel = args.model || optimal.model;
|
|
82
|
+
const apiKey = process.env[optimal.apiKeyEnv] || "";
|
|
83
|
+
if (!apiKey) {
|
|
84
|
+
return errorResponse({
|
|
85
|
+
errorType: "missing_api_key",
|
|
86
|
+
message: `No API key found for ${optimal.provider} (env: ${optimal.apiKeyEnv})`,
|
|
87
|
+
recommendation: `Set the ${optimal.apiKeyEnv} environment variable or choose a different provider.`,
|
|
88
|
+
provider: optimal.provider
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
const adapter = createProvider(optimal.provider, {
|
|
93
|
+
apiKey,
|
|
94
|
+
baseUrl: optimal.baseUrl
|
|
95
|
+
});
|
|
96
|
+
const messages = [{ role: "user", content: args.prompt }];
|
|
97
|
+
const result = await adapter.complete(messages, {
|
|
98
|
+
model: providerModel,
|
|
99
|
+
maxTokens: args.maxTokens || 4096,
|
|
100
|
+
temperature: args.temperature,
|
|
101
|
+
system: args.system
|
|
102
|
+
});
|
|
103
|
+
const text = result.content.filter((c) => c.type === "text").map((c) => c.text).join("");
|
|
104
|
+
return {
|
|
105
|
+
content: [
|
|
106
|
+
{
|
|
107
|
+
type: "text",
|
|
108
|
+
text: JSON.stringify(
|
|
109
|
+
{
|
|
110
|
+
provider: optimal.provider,
|
|
111
|
+
model: providerModel,
|
|
112
|
+
response: text,
|
|
113
|
+
usage: result.usage
|
|
114
|
+
},
|
|
115
|
+
null,
|
|
116
|
+
2
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
]
|
|
120
|
+
};
|
|
121
|
+
} catch (error) {
|
|
122
|
+
logger.error("delegate_to_model failed", { error: error.message });
|
|
123
|
+
return errorResponse(classifyApiError(error, optimal.provider));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* batch_submit — submit prompts to Anthropic Batch API
|
|
128
|
+
*/
|
|
129
|
+
async handleBatchSubmit(args) {
|
|
130
|
+
if (!isFeatureEnabled("multiProvider")) {
|
|
131
|
+
return errorResponse({
|
|
132
|
+
errorType: "feature_disabled",
|
|
133
|
+
message: "Multi-provider routing is disabled.",
|
|
134
|
+
recommendation: "Set STACKMEMORY_MULTI_PROVIDER=true to enable multi-provider routing."
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
const batchClient = this.getBatchClient();
|
|
139
|
+
const requests = args.prompts.map((p) => ({
|
|
140
|
+
custom_id: p.id,
|
|
141
|
+
params: {
|
|
142
|
+
model: p.model || "claude-sonnet-4-5-20250929",
|
|
143
|
+
max_tokens: p.maxTokens || 4096,
|
|
144
|
+
messages: [{ role: "user", content: p.prompt }],
|
|
145
|
+
system: p.system
|
|
146
|
+
}
|
|
147
|
+
}));
|
|
148
|
+
const batchId = await batchClient.submit(requests, args.description);
|
|
149
|
+
return {
|
|
150
|
+
content: [
|
|
151
|
+
{
|
|
152
|
+
type: "text",
|
|
153
|
+
text: JSON.stringify(
|
|
154
|
+
{
|
|
155
|
+
batchId,
|
|
156
|
+
status: "submitted",
|
|
157
|
+
requestCount: requests.length
|
|
158
|
+
},
|
|
159
|
+
null,
|
|
160
|
+
2
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
]
|
|
164
|
+
};
|
|
165
|
+
} catch (error) {
|
|
166
|
+
return errorResponse({
|
|
167
|
+
errorType: "batch_error",
|
|
168
|
+
message: error.message,
|
|
169
|
+
recommendation: "Check ANTHROPIC_API_KEY and batch request format."
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* batch_check — poll status / retrieve results
|
|
175
|
+
*/
|
|
176
|
+
async handleBatchCheck(args) {
|
|
177
|
+
if (!isFeatureEnabled("multiProvider")) {
|
|
178
|
+
return errorResponse({
|
|
179
|
+
errorType: "feature_disabled",
|
|
180
|
+
message: "Multi-provider routing is disabled.",
|
|
181
|
+
recommendation: "Set STACKMEMORY_MULTI_PROVIDER=true to enable multi-provider routing."
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
try {
|
|
185
|
+
const batchClient = this.getBatchClient();
|
|
186
|
+
const job = await batchClient.poll(args.batchId);
|
|
187
|
+
if (args.retrieve && job.processing_status === "ended") {
|
|
188
|
+
const results = await batchClient.retrieve(args.batchId);
|
|
189
|
+
return {
|
|
190
|
+
content: [
|
|
191
|
+
{
|
|
192
|
+
type: "text",
|
|
193
|
+
text: JSON.stringify({ job, results }, null, 2)
|
|
194
|
+
}
|
|
195
|
+
]
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
content: [
|
|
200
|
+
{
|
|
201
|
+
type: "text",
|
|
202
|
+
text: JSON.stringify(
|
|
203
|
+
{
|
|
204
|
+
batchId: args.batchId,
|
|
205
|
+
status: job.processing_status,
|
|
206
|
+
counts: job.request_counts,
|
|
207
|
+
createdAt: job.created_at,
|
|
208
|
+
endedAt: job.ended_at
|
|
209
|
+
},
|
|
210
|
+
null,
|
|
211
|
+
2
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
]
|
|
215
|
+
};
|
|
216
|
+
} catch (error) {
|
|
217
|
+
return errorResponse({
|
|
218
|
+
errorType: "batch_error",
|
|
219
|
+
message: error.message,
|
|
220
|
+
recommendation: "Check the batchId is valid and ANTHROPIC_API_KEY is set."
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
export {
|
|
226
|
+
ProviderHandlers
|
|
227
|
+
};
|