@mem9/opencode 0.1.3 → 0.1.4
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/package.json +1 -1
- package/src/server/hooks.ts +17 -2
- package/src/server/ingest/submit.ts +13 -0
- package/src/server/quota-error.ts +259 -0
- package/src/server/server-backend.ts +16 -12
- package/src/server/tools.ts +10 -20
package/package.json
CHANGED
package/src/server/hooks.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { IngestMessage, MemoryBackend } from "./backend.ts";
|
|
|
3
3
|
import type { DebugLogger } from "./debug.ts";
|
|
4
4
|
import { selectMessagesForIngest } from "./ingest/select.ts";
|
|
5
5
|
import { submitMessagesForIngest } from "./ingest/submit.ts";
|
|
6
|
+
import { formatRuntimeQuotaNotice, parseRuntimeQuotaDenied } from "./quota-error.ts";
|
|
6
7
|
import { formatRecallBlock } from "./recall/format.ts";
|
|
7
8
|
import { buildRecallQuery } from "./recall/query.ts";
|
|
8
9
|
import type { SessionTranscriptLoader } from "./session-transcript.ts";
|
|
@@ -185,14 +186,16 @@ async function ingestSessionTranscript(
|
|
|
185
186
|
sessionID,
|
|
186
187
|
messageCount: selectedMessages.length,
|
|
187
188
|
});
|
|
188
|
-
await submitMessagesForIngest({
|
|
189
|
+
const result = await submitMessagesForIngest({
|
|
189
190
|
backend,
|
|
190
191
|
messages: transcript,
|
|
191
192
|
sessionID,
|
|
192
193
|
agentID: state.agentID,
|
|
193
194
|
debugLogger: options.debugLogger,
|
|
194
195
|
});
|
|
195
|
-
|
|
196
|
+
if (result) {
|
|
197
|
+
state.lastIngestFingerprint = fingerprint;
|
|
198
|
+
}
|
|
196
199
|
} finally {
|
|
197
200
|
if (state.pendingIngestFingerprint === fingerprint) {
|
|
198
201
|
state.pendingIngestFingerprint = null;
|
|
@@ -308,6 +311,18 @@ export function buildHooks(
|
|
|
308
311
|
output.system.push(block);
|
|
309
312
|
}
|
|
310
313
|
} catch (error) {
|
|
314
|
+
const quotaDenied = parseRuntimeQuotaDenied(error);
|
|
315
|
+
if (quotaDenied) {
|
|
316
|
+
await options.debugLogger?.("recall.quota_denied", {
|
|
317
|
+
sessionID: input.sessionID,
|
|
318
|
+
code: quotaDenied.code,
|
|
319
|
+
actionType: quotaDenied.recommendedAction?.type,
|
|
320
|
+
hasActionUrl: Boolean(quotaDenied.recommendedAction?.url),
|
|
321
|
+
});
|
|
322
|
+
output.system.push(formatRuntimeQuotaNotice(error, "recall paused"));
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
311
326
|
await options.debugLogger?.("recall.error", {
|
|
312
327
|
sessionID: input.sessionID,
|
|
313
328
|
error: error instanceof Error ? error.message : String(error),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { IngestInput, IngestResult, MemoryBackend } from "../backend.ts";
|
|
2
2
|
import type { DebugLogger } from "../debug.ts";
|
|
3
|
+
import { parseRuntimeQuotaDenied } from "../quota-error.ts";
|
|
3
4
|
import { selectMessagesForIngest } from "./select.ts";
|
|
4
5
|
|
|
5
6
|
export interface SubmitIngestOptions {
|
|
@@ -45,6 +46,18 @@ export async function submitMessagesForIngest(
|
|
|
45
46
|
});
|
|
46
47
|
return result;
|
|
47
48
|
} catch (error) {
|
|
49
|
+
const quotaDenied = parseRuntimeQuotaDenied(error);
|
|
50
|
+
if (quotaDenied) {
|
|
51
|
+
await options.debugLogger?.("ingest.quota_denied", {
|
|
52
|
+
sessionID: options.sessionID,
|
|
53
|
+
agentID: options.agentID,
|
|
54
|
+
code: quotaDenied.code,
|
|
55
|
+
actionType: quotaDenied.recommendedAction?.type,
|
|
56
|
+
hasActionUrl: Boolean(quotaDenied.recommendedAction?.url),
|
|
57
|
+
});
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
48
61
|
await options.debugLogger?.("ingest.error", {
|
|
49
62
|
sessionID: options.sessionID,
|
|
50
63
|
agentID: options.agentID,
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
export class Mem9HttpError extends Error {
|
|
2
|
+
constructor(
|
|
3
|
+
message: string,
|
|
4
|
+
readonly status: number,
|
|
5
|
+
readonly body: string,
|
|
6
|
+
readonly data: unknown,
|
|
7
|
+
) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "Mem9HttpError";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface RuntimeRecommendedAction {
|
|
14
|
+
providerActionCode?: string;
|
|
15
|
+
severity?: string;
|
|
16
|
+
type?: string;
|
|
17
|
+
url?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface RuntimeQuotaDenied {
|
|
21
|
+
status: number | null;
|
|
22
|
+
code: string;
|
|
23
|
+
message: string;
|
|
24
|
+
meter?: string;
|
|
25
|
+
quotaGateReason?: string;
|
|
26
|
+
retryAfterSeconds?: number;
|
|
27
|
+
recommendedAction?: RuntimeRecommendedAction;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
31
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function normalizeString(value: unknown): string {
|
|
35
|
+
return typeof value === "string" ? value.trim() : "";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function normalizePositiveInteger(value: unknown): number | null {
|
|
39
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function parseJsonOrUndefined(text: string): unknown {
|
|
43
|
+
if (!text.trim()) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
return JSON.parse(text) as unknown;
|
|
49
|
+
} catch {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function messageFromErrorBody(status: number, body: string, data: unknown): string {
|
|
55
|
+
if (isRecord(data)) {
|
|
56
|
+
const message = normalizeString(data.message);
|
|
57
|
+
if (message) {
|
|
58
|
+
return message;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const error = normalizeString(data.error);
|
|
62
|
+
if (error) {
|
|
63
|
+
return error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const text = body.trim();
|
|
68
|
+
return text || `HTTP ${status}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function normalizeRecommendedAction(runtimeQuota: Record<string, unknown>): RuntimeRecommendedAction | null {
|
|
72
|
+
const nested = isRecord(runtimeQuota.recommendedAction) ? runtimeQuota.recommendedAction : {};
|
|
73
|
+
const providerActionCode = normalizeString(nested.providerActionCode);
|
|
74
|
+
const severity = normalizeString(nested.severity);
|
|
75
|
+
const type = normalizeString(nested.type);
|
|
76
|
+
const url = normalizeString(nested.url);
|
|
77
|
+
|
|
78
|
+
if (!providerActionCode && !severity && !type && !url) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
...(providerActionCode ? { providerActionCode } : {}),
|
|
84
|
+
...(severity ? { severity } : {}),
|
|
85
|
+
...(type ? { type } : {}),
|
|
86
|
+
...(url ? { url } : {}),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function quotaGateReason(runtimeQuota: Record<string, unknown>): string {
|
|
91
|
+
const quotaGateResult = isRecord(runtimeQuota.quotaGateResult) ? runtimeQuota.quotaGateResult : {};
|
|
92
|
+
return normalizeString(quotaGateResult.reason);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function retryAfterSeconds(runtimeQuota: Record<string, unknown>): number | null {
|
|
96
|
+
const direct = normalizePositiveInteger(runtimeQuota.retryAfterSeconds);
|
|
97
|
+
if (direct !== null) {
|
|
98
|
+
return direct;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const quotaGateResult = isRecord(runtimeQuota.quotaGateResult) ? runtimeQuota.quotaGateResult : {};
|
|
102
|
+
const postQuotaRateLimit = isRecord(quotaGateResult.postQuotaRateLimit) ? quotaGateResult.postQuotaRateLimit : {};
|
|
103
|
+
return normalizePositiveInteger(postQuotaRateLimit.retryAfterSeconds);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function isPostQuotaRateLimited(denied: RuntimeQuotaDenied): boolean {
|
|
107
|
+
return denied.status === 429 ||
|
|
108
|
+
denied.quotaGateReason === "postQuotaRateLimitExceeded";
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function quotaReason(denied: RuntimeQuotaDenied): string {
|
|
112
|
+
if (isPostQuotaRateLimited(denied)) {
|
|
113
|
+
return "this API key has reached the temporary request limit for this memory feature";
|
|
114
|
+
}
|
|
115
|
+
const providerActionCode = normalizeString(denied.recommendedAction?.providerActionCode);
|
|
116
|
+
if (providerActionCode === "claimApiKey") {
|
|
117
|
+
return "the included usage quota for this API key has been used up";
|
|
118
|
+
}
|
|
119
|
+
if (providerActionCode === "increaseSpendingLimit") {
|
|
120
|
+
return "the configured spending limit would be exceeded";
|
|
121
|
+
}
|
|
122
|
+
if (providerActionCode === "enableOnDemand") {
|
|
123
|
+
return "the included usage quota has been used up and on-demand usage is not enabled";
|
|
124
|
+
}
|
|
125
|
+
if (providerActionCode === "upgradePlan") {
|
|
126
|
+
return "the included usage quota for this mem9 account has been used up";
|
|
127
|
+
}
|
|
128
|
+
if (providerActionCode === "resolveAccountState") {
|
|
129
|
+
return "the current account or billing state blocks runtime memory access";
|
|
130
|
+
}
|
|
131
|
+
return "the runtime quota check blocked this request";
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function quotaNoticeSubject(denied: RuntimeQuotaDenied, operation: string): { headline: string; userState: string } {
|
|
135
|
+
const meter = normalizeString(denied.meter);
|
|
136
|
+
if (meter === "memory_write_requests") {
|
|
137
|
+
return {
|
|
138
|
+
headline: "Mem9 memory saving is temporarily unavailable",
|
|
139
|
+
userState: "mem9 cannot save new memories right now",
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
if (meter === "memory_recall_requests") {
|
|
143
|
+
return {
|
|
144
|
+
headline: "Mem9 recall is temporarily unavailable",
|
|
145
|
+
userState: "mem9 cannot recall memories right now",
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const operationText = normalizeString(operation).toLowerCase();
|
|
150
|
+
if (/\b(ingest|save|store|write)\b/.test(operationText)) {
|
|
151
|
+
return {
|
|
152
|
+
headline: "Mem9 memory saving is temporarily unavailable",
|
|
153
|
+
userState: "mem9 cannot save new memories right now",
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
if (/\b(recall|search)\b/.test(operationText)) {
|
|
157
|
+
return {
|
|
158
|
+
headline: "Mem9 recall is temporarily unavailable",
|
|
159
|
+
userState: "mem9 cannot recall memories right now",
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
headline: "Mem9 memory is temporarily unavailable",
|
|
165
|
+
userState: "mem9 cannot complete the memory request right now",
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function actionUrlForDenied(denied: RuntimeQuotaDenied): string {
|
|
170
|
+
return normalizeString(denied.recommendedAction?.url);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function actionInstruction(denied: RuntimeQuotaDenied): string {
|
|
174
|
+
const action = denied.recommendedAction;
|
|
175
|
+
const providerActionCode = normalizeString(action?.providerActionCode);
|
|
176
|
+
const actionUrl = actionUrlForDenied(denied);
|
|
177
|
+
if (!actionUrl) {
|
|
178
|
+
if (isPostQuotaRateLimited(denied)) {
|
|
179
|
+
return "Tell them that the quota/rate-limit check blocked this request and to retry later or open the mem9 console to review account and billing settings.";
|
|
180
|
+
}
|
|
181
|
+
return "Ask them to open the mem9 console to resolve the account or billing state.";
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
switch (providerActionCode) {
|
|
185
|
+
case "claimApiKey":
|
|
186
|
+
return `Ask them to open this link to sign in or create a mem9 account and claim this API key: ${actionUrl}. After claiming the key, they can upgrade their plan or set up billing to get more usage. Include the link exactly as written.`;
|
|
187
|
+
case "upgradePlan":
|
|
188
|
+
return `Ask them to open this link to upgrade their mem9 plan and get more included usage: ${actionUrl}. Include the link exactly as written.`;
|
|
189
|
+
case "increaseSpendingLimit":
|
|
190
|
+
return `Ask them to open this link to increase the mem9 spending limit or adjust billing settings: ${actionUrl}. Include the link exactly as written.`;
|
|
191
|
+
case "enableOnDemand":
|
|
192
|
+
return `Ask them to open this link to enable billing or on-demand usage for mem9: ${actionUrl}. Include the link exactly as written.`;
|
|
193
|
+
case "resolveAccountState":
|
|
194
|
+
return `Ask them to open this mem9 link to resolve the account or billing state: ${actionUrl}. Include the link exactly as written.`;
|
|
195
|
+
default:
|
|
196
|
+
return `Ask them to open this mem9 link to resolve the account or billing state: ${actionUrl}. Include the link exactly as written.`;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function parseRuntimeQuotaDenied(value: unknown): RuntimeQuotaDenied | null {
|
|
201
|
+
const payload = value instanceof Mem9HttpError ? value.data : value;
|
|
202
|
+
if (!isRecord(payload)) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const details = isRecord(payload.details) ? payload.details : {};
|
|
207
|
+
if (normalizeString(details.errorCategory) !== "runtime_quota_denied") {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
const runtimeQuota = isRecord(details.runtimeQuota) ? details.runtimeQuota : {};
|
|
211
|
+
|
|
212
|
+
const recommendedAction = normalizeRecommendedAction(runtimeQuota);
|
|
213
|
+
const retryAfter = retryAfterSeconds(runtimeQuota);
|
|
214
|
+
const reason = quotaGateReason(runtimeQuota);
|
|
215
|
+
return {
|
|
216
|
+
status: value instanceof Mem9HttpError ? value.status : null,
|
|
217
|
+
code: "runtime_quota_denied",
|
|
218
|
+
message: normalizeString(payload.error) || "Runtime usage quota denied.",
|
|
219
|
+
...(normalizeString(runtimeQuota.meter) ? { meter: normalizeString(runtimeQuota.meter) } : {}),
|
|
220
|
+
...(reason ? { quotaGateReason: reason } : {}),
|
|
221
|
+
...(retryAfter !== null ? { retryAfterSeconds: retryAfter } : {}),
|
|
222
|
+
...(recommendedAction ? { recommendedAction } : {}),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export function formatRuntimeQuotaNotice(value: unknown, operation: string): string {
|
|
227
|
+
const denied = parseRuntimeQuotaDenied(value);
|
|
228
|
+
if (!denied) {
|
|
229
|
+
return "";
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const subject = quotaNoticeSubject(denied, operation);
|
|
233
|
+
return `${subject.headline} because ${quotaReason(denied)}. In your reply, briefly tell the user that ${subject.userState}. ${actionInstruction(denied)}`;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function toolErrorPayload(error: unknown): Record<string, unknown> {
|
|
237
|
+
const denied = parseRuntimeQuotaDenied(error);
|
|
238
|
+
if (denied) {
|
|
239
|
+
const actionUrl = actionUrlForDenied(denied);
|
|
240
|
+
return {
|
|
241
|
+
ok: false,
|
|
242
|
+
error: denied.message,
|
|
243
|
+
status_code: denied.status,
|
|
244
|
+
code: denied.code,
|
|
245
|
+
quota: {
|
|
246
|
+
code: denied.code,
|
|
247
|
+
message: denied.message,
|
|
248
|
+
...(denied.retryAfterSeconds !== undefined ? { retryAfterSeconds: denied.retryAfterSeconds } : {}),
|
|
249
|
+
...(denied.recommendedAction ? { recommendedAction: denied.recommendedAction } : {}),
|
|
250
|
+
},
|
|
251
|
+
...(actionUrl ? { action_url: actionUrl } : {}),
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
ok: false,
|
|
257
|
+
error: error instanceof Error ? error.message : String(error),
|
|
258
|
+
};
|
|
259
|
+
}
|
|
@@ -12,10 +12,11 @@ import type {
|
|
|
12
12
|
SearchInput,
|
|
13
13
|
} from "../shared/types.ts";
|
|
14
14
|
import { DEFAULT_SCOPE_CONFIG } from "../shared/defaults.ts";
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
import {
|
|
16
|
+
Mem9HttpError,
|
|
17
|
+
messageFromErrorBody,
|
|
18
|
+
parseJsonOrUndefined,
|
|
19
|
+
} from "./quota-error.ts";
|
|
19
20
|
|
|
20
21
|
function normalizeTimeoutMs(value: number | undefined, fallback: number): number {
|
|
21
22
|
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
@@ -82,13 +83,16 @@ export class ServerBackend implements MemoryBackend {
|
|
|
82
83
|
if (resp.status === 204) return undefined as T;
|
|
83
84
|
|
|
84
85
|
const text = await resp.text();
|
|
85
|
-
const data = text ? (JSON.parse(text) as unknown) : undefined;
|
|
86
86
|
if (!resp.ok) {
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
const data = parseJsonOrUndefined(text);
|
|
88
|
+
throw new Mem9HttpError(
|
|
89
|
+
messageFromErrorBody(resp.status, text, data),
|
|
90
|
+
resp.status,
|
|
91
|
+
text,
|
|
92
|
+
data,
|
|
93
|
+
);
|
|
90
94
|
}
|
|
91
|
-
return
|
|
95
|
+
return JSON.parse(text) as T;
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
async store(input: CreateMemoryInput): Promise<StoreResult> {
|
|
@@ -133,7 +137,7 @@ export class ServerBackend implements MemoryBackend {
|
|
|
133
137
|
try {
|
|
134
138
|
return await this.request<Memory>("GET", this.memoryPath(`/memories/${id}`));
|
|
135
139
|
} catch (err) {
|
|
136
|
-
if (err instanceof
|
|
140
|
+
if (err instanceof Mem9HttpError && err.status === 404) {
|
|
137
141
|
return null;
|
|
138
142
|
}
|
|
139
143
|
throw err;
|
|
@@ -144,7 +148,7 @@ export class ServerBackend implements MemoryBackend {
|
|
|
144
148
|
try {
|
|
145
149
|
return await this.request<Memory>("PUT", this.memoryPath(`/memories/${id}`), input);
|
|
146
150
|
} catch (err) {
|
|
147
|
-
if (err instanceof
|
|
151
|
+
if (err instanceof Mem9HttpError && err.status === 404) {
|
|
148
152
|
return null;
|
|
149
153
|
}
|
|
150
154
|
throw err;
|
|
@@ -156,7 +160,7 @@ export class ServerBackend implements MemoryBackend {
|
|
|
156
160
|
await this.request("DELETE", this.memoryPath(`/memories/${id}`));
|
|
157
161
|
return true;
|
|
158
162
|
} catch (err) {
|
|
159
|
-
if (err instanceof
|
|
163
|
+
if (err instanceof Mem9HttpError && err.status === 404) {
|
|
160
164
|
return false;
|
|
161
165
|
}
|
|
162
166
|
throw err;
|
package/src/server/tools.ts
CHANGED
|
@@ -5,6 +5,11 @@ import type {
|
|
|
5
5
|
UpdateMemoryInput,
|
|
6
6
|
SearchInput,
|
|
7
7
|
} from "../shared/types.ts";
|
|
8
|
+
import { toolErrorPayload } from "./quota-error.ts";
|
|
9
|
+
|
|
10
|
+
function jsonToolError(error: unknown): string {
|
|
11
|
+
return JSON.stringify(toolErrorPayload(error));
|
|
12
|
+
}
|
|
8
13
|
|
|
9
14
|
/**
|
|
10
15
|
* Build the 5 memory tools for OpenCode.
|
|
@@ -45,10 +50,7 @@ export function buildTools(backend: MemoryBackend): Record<string, ReturnType<ty
|
|
|
45
50
|
const result = await backend.store(input);
|
|
46
51
|
return JSON.stringify({ ok: true, data: result });
|
|
47
52
|
} catch (err) {
|
|
48
|
-
return
|
|
49
|
-
ok: false,
|
|
50
|
-
error: err instanceof Error ? err.message : String(err),
|
|
51
|
-
});
|
|
53
|
+
return jsonToolError(err);
|
|
52
54
|
}
|
|
53
55
|
},
|
|
54
56
|
}),
|
|
@@ -97,10 +99,7 @@ export function buildTools(backend: MemoryBackend): Record<string, ReturnType<ty
|
|
|
97
99
|
const result = await backend.search(input);
|
|
98
100
|
return JSON.stringify({ ok: true, ...result });
|
|
99
101
|
} catch (err) {
|
|
100
|
-
return
|
|
101
|
-
ok: false,
|
|
102
|
-
error: err instanceof Error ? err.message : String(err),
|
|
103
|
-
});
|
|
102
|
+
return jsonToolError(err);
|
|
104
103
|
}
|
|
105
104
|
},
|
|
106
105
|
}),
|
|
@@ -118,10 +117,7 @@ export function buildTools(backend: MemoryBackend): Record<string, ReturnType<ty
|
|
|
118
117
|
}
|
|
119
118
|
return JSON.stringify({ ok: true, data: result });
|
|
120
119
|
} catch (err) {
|
|
121
|
-
return
|
|
122
|
-
ok: false,
|
|
123
|
-
error: err instanceof Error ? err.message : String(err),
|
|
124
|
-
});
|
|
120
|
+
return jsonToolError(err);
|
|
125
121
|
}
|
|
126
122
|
},
|
|
127
123
|
}),
|
|
@@ -157,10 +153,7 @@ export function buildTools(backend: MemoryBackend): Record<string, ReturnType<ty
|
|
|
157
153
|
}
|
|
158
154
|
return JSON.stringify({ ok: true, data: result });
|
|
159
155
|
} catch (err) {
|
|
160
|
-
return
|
|
161
|
-
ok: false,
|
|
162
|
-
error: err instanceof Error ? err.message : String(err),
|
|
163
|
-
});
|
|
156
|
+
return jsonToolError(err);
|
|
164
157
|
}
|
|
165
158
|
},
|
|
166
159
|
}),
|
|
@@ -178,10 +171,7 @@ export function buildTools(backend: MemoryBackend): Record<string, ReturnType<ty
|
|
|
178
171
|
}
|
|
179
172
|
return JSON.stringify({ ok: true });
|
|
180
173
|
} catch (err) {
|
|
181
|
-
return
|
|
182
|
-
ok: false,
|
|
183
|
-
error: err instanceof Error ? err.message : String(err),
|
|
184
|
-
});
|
|
174
|
+
return jsonToolError(err);
|
|
185
175
|
}
|
|
186
176
|
},
|
|
187
177
|
}),
|