@adaptiqai/deepseek-harness 0.2.3 → 0.2.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/lib/client.js +1 -1
- package/lib/index.js +190 -46
- package/package.json +2 -2
package/lib/index.js
CHANGED
|
@@ -140,7 +140,10 @@ function classifyFailure(status, raw, headers, model2) {
|
|
|
140
140
|
return make(CODES.invalidRequest, `AdaptiqAI couldn't accept this request${said}.`, null);
|
|
141
141
|
}
|
|
142
142
|
if (effective !== null && effective >= 500) {
|
|
143
|
-
|
|
143
|
+
const who = model2 ?? "AdaptiqAI";
|
|
144
|
+
if (effective === 503)
|
|
145
|
+
return make(CODES.server, `${model2 ?? "This model"} is offline right now. It will retry automatically.`, "retrying");
|
|
146
|
+
return make(CODES.server, `${who} didn't answer just now. It will retry automatically.`, "retrying");
|
|
144
147
|
}
|
|
145
148
|
if (effective === null) {
|
|
146
149
|
return make(CODES.server, detail.message !== null ? `AdaptiqAI stopped the reply: ${detail.message.slice(0, 200)}` : "AdaptiqAI stopped the reply.", null);
|
|
@@ -149,18 +152,29 @@ function classifyFailure(status, raw, headers, model2) {
|
|
|
149
152
|
}
|
|
150
153
|
function transportFailure(error) {
|
|
151
154
|
const name2 = error instanceof Error ? error.name : "";
|
|
152
|
-
const
|
|
155
|
+
const cause = error instanceof Error ? error.cause?.code : void 0;
|
|
156
|
+
const timeout = name2 === "TimeoutError" || cause === "UND_ERR_HEADERS_TIMEOUT" || cause === "UND_ERR_BODY_TIMEOUT";
|
|
153
157
|
return {
|
|
154
158
|
code: timeout ? CODES.timeout : CODES.transport,
|
|
155
|
-
message: timeout ? "AdaptiqAI took too long to answer. It will retry automatically." : "AdaptiqAI
|
|
159
|
+
message: timeout ? "AdaptiqAI took too long to answer. It will retry automatically." : "AdaptiqAI can't be reached right now. It will retry automatically.",
|
|
156
160
|
status: null,
|
|
157
161
|
requestId: null,
|
|
158
162
|
retryAfterMs: null,
|
|
159
163
|
serverType: null,
|
|
160
164
|
serverCode: null,
|
|
161
|
-
notice: "offline"
|
|
165
|
+
notice: timeout ? "retrying" : "offline"
|
|
162
166
|
};
|
|
163
167
|
}
|
|
168
|
+
function failureReach(failure) {
|
|
169
|
+
if (failure.code === CODES.transport)
|
|
170
|
+
return "unreachable";
|
|
171
|
+
if (failure.code === CODES.timeout)
|
|
172
|
+
return "transient";
|
|
173
|
+
return failure.status !== null && failure.status >= 500 && failure.status !== 507 ? "transient" : null;
|
|
174
|
+
}
|
|
175
|
+
function errorReach(error) {
|
|
176
|
+
return error instanceof AdaptiqHttpError ? failureReach(error.failure) : null;
|
|
177
|
+
}
|
|
164
178
|
var AdaptiqHttpError = class extends Error {
|
|
165
179
|
failure;
|
|
166
180
|
body;
|
|
@@ -1261,6 +1275,11 @@ var str4 = (value) => typeof value === "string" && value.trim() !== "" ? value.t
|
|
|
1261
1275
|
var count2 = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
|
|
1262
1276
|
var DEFAULT_PREFERENCES = { rememberByDefault: true, welcomed: false };
|
|
1263
1277
|
var PAGE = 50;
|
|
1278
|
+
var TROUBLE_QUIET_MS = 6e4;
|
|
1279
|
+
var SESSION_BACKOFF_MAX_MS = 5 * 6e4;
|
|
1280
|
+
function sessionBackoffMs(failures) {
|
|
1281
|
+
return Math.min(SESSION_BACKOFF_MAX_MS, 3e4 * 2 ** Math.max(1, failures));
|
|
1282
|
+
}
|
|
1264
1283
|
function copyKey(source, target) {
|
|
1265
1284
|
return `${source}\0${target ?? ""}`;
|
|
1266
1285
|
}
|
|
@@ -1436,6 +1455,10 @@ var Workspace = class {
|
|
|
1436
1455
|
catalog = [];
|
|
1437
1456
|
catalogKey = "";
|
|
1438
1457
|
catalogAt = 0;
|
|
1458
|
+
/** The last `/v1/models` rows read: one stalled engine can fail the list for a while. */
|
|
1459
|
+
modelRows = [];
|
|
1460
|
+
/** Background reads have failed for a passing reason since `since`. */
|
|
1461
|
+
trouble = null;
|
|
1439
1462
|
offers = [];
|
|
1440
1463
|
checkoutMode = null;
|
|
1441
1464
|
/** Offer id → what the account summary says about it (model id, console link). */
|
|
@@ -1655,7 +1678,8 @@ var Workspace = class {
|
|
|
1655
1678
|
return;
|
|
1656
1679
|
this.timer = setTimeout(() => {
|
|
1657
1680
|
void this.refresh().finally(() => {
|
|
1658
|
-
this.
|
|
1681
|
+
const normal = this.listeners.size > 0 ? this.deps.watchedRefreshMs : this.deps.refreshMs;
|
|
1682
|
+
this.schedule(this.trouble === null ? normal : Math.min(normal, 15e3 * 2 ** Math.min(this.trouble.failures - 1, 6)));
|
|
1659
1683
|
});
|
|
1660
1684
|
}, ms);
|
|
1661
1685
|
this.timer.unref?.();
|
|
@@ -1690,8 +1714,17 @@ var Workspace = class {
|
|
|
1690
1714
|
this.setCatalog([]);
|
|
1691
1715
|
return;
|
|
1692
1716
|
}
|
|
1693
|
-
|
|
1694
|
-
|
|
1717
|
+
let data;
|
|
1718
|
+
try {
|
|
1719
|
+
const rows = await this.deps.api.json("/v1/models");
|
|
1720
|
+
data = Array.isArray(rows?.data) ? rows.data : [];
|
|
1721
|
+
this.modelRows = data;
|
|
1722
|
+
} catch (error) {
|
|
1723
|
+
if (errorReach(error) === null || this.modelRows.length === 0)
|
|
1724
|
+
throw error;
|
|
1725
|
+
this.deps.log?.("warn", `adaptiq: model list unavailable, keeping the last one: ${error instanceof Error ? error.message : String(error)}`);
|
|
1726
|
+
data = this.modelRows;
|
|
1727
|
+
}
|
|
1695
1728
|
const summary2 = await this.loadSummary();
|
|
1696
1729
|
let availability;
|
|
1697
1730
|
if (summary2 !== null) {
|
|
@@ -1708,19 +1741,33 @@ var Workspace = class {
|
|
|
1708
1741
|
if (this.memory === null || !this.memory.available)
|
|
1709
1742
|
await this.loadFallbackMemory();
|
|
1710
1743
|
this.lastError = null;
|
|
1744
|
+
this.trouble = null;
|
|
1711
1745
|
this.refreshedAt = this.now();
|
|
1712
1746
|
this.evaluateNotices();
|
|
1713
1747
|
} catch (error) {
|
|
1714
1748
|
const failure = error instanceof AdaptiqHttpError ? error.failure : null;
|
|
1715
1749
|
this.lastError = { code: failure?.code ?? "UNKNOWN", message: failure?.message ?? (error instanceof Error ? error.message : String(error)), at: this.now() };
|
|
1716
|
-
|
|
1717
|
-
|
|
1750
|
+
const reach = failure === null ? null : failureReach(failure);
|
|
1751
|
+
if (reach !== null) {
|
|
1752
|
+
const since = this.trouble?.since ?? this.now();
|
|
1753
|
+
this.trouble = { since, reach, failures: (this.trouble?.failures ?? 0) + 1 };
|
|
1754
|
+
if (this.now() - since >= TROUBLE_QUIET_MS)
|
|
1755
|
+
this.raiseTrouble(reach);
|
|
1756
|
+
}
|
|
1718
1757
|
this.deps.log?.("warn", `adaptiq: refresh failed: ${this.lastError.message}`);
|
|
1719
1758
|
} finally {
|
|
1720
1759
|
this.loading = false;
|
|
1721
1760
|
this.publish();
|
|
1722
1761
|
}
|
|
1723
1762
|
}
|
|
1763
|
+
/**
|
|
1764
|
+
* Say, calmly, that background reads keep failing: "busy" while AdaptiqAI
|
|
1765
|
+
* answers with errors, "can't be reached" only when there is no connection.
|
|
1766
|
+
* @param reach - the kind of trouble.
|
|
1767
|
+
*/
|
|
1768
|
+
raiseTrouble(reach) {
|
|
1769
|
+
this.raise(reach === "unreachable" ? { kind: "offline", tone: "warning", title: "AdaptiqAI can't be reached", body: "Retrying automatically. Your models and conversations stay as they are.", actions: [{ id: "refresh", label: "Try again" }] } : { kind: "offline", tone: "info", title: "AdaptiqAI is busy \u2014 retrying", body: "Your models and conversations stay as they are. This usually clears by itself.", actions: [] });
|
|
1770
|
+
}
|
|
1724
1771
|
setCatalog(models) {
|
|
1725
1772
|
const key = JSON.stringify(models);
|
|
1726
1773
|
this.catalogAt = this.now();
|
|
@@ -1733,8 +1780,13 @@ var Workspace = class {
|
|
|
1733
1780
|
if (session.listeners.size === 0)
|
|
1734
1781
|
continue;
|
|
1735
1782
|
this.emitSession(session);
|
|
1736
|
-
|
|
1783
|
+
const awaited = session.backoff?.model ?? null;
|
|
1784
|
+
if (awaited !== null && !this.modelDown(awaited)) {
|
|
1785
|
+
session.backoff = null;
|
|
1786
|
+
this.pollSession(session, 0);
|
|
1787
|
+
} else if (!session.exists && session.remoteAt === 0) {
|
|
1737
1788
|
this.pollSession(session, 0);
|
|
1789
|
+
}
|
|
1738
1790
|
}
|
|
1739
1791
|
}
|
|
1740
1792
|
async loadSummary() {
|
|
@@ -2085,6 +2137,9 @@ var Workspace = class {
|
|
|
2085
2137
|
knowledge: [],
|
|
2086
2138
|
conversations: [],
|
|
2087
2139
|
remoteAt: 0,
|
|
2140
|
+
home: null,
|
|
2141
|
+
notOn: /* @__PURE__ */ new Set(),
|
|
2142
|
+
backoff: null,
|
|
2088
2143
|
listeners: /* @__PURE__ */ new Set()
|
|
2089
2144
|
};
|
|
2090
2145
|
this.sessions.set(id, session);
|
|
@@ -2162,13 +2217,29 @@ var Workspace = class {
|
|
|
2162
2217
|
clearTimeout(session.timer);
|
|
2163
2218
|
if (this.disposed || session.listeners.size === 0)
|
|
2164
2219
|
return;
|
|
2220
|
+
const wait = session.backoff === null ? 0 : session.backoff.until - this.now();
|
|
2165
2221
|
session.timer = setTimeout(() => {
|
|
2166
2222
|
void this.loadSession(session).finally(() => {
|
|
2167
2223
|
this.pollSession(session, sessionPollDelay(session));
|
|
2168
2224
|
});
|
|
2169
|
-
}, ms);
|
|
2225
|
+
}, Math.max(ms, wait));
|
|
2170
2226
|
session.timer.unref?.();
|
|
2171
2227
|
}
|
|
2228
|
+
/** @returns whether a model's instance is offline or disabled (not asked about conversations). */
|
|
2229
|
+
modelDown(id) {
|
|
2230
|
+
const status = this.catalog.find((entry) => entry.id === id)?.status;
|
|
2231
|
+
return status === "offline" || status === "disabled";
|
|
2232
|
+
}
|
|
2233
|
+
/**
|
|
2234
|
+
* Read a conversation less often after a failed read or while its model is
|
|
2235
|
+
* offline: 1, 2, 4, then every 5 minutes.
|
|
2236
|
+
* @param session - the conversation.
|
|
2237
|
+
* @param model - the offline model it waits for, or null after a failed read.
|
|
2238
|
+
*/
|
|
2239
|
+
backOff(session, model2) {
|
|
2240
|
+
const failures = (session.backoff?.failures ?? 0) + 1;
|
|
2241
|
+
session.backoff = { failures, until: this.now() + sessionBackoffMs(failures), model: model2 };
|
|
2242
|
+
}
|
|
2172
2243
|
/**
|
|
2173
2244
|
* The memory store a conversation's knowledge and memory live in: where it is
|
|
2174
2245
|
* stored once it exists, otherwise the model selected for it.
|
|
@@ -2255,42 +2326,23 @@ var Workspace = class {
|
|
|
2255
2326
|
return;
|
|
2256
2327
|
session.model = route.model;
|
|
2257
2328
|
}
|
|
2258
|
-
const
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
try {
|
|
2263
|
-
body = await this.deps.api.json(path, { allow: [404] });
|
|
2264
|
-
routed = true;
|
|
2265
|
-
} catch (error) {
|
|
2266
|
-
if (!(error instanceof AdaptiqHttpError) || error.status !== 409 && error.status !== 400)
|
|
2267
|
-
throw error;
|
|
2268
|
-
const models = obj4(obj4(error.body)?.detail)?.models;
|
|
2269
|
-
if (Array.isArray(models))
|
|
2270
|
-
suggested = models.filter((model2) => typeof model2 === "string" && model2 !== "");
|
|
2271
|
-
}
|
|
2272
|
-
const candidates = routed ? [] : session.model !== null ? [session.model] : suggested.length > 0 ? suggested.slice(0, 6) : [...this.catalog].sort((a, b) => Number(b.status === "running") - Number(a.status === "running")).map((entry) => entry.id).slice(0, 6);
|
|
2273
|
-
for (const model2 of candidates) {
|
|
2274
|
-
body = await this.deps.api.json(path, { headers: { "x-adaptiq-model": model2 }, allow: [404] });
|
|
2275
|
-
if (body !== null) {
|
|
2276
|
-
if (session.model === null) {
|
|
2277
|
-
session.model = model2;
|
|
2278
|
-
session.onAdaptiq ??= true;
|
|
2279
|
-
}
|
|
2280
|
-
break;
|
|
2281
|
-
}
|
|
2282
|
-
}
|
|
2283
|
-
if (routed && body !== null) {
|
|
2284
|
-
const served = str4(obj4(body.session)?.model) ?? str4(body.model);
|
|
2285
|
-
if (served !== null)
|
|
2286
|
-
session.model = served;
|
|
2287
|
-
else if (session.model === null && session.lastUsed !== null)
|
|
2288
|
-
session.model = session.lastUsed;
|
|
2289
|
-
session.onAdaptiq ??= true;
|
|
2329
|
+
const found = await this.findSession(session);
|
|
2330
|
+
if (found === void 0) {
|
|
2331
|
+
session.remoteAt = this.now();
|
|
2332
|
+
return;
|
|
2290
2333
|
}
|
|
2291
|
-
|
|
2334
|
+
session.backoff = null;
|
|
2335
|
+
if (found.asked)
|
|
2292
2336
|
session.remoteAt = this.now();
|
|
2337
|
+
const body = found.body;
|
|
2293
2338
|
if (body === null) {
|
|
2339
|
+
if (session.exists) {
|
|
2340
|
+
session.memoryTokens = null;
|
|
2341
|
+
session.saved = null;
|
|
2342
|
+
session.optimizing = false;
|
|
2343
|
+
session.revision = null;
|
|
2344
|
+
session.conversations = [];
|
|
2345
|
+
}
|
|
2294
2346
|
session.exists = false;
|
|
2295
2347
|
session.knowledge = [];
|
|
2296
2348
|
return;
|
|
@@ -2314,6 +2366,7 @@ var Workspace = class {
|
|
|
2314
2366
|
session.conversations = await this.attachedConversations(session).catch(() => session.conversations);
|
|
2315
2367
|
} catch (error) {
|
|
2316
2368
|
session.remoteAt = this.now();
|
|
2369
|
+
this.backOff(session, null);
|
|
2317
2370
|
this.deps.log?.("warn", `adaptiq: could not read conversation ${session.id}: ${error instanceof Error ? error.message : String(error)}`);
|
|
2318
2371
|
if (!(error instanceof AdaptiqHttpError))
|
|
2319
2372
|
throw error;
|
|
@@ -2329,6 +2382,83 @@ var Workspace = class {
|
|
|
2329
2382
|
session.loading = void 0;
|
|
2330
2383
|
}
|
|
2331
2384
|
}
|
|
2385
|
+
/**
|
|
2386
|
+
* Find a conversation on the server. Once a model has answered for it, only
|
|
2387
|
+
* that model is asked. Before that, the session-aware route (which needs no
|
|
2388
|
+
* model), then, on gateways that want one, the conversation's model or the
|
|
2389
|
+
* models the gateway names: never a model that is offline or that already
|
|
2390
|
+
* said the conversation is not there.
|
|
2391
|
+
* @param session - the conversation.
|
|
2392
|
+
* @returns its body (null when the server does not have it) and whether
|
|
2393
|
+
* anything could be asked, or undefined when its model is offline (backed off).
|
|
2394
|
+
*/
|
|
2395
|
+
async findSession(session) {
|
|
2396
|
+
const path = `/v1/sessions/${encodeURIComponent(session.id)}`;
|
|
2397
|
+
const ask = async (model2) => {
|
|
2398
|
+
try {
|
|
2399
|
+
return await this.deps.api.json(path, { headers: { "x-adaptiq-model": model2 }, allow: [404] });
|
|
2400
|
+
} catch (error) {
|
|
2401
|
+
if (error instanceof AdaptiqHttpError && error.status === 409)
|
|
2402
|
+
return null;
|
|
2403
|
+
throw error;
|
|
2404
|
+
}
|
|
2405
|
+
};
|
|
2406
|
+
if (session.home !== null) {
|
|
2407
|
+
const home = session.home;
|
|
2408
|
+
if (this.modelDown(home)) {
|
|
2409
|
+
this.backOff(session, home);
|
|
2410
|
+
return void 0;
|
|
2411
|
+
}
|
|
2412
|
+
const body = await ask(home);
|
|
2413
|
+
if (body === null) {
|
|
2414
|
+
session.home = null;
|
|
2415
|
+
session.notOn.add(home);
|
|
2416
|
+
} else
|
|
2417
|
+
session.model = home;
|
|
2418
|
+
return { body, asked: true };
|
|
2419
|
+
}
|
|
2420
|
+
let suggested = [];
|
|
2421
|
+
try {
|
|
2422
|
+
const body = await this.deps.api.json(path, { allow: [404] });
|
|
2423
|
+
if (body !== null) {
|
|
2424
|
+
const served = str4(obj4(body.session)?.model) ?? str4(body.model);
|
|
2425
|
+
if (served !== null) {
|
|
2426
|
+
session.model = served;
|
|
2427
|
+
session.home = served;
|
|
2428
|
+
} else if (session.model === null && session.lastUsed !== null)
|
|
2429
|
+
session.model = session.lastUsed;
|
|
2430
|
+
session.onAdaptiq ??= true;
|
|
2431
|
+
}
|
|
2432
|
+
return { body, asked: true };
|
|
2433
|
+
} catch (error) {
|
|
2434
|
+
if (!(error instanceof AdaptiqHttpError) || error.status !== 409 && error.status !== 400)
|
|
2435
|
+
throw error;
|
|
2436
|
+
const models = obj4(obj4(error.body)?.detail)?.models;
|
|
2437
|
+
if (Array.isArray(models))
|
|
2438
|
+
suggested = models.filter((model2) => typeof model2 === "string" && model2 !== "");
|
|
2439
|
+
}
|
|
2440
|
+
const listed = session.model !== null ? [session.model] : suggested.length > 0 ? suggested : [...this.catalog].sort((a, b) => Number(b.status === "running") - Number(a.status === "running")).map((entry) => entry.id);
|
|
2441
|
+
const open = listed.filter((model2) => !session.notOn.has(model2)).slice(0, 6);
|
|
2442
|
+
const candidates = open.filter((model2) => !this.modelDown(model2));
|
|
2443
|
+
for (const model2 of candidates) {
|
|
2444
|
+
const body = await ask(model2);
|
|
2445
|
+
if (body !== null) {
|
|
2446
|
+
session.home = model2;
|
|
2447
|
+
if (session.model === null) {
|
|
2448
|
+
session.model = model2;
|
|
2449
|
+
session.onAdaptiq ??= true;
|
|
2450
|
+
}
|
|
2451
|
+
return { body, asked: true };
|
|
2452
|
+
}
|
|
2453
|
+
session.notOn.add(model2);
|
|
2454
|
+
}
|
|
2455
|
+
const offline = open.find((model2) => this.modelDown(model2));
|
|
2456
|
+
if (offline !== void 0) {
|
|
2457
|
+
this.backOff(session, offline);
|
|
2458
|
+
return void 0;
|
|
2459
|
+
}
|
|
2460
|
+
return { body: null, asked: candidates.length > 0 };
|
|
2461
|
+
}
|
|
2332
2462
|
/**
|
|
2333
2463
|
* Choose whether new messages of a conversation are saved.
|
|
2334
2464
|
* @param id - harness session id.
|
|
@@ -2387,6 +2517,8 @@ var Workspace = class {
|
|
|
2387
2517
|
const session = this.session(sessionId2);
|
|
2388
2518
|
session.onAdaptiq = true;
|
|
2389
2519
|
session.model = model2;
|
|
2520
|
+
session.notOn.delete(model2);
|
|
2521
|
+
session.backoff = null;
|
|
2390
2522
|
session.inflight++;
|
|
2391
2523
|
session.laneHeld = false;
|
|
2392
2524
|
session.failedNow = false;
|
|
@@ -2420,7 +2552,7 @@ var Workspace = class {
|
|
|
2420
2552
|
session.laneHeld = true;
|
|
2421
2553
|
session.retry = null;
|
|
2422
2554
|
this.setPhase(session, "responding");
|
|
2423
|
-
this.clearNotices("busy", "preparing", "offline");
|
|
2555
|
+
this.clearNotices("busy", "preparing", "offline", "retrying");
|
|
2424
2556
|
}
|
|
2425
2557
|
usage(sessionId2, model2, usage2) {
|
|
2426
2558
|
if (sessionId2 === void 0)
|
|
@@ -2503,8 +2635,20 @@ var Workspace = class {
|
|
|
2503
2635
|
case "model-unavailable":
|
|
2504
2636
|
this.raise({ kind: "model-unavailable", tone: "warning", title: `${name2} isn't available`, body: failure.message, actions: [{ id: "open-models", label: "See your models", url: this.links.models }] });
|
|
2505
2637
|
break;
|
|
2638
|
+
case "retrying": {
|
|
2639
|
+
const offline = entry?.status === "offline";
|
|
2640
|
+
this.raise({
|
|
2641
|
+
kind: "retrying",
|
|
2642
|
+
tone: "info",
|
|
2643
|
+
title: offline ? `${name2} is offline` : `${name2} didn't answer just now`,
|
|
2644
|
+
body: offline ? "Your request retries by itself. Your other AdaptiqAI models still work; pick one to continue now." : "Your request retries by itself.",
|
|
2645
|
+
actions: [],
|
|
2646
|
+
...sessionId2 === void 0 ? {} : { sessionId: sessionId2 }
|
|
2647
|
+
});
|
|
2648
|
+
break;
|
|
2649
|
+
}
|
|
2506
2650
|
case "offline":
|
|
2507
|
-
this.raise({ kind: "offline", tone: "warning", title: "AdaptiqAI can't be reached", body:
|
|
2651
|
+
this.raise({ kind: "offline", tone: "warning", title: "AdaptiqAI can't be reached", body: "Your request retries by itself once the connection is back.", actions: [], ...sessionId2 === void 0 ? {} : { sessionId: sessionId2 } });
|
|
2508
2652
|
break;
|
|
2509
2653
|
default:
|
|
2510
2654
|
break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adaptiqai/deepseek-harness",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "AdaptiqAI for DeepSeek Harness: models with 5M-token native memory and Knowledge. Sign up, get a model and work without leaving the harness",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh-plugin",
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"react-dom": "~18.3.1",
|
|
90
90
|
"typescript": "~5.9.3",
|
|
91
91
|
"vitest": "~3.2.7",
|
|
92
|
-
"@adaptiqai/core": "0.2.
|
|
92
|
+
"@adaptiqai/core": "0.2.4"
|
|
93
93
|
},
|
|
94
94
|
"repository": {
|
|
95
95
|
"type": "git",
|