@adaptiqai/deepseek-harness 0.2.2 → 0.2.3
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 +4 -4
- package/lib/index.js +101 -8
- package/lib/types/client/Connect.d.ts +25 -4
- package/lib/types/client/SettingsPage.d.ts +10 -1
- package/lib/types/client/pill.d.ts +59 -0
- package/lib/types/client/ui.d.ts +11 -5
- package/package.json +2 -2
package/lib/index.js
CHANGED
|
@@ -699,6 +699,24 @@ function formatPercent(fraction) {
|
|
|
699
699
|
return "<1%";
|
|
700
700
|
return `${Math.min(100, Math.round(fraction * 100))}%`;
|
|
701
701
|
}
|
|
702
|
+
var QUIET_START_MS = 2e3;
|
|
703
|
+
var LANES_FRESH_MS = 3e4;
|
|
704
|
+
function waitKind(request, lanes, refreshedAt, now) {
|
|
705
|
+
if (request.phase === "preparing")
|
|
706
|
+
return "preparing";
|
|
707
|
+
if (request.phase === "queued")
|
|
708
|
+
return "lane";
|
|
709
|
+
if (request.phase !== "starting")
|
|
710
|
+
return null;
|
|
711
|
+
if (request.since !== null && now - request.since < QUIET_START_MS)
|
|
712
|
+
return null;
|
|
713
|
+
const total = lanes?.lanes ?? null;
|
|
714
|
+
const running = lanes?.runningNow ?? null;
|
|
715
|
+
const fresh = refreshedAt !== null && now - refreshedAt <= LANES_FRESH_MS;
|
|
716
|
+
if (request.laneHeld !== true && fresh && total !== null && total > 0 && running !== null && running >= total)
|
|
717
|
+
return "lane";
|
|
718
|
+
return "starting";
|
|
719
|
+
}
|
|
702
720
|
|
|
703
721
|
// ../core/lib/catalog.js
|
|
704
722
|
var obj = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
@@ -1516,8 +1534,10 @@ var Workspace = class {
|
|
|
1516
1534
|
this.purchases.dispose();
|
|
1517
1535
|
clearTimeout(this.timer);
|
|
1518
1536
|
clearTimeout(this.attachTimer);
|
|
1519
|
-
for (const session of this.sessions.values())
|
|
1537
|
+
for (const session of this.sessions.values()) {
|
|
1520
1538
|
clearTimeout(session.timer);
|
|
1539
|
+
clearTimeout(session.waitTimer);
|
|
1540
|
+
}
|
|
1521
1541
|
this.listeners.clear();
|
|
1522
1542
|
}
|
|
1523
1543
|
/** Record that the provider route belongs to someone else. */
|
|
@@ -2049,6 +2069,10 @@ var Workspace = class {
|
|
|
2049
2069
|
onAdaptiq: route === void 0 ? null : route.provider === this.deps.route,
|
|
2050
2070
|
phase: "idle",
|
|
2051
2071
|
phaseSince: null,
|
|
2072
|
+
inflight: 0,
|
|
2073
|
+
laneHeld: false,
|
|
2074
|
+
retry: null,
|
|
2075
|
+
failedNow: false,
|
|
2052
2076
|
model: route?.provider === this.deps.route ? route.model : null,
|
|
2053
2077
|
selected: null,
|
|
2054
2078
|
lastUsed: null,
|
|
@@ -2090,6 +2114,7 @@ var Workspace = class {
|
|
|
2090
2114
|
remember: this.remember(id),
|
|
2091
2115
|
phase: session.phase,
|
|
2092
2116
|
phaseSince: session.phaseSince,
|
|
2117
|
+
waiting: this.waiting(session),
|
|
2093
2118
|
model: session.model,
|
|
2094
2119
|
modelName: this.modelName(session.model),
|
|
2095
2120
|
selectedModel: session.selected,
|
|
@@ -2322,6 +2347,38 @@ var Workspace = class {
|
|
|
2322
2347
|
session.phase = phase;
|
|
2323
2348
|
session.phaseSince = this.now();
|
|
2324
2349
|
this.emitSession(session);
|
|
2350
|
+
this.watchWait(session);
|
|
2351
|
+
}
|
|
2352
|
+
/** What the conversation's request waits for, as the composer says it. */
|
|
2353
|
+
waiting(session) {
|
|
2354
|
+
const entry = this.catalog.find((item) => item.id === session.model);
|
|
2355
|
+
return waitKind({ phase: session.phase, since: session.phaseSince, laneHeld: session.laneHeld }, entry ?? null, this.refreshedAt, this.now());
|
|
2356
|
+
}
|
|
2357
|
+
/**
|
|
2358
|
+
* A request that stays quiet past {@link QUIET_START_MS} gets a word: look
|
|
2359
|
+
* again then, with the lane counts read fresh when they are older than that.
|
|
2360
|
+
*/
|
|
2361
|
+
watchWait(session) {
|
|
2362
|
+
clearTimeout(session.waitTimer);
|
|
2363
|
+
if (session.phase !== "starting" || this.disposed)
|
|
2364
|
+
return;
|
|
2365
|
+
const check = () => {
|
|
2366
|
+
if (session.phase !== "starting" || this.disposed)
|
|
2367
|
+
return;
|
|
2368
|
+
this.emitSession(session);
|
|
2369
|
+
const stale = this.refreshedAt === null || this.now() - this.refreshedAt > 1e4;
|
|
2370
|
+
if (stale && !session.laneHeld && this.deps.auth.phase() === "connected") {
|
|
2371
|
+
void this.refresh().then(() => {
|
|
2372
|
+
if (session.phase === "starting")
|
|
2373
|
+
this.emitSession(session);
|
|
2374
|
+
}, () => {
|
|
2375
|
+
});
|
|
2376
|
+
}
|
|
2377
|
+
session.waitTimer = setTimeout(check, 15e3);
|
|
2378
|
+
session.waitTimer.unref?.();
|
|
2379
|
+
};
|
|
2380
|
+
session.waitTimer = setTimeout(check, QUIET_START_MS);
|
|
2381
|
+
session.waitTimer.unref?.();
|
|
2325
2382
|
}
|
|
2326
2383
|
// RequestEvents — the adapter reports each request here.
|
|
2327
2384
|
started(sessionId2, model2) {
|
|
@@ -2330,7 +2387,11 @@ var Workspace = class {
|
|
|
2330
2387
|
const session = this.session(sessionId2);
|
|
2331
2388
|
session.onAdaptiq = true;
|
|
2332
2389
|
session.model = model2;
|
|
2333
|
-
|
|
2390
|
+
session.inflight++;
|
|
2391
|
+
session.laneHeld = false;
|
|
2392
|
+
session.failedNow = false;
|
|
2393
|
+
const retry = session.retry !== null && session.retry.until > this.now() ? session.retry : null;
|
|
2394
|
+
this.setPhase(session, retry?.phase ?? "starting");
|
|
2334
2395
|
}
|
|
2335
2396
|
progress(sessionId2, stage) {
|
|
2336
2397
|
if (sessionId2 === void 0)
|
|
@@ -2340,13 +2401,25 @@ var Workspace = class {
|
|
|
2340
2401
|
return;
|
|
2341
2402
|
if (stage === "inference.queued")
|
|
2342
2403
|
this.setPhase(session, "queued");
|
|
2343
|
-
else if (stage === "context.preparing"
|
|
2404
|
+
else if (stage === "context.preparing")
|
|
2344
2405
|
this.setPhase(session, "preparing");
|
|
2406
|
+
else if (stage === "context.ready" || stage === "inference.ready") {
|
|
2407
|
+
if (stage === "inference.ready")
|
|
2408
|
+
session.laneHeld = true;
|
|
2409
|
+
session.retry = null;
|
|
2410
|
+
if (session.phase === "starting")
|
|
2411
|
+
this.emitSession(session);
|
|
2412
|
+
else
|
|
2413
|
+
this.setPhase(session, "starting");
|
|
2414
|
+
}
|
|
2345
2415
|
}
|
|
2346
2416
|
output(sessionId2) {
|
|
2347
2417
|
if (sessionId2 === void 0)
|
|
2348
2418
|
return;
|
|
2349
|
-
this.
|
|
2419
|
+
const session = this.session(sessionId2);
|
|
2420
|
+
session.laneHeld = true;
|
|
2421
|
+
session.retry = null;
|
|
2422
|
+
this.setPhase(session, "responding");
|
|
2350
2423
|
this.clearNotices("busy", "preparing", "offline");
|
|
2351
2424
|
}
|
|
2352
2425
|
usage(sessionId2, model2, usage2) {
|
|
@@ -2382,8 +2455,13 @@ var Workspace = class {
|
|
|
2382
2455
|
message: failure.message
|
|
2383
2456
|
});
|
|
2384
2457
|
this.failures.length = Math.min(this.failures.length, 12);
|
|
2385
|
-
if (sessionId2 !== void 0)
|
|
2386
|
-
this.
|
|
2458
|
+
if (sessionId2 !== void 0) {
|
|
2459
|
+
const session = this.session(sessionId2);
|
|
2460
|
+
session.failedNow = true;
|
|
2461
|
+
const waits = RETRYABLE_CODES.includes(failure.code) && (failure.notice === "busy" || failure.notice === "preparing");
|
|
2462
|
+
session.retry = waits ? { phase: failure.notice === "busy" ? "queued" : "preparing", until: this.now() + (failure.retryAfterMs ?? 3e4) + 15e3 } : null;
|
|
2463
|
+
this.setPhase(session, session.retry?.phase ?? "interrupted");
|
|
2464
|
+
}
|
|
2387
2465
|
const entry = this.catalog.find((item) => item.id === model2);
|
|
2388
2466
|
const name2 = entry?.name ?? model2;
|
|
2389
2467
|
switch (failure.notice) {
|
|
@@ -2436,8 +2514,23 @@ var Workspace = class {
|
|
|
2436
2514
|
if (sessionId2 === void 0)
|
|
2437
2515
|
return;
|
|
2438
2516
|
const session = this.session(sessionId2);
|
|
2439
|
-
|
|
2440
|
-
|
|
2517
|
+
session.inflight = Math.max(0, session.inflight - 1);
|
|
2518
|
+
if (session.failedNow && session.retry !== null) {
|
|
2519
|
+
const retry = session.retry;
|
|
2520
|
+
clearTimeout(session.waitTimer);
|
|
2521
|
+
session.waitTimer = setTimeout(() => {
|
|
2522
|
+
if (session.inflight === 0 && session.retry === retry) {
|
|
2523
|
+
session.retry = null;
|
|
2524
|
+
this.setPhase(session, "interrupted");
|
|
2525
|
+
}
|
|
2526
|
+
}, Math.max(0, retry.until - this.now()));
|
|
2527
|
+
session.waitTimer.unref?.();
|
|
2528
|
+
} else {
|
|
2529
|
+
if (!session.failedNow)
|
|
2530
|
+
session.retry = null;
|
|
2531
|
+
if (session.phase !== "interrupted")
|
|
2532
|
+
this.setPhase(session, "idle");
|
|
2533
|
+
}
|
|
2441
2534
|
if (session.listeners.size > 0) {
|
|
2442
2535
|
setTimeout(() => {
|
|
2443
2536
|
void this.loadSession(session);
|
|
@@ -1,18 +1,39 @@
|
|
|
1
|
-
import type { AccountSnapshot } from '@adaptiqai/core/types';
|
|
1
|
+
import type { AccountPhase, AccountSnapshot } from '@adaptiqai/core/types';
|
|
2
2
|
import type { Kit } from './actions.ts';
|
|
3
3
|
import { type T } from './ui.tsx';
|
|
4
|
+
/** How long "Connected" stays before the card moves on by itself. */
|
|
5
|
+
export declare const CONNECTED_MOMENT_MS = 1500;
|
|
6
|
+
/**
|
|
7
|
+
* The moment right after the browser approval: true from the switch
|
|
8
|
+
* signing-in → connected until {@link CONNECTED_MOMENT_MS} later or dismiss.
|
|
9
|
+
* @param phase - the account phase, or null before it is known.
|
|
10
|
+
* @returns whether to show it, and how to end it early.
|
|
11
|
+
*/
|
|
12
|
+
export declare function useConnectedMoment(phase: AccountPhase | null, ms?: number): {
|
|
13
|
+
show: boolean;
|
|
14
|
+
dismiss: () => void;
|
|
15
|
+
};
|
|
16
|
+
/** "Connected to <workspace>": the stones settle, a check lands. Clicking anywhere ends it. */
|
|
17
|
+
export declare function ConnectedMoment({ t, workspace, onClose }: {
|
|
18
|
+
t: T;
|
|
19
|
+
workspace: string | null;
|
|
20
|
+
onClose: () => void;
|
|
21
|
+
}): import("react").JSX.Element;
|
|
4
22
|
/** The code split in two halves of boxed characters. */
|
|
5
23
|
export declare function DeviceCode({ code }: {
|
|
6
24
|
code: string;
|
|
7
25
|
}): import("react").JSX.Element;
|
|
8
26
|
/**
|
|
9
27
|
* Hero content for each sign-in state. Lives on a navy hero surface.
|
|
10
|
-
* @param props - account snapshot, kit, translate.
|
|
28
|
+
* @param props - account snapshot, kit, translate, and a quiet second action (Later, Cancel).
|
|
11
29
|
* @returns the hero body.
|
|
12
30
|
*/
|
|
13
|
-
export declare function ConnectHero({ account, kit, t,
|
|
31
|
+
export declare function ConnectHero({ account, kit, t, aside }: {
|
|
14
32
|
account: AccountSnapshot | null;
|
|
15
33
|
kit: Kit;
|
|
16
34
|
t: T;
|
|
17
|
-
|
|
35
|
+
aside?: {
|
|
36
|
+
label: string;
|
|
37
|
+
onClick: () => void;
|
|
38
|
+
};
|
|
18
39
|
}): import("react").JSX.Element;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AccountSnapshot, ModelView } from '@adaptiqai/core/types';
|
|
1
|
+
import type { AccountSnapshot, MemoryView, ModelView } from '@adaptiqai/core/types';
|
|
2
2
|
import type { Kit } from './actions.ts';
|
|
3
3
|
import { type T } from './ui.tsx';
|
|
4
4
|
type Hook<S> = <R>(select: (snapshot: S) => R) => R;
|
|
@@ -7,6 +7,15 @@ export interface SettingsProps {
|
|
|
7
7
|
kit: Kit;
|
|
8
8
|
useAqAccount: Hook<AccountSnapshot | null>;
|
|
9
9
|
}
|
|
10
|
+
/** The fullness chip: green below 70 %, amber to 90 %, red above. */
|
|
11
|
+
export declare function MemoryChip({ store }: {
|
|
12
|
+
store: MemoryView;
|
|
13
|
+
}): import("react").JSX.Element;
|
|
14
|
+
/** The store's bar, in the same colour as its chip. */
|
|
15
|
+
export declare function MemoryBar({ store, thin }: {
|
|
16
|
+
store: MemoryView;
|
|
17
|
+
thin?: boolean;
|
|
18
|
+
}): import("react").JSX.Element;
|
|
10
19
|
/**
|
|
11
20
|
* A model's facts, the same fields in the same order for every model:
|
|
12
21
|
* memory, thinking levels, region. A field is left out only when unknown.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/** The composer pill's words and timing, kept apart from React so they can be tested. */
|
|
2
|
+
import type { WaitKind } from '@adaptiqai/core/types';
|
|
3
|
+
type T = (key: string, params?: Record<string, unknown>) => string;
|
|
4
|
+
/** How long "+24.6k from memory" stays after a reply. */
|
|
5
|
+
export declare const AFTER_REPLY_MS = 4000;
|
|
6
|
+
/** How long the count-up to that figure takes. */
|
|
7
|
+
export declare const COUNT_UP_MS = 900;
|
|
8
|
+
/**
|
|
9
|
+
* The pill's words while a request waits.
|
|
10
|
+
* @param t - translate.
|
|
11
|
+
* @param kind - what the request waits for.
|
|
12
|
+
* @returns e.g. "Waiting for a free lane".
|
|
13
|
+
*/
|
|
14
|
+
export declare function waitText(t: T, kind: WaitKind): string;
|
|
15
|
+
/**
|
|
16
|
+
* The pill's words after a reply, split around the figure so it can be set heavier.
|
|
17
|
+
* @param t - translate.
|
|
18
|
+
* @param fromMemory - tokens the reply read from memory (shown so far, while counting up).
|
|
19
|
+
* @returns e.g. { before: '', figure: '+24.6k', after: ' from memory' }.
|
|
20
|
+
*/
|
|
21
|
+
export declare function afterReplyParts(t: T, fromMemory: number): {
|
|
22
|
+
before: string;
|
|
23
|
+
figure: string;
|
|
24
|
+
after: string;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* The pill's words after a reply.
|
|
28
|
+
* @param t - translate.
|
|
29
|
+
* @param fromMemory - tokens the reply read from memory.
|
|
30
|
+
* @returns e.g. "+24.6k from memory".
|
|
31
|
+
*/
|
|
32
|
+
export declare function afterReplyText(t: T, fromMemory: number): string;
|
|
33
|
+
/**
|
|
34
|
+
* The figure partway through the count-up: fast at first, settling softly.
|
|
35
|
+
* @param target - the final figure.
|
|
36
|
+
* @param elapsed - ms since the count-up began.
|
|
37
|
+
* @param duration - ms the count-up takes.
|
|
38
|
+
* @returns the figure to show now.
|
|
39
|
+
*/
|
|
40
|
+
export declare function countUp(target: number, elapsed: number, duration?: number): number;
|
|
41
|
+
/**
|
|
42
|
+
* Follows a conversation's last turn and reports a new reply that read from
|
|
43
|
+
* memory for {@link AFTER_REPLY_MS}. The turn already there when the view
|
|
44
|
+
* first arrives is history, not news.
|
|
45
|
+
*/
|
|
46
|
+
export declare class AfterReply {
|
|
47
|
+
private readonly onChange;
|
|
48
|
+
private readonly ms;
|
|
49
|
+
private baseline;
|
|
50
|
+
private timer;
|
|
51
|
+
constructor(onChange: (fromMemory: number | null) => void, ms?: number);
|
|
52
|
+
/** The conversation's view arrived or changed. */
|
|
53
|
+
seen(turn: {
|
|
54
|
+
readonly at: number;
|
|
55
|
+
readonly fromMemory: number;
|
|
56
|
+
} | null): void;
|
|
57
|
+
dispose(): void;
|
|
58
|
+
}
|
|
59
|
+
export {};
|
package/lib/types/client/ui.d.ts
CHANGED
|
@@ -3,11 +3,15 @@ import { type ReactNode } from 'react';
|
|
|
3
3
|
import { formatTokens } from '@adaptiqai/core/format';
|
|
4
4
|
/** Translate with the plugin's dictionary. */
|
|
5
5
|
export type T = (key: string, params?: Record<string, unknown>) => string;
|
|
6
|
-
/**
|
|
7
|
-
|
|
6
|
+
/**
|
|
7
|
+
* The cairn: three coral stones under one ink stone. `settle` stacks them as
|
|
8
|
+
* it appears, bottom stone first (about 600 ms in all).
|
|
9
|
+
*/
|
|
10
|
+
export declare function Cairn({ size, alive, onDark, settle }: {
|
|
8
11
|
size?: number;
|
|
9
12
|
alive?: boolean;
|
|
10
13
|
onDark?: boolean;
|
|
14
|
+
settle?: boolean;
|
|
11
15
|
}): import("react").JSX.Element;
|
|
12
16
|
/** The AdaptiqAI wordmark in the current text colour. */
|
|
13
17
|
export declare function Wordmark({ height }: {
|
|
@@ -25,13 +29,15 @@ export declare function Ring({ fraction, size, stroke }: {
|
|
|
25
29
|
size?: number;
|
|
26
30
|
stroke?: number;
|
|
27
31
|
}): import("react").JSX.Element;
|
|
28
|
-
/**
|
|
29
|
-
export
|
|
32
|
+
/** How full a meter reads; the bar and the percentage chip share it. */
|
|
33
|
+
export type MeterTone = 'ok' | 'filling' | 'full';
|
|
34
|
+
/** A segmented usage bar, coloured by how full it is. */
|
|
35
|
+
export declare function UsageBar({ segments, tone, thin }: {
|
|
30
36
|
segments: readonly {
|
|
31
37
|
cls: string;
|
|
32
38
|
fraction: number;
|
|
33
39
|
}[];
|
|
34
|
-
|
|
40
|
+
tone: MeterTone;
|
|
35
41
|
thin?: boolean;
|
|
36
42
|
}): import("react").JSX.Element;
|
|
37
43
|
/** Compact token text. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adaptiqai/deepseek-harness",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
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.3"
|
|
93
93
|
},
|
|
94
94
|
"repository": {
|
|
95
95
|
"type": "git",
|