@adaptiqai/opencode 0.1.0 → 0.1.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/lib/index.js +239 -92
- package/package.json +2 -2
package/lib/index.js
CHANGED
|
@@ -104,7 +104,7 @@ function classifyFailure(status, raw, headers, model2, options = {}) {
|
|
|
104
104
|
if (effective === 503 && detail.code !== null && MODEL_OFFLINE_CODES.includes(detail.code)) {
|
|
105
105
|
return make(CODES.server, `${model2 ?? "This model"} is offline right now. It will retry automatically.`, "retrying");
|
|
106
106
|
}
|
|
107
|
-
if (has(detail, "AdaptiqNativePreflightBusy", "session_route_unavailable", "conversation_preparing", "preparing", "upload_capacity_exhausted") || effective === 503 && !has(detail, "offline")) {
|
|
107
|
+
if (has(detail, "AdaptiqNativePreflightBusy", "session_route_unavailable", "conversation_preparing", "preparing", "upload_capacity_exhausted", "AdaptiqMemoryNotReady") || effective === 425 || effective === 503 && !has(detail, "offline")) {
|
|
108
108
|
return make(CODES.preparing, "AdaptiqAI is getting your conversation ready. It will retry automatically.", "preparing");
|
|
109
109
|
}
|
|
110
110
|
if (effective === 409 && has(detail, "AdaptiqNativePreflight", "conversation")) {
|
|
@@ -321,6 +321,91 @@ function detailMessage(raw) {
|
|
|
321
321
|
}
|
|
322
322
|
return null;
|
|
323
323
|
}
|
|
324
|
+
var EXPIRED = "The sign-in link expired. Start again.";
|
|
325
|
+
async function startDeviceCode(client, request) {
|
|
326
|
+
let response;
|
|
327
|
+
try {
|
|
328
|
+
response = await client.fetch(`${client.apiBase}/v1/cli-auth/device`, {
|
|
329
|
+
method: "POST",
|
|
330
|
+
headers: { "content-type": "application/json", accept: "application/json" },
|
|
331
|
+
body: JSON.stringify({
|
|
332
|
+
client_app: request.app,
|
|
333
|
+
client_name: request.app,
|
|
334
|
+
device_name: request.device,
|
|
335
|
+
client_binding: request.binding,
|
|
336
|
+
...request.tokenLifetime === void 0 ? {} : { token_lifetime: request.tokenLifetime }
|
|
337
|
+
}),
|
|
338
|
+
signal: client.signal === void 0 ? AbortSignal.timeout(2e4) : AbortSignal.any([client.signal, AbortSignal.timeout(2e4)]),
|
|
339
|
+
redirect: "error"
|
|
340
|
+
});
|
|
341
|
+
} catch (error45) {
|
|
342
|
+
if (client.signal?.aborted === true)
|
|
343
|
+
throw error45;
|
|
344
|
+
throw new Error(UNREACHABLE, { cause: error45 });
|
|
345
|
+
}
|
|
346
|
+
if (!response.ok) {
|
|
347
|
+
const message = detailMessage(await readJson(response));
|
|
348
|
+
if (response.status >= 500)
|
|
349
|
+
throw new Error("AdaptiqAI isn't answering right now. Try again in a minute.");
|
|
350
|
+
throw new Error(message ?? `AdaptiqAI sign-in could not start (HTTP ${response.status}).`);
|
|
351
|
+
}
|
|
352
|
+
const body = await readJson(response);
|
|
353
|
+
const deviceCode = String(body?.device_code ?? "");
|
|
354
|
+
const userCode = String(body?.user_code ?? "");
|
|
355
|
+
const url2 = String(body?.verification_uri_complete ?? body?.verification_uri ?? "");
|
|
356
|
+
if (deviceCode === "" || userCode === "" || !/^https?:\/\//u.test(url2))
|
|
357
|
+
throw new Error("AdaptiqAI returned an incomplete sign-in.");
|
|
358
|
+
return {
|
|
359
|
+
deviceCode,
|
|
360
|
+
userCode,
|
|
361
|
+
url: url2,
|
|
362
|
+
expiresAt: client.now() + Math.min(DEVICE_TIMEOUT_MS, Math.max(60, Number(body?.expires_in ?? 600)) * 1e3),
|
|
363
|
+
interval: Math.max(1, Number(body?.interval ?? 2)) * 1e3
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
async function pollDeviceCode(client, code, extra = {}) {
|
|
367
|
+
let response;
|
|
368
|
+
try {
|
|
369
|
+
response = await client.fetch(`${client.apiBase}/v1/cli-auth/device/token`, {
|
|
370
|
+
method: "POST",
|
|
371
|
+
headers: { "content-type": "application/json", accept: "application/json" },
|
|
372
|
+
body: JSON.stringify({ ...extra, device_code: code.deviceCode }),
|
|
373
|
+
signal: client.signal === void 0 ? AbortSignal.timeout(2e4) : AbortSignal.any([client.signal, AbortSignal.timeout(2e4)]),
|
|
374
|
+
redirect: "error"
|
|
375
|
+
});
|
|
376
|
+
} catch {
|
|
377
|
+
return { state: "waiting", delayMs: code.interval * 2 };
|
|
378
|
+
}
|
|
379
|
+
if (response.status === 202)
|
|
380
|
+
return { state: "waiting", delayMs: code.interval };
|
|
381
|
+
if (response.status === 429) {
|
|
382
|
+
const retry = Number(response.headers.get("retry-after") ?? 0) * 1e3;
|
|
383
|
+
return { state: "waiting", delayMs: Math.max(code.interval, retry) + 250 };
|
|
384
|
+
}
|
|
385
|
+
if (response.status === 403) {
|
|
386
|
+
const raw = await readJson(response);
|
|
387
|
+
return { state: "denied", message: detailCode(raw) === "access_denied" ? "The connection was declined in the browser." : detailMessage(raw) ?? "The connection was declined." };
|
|
388
|
+
}
|
|
389
|
+
if (response.status === 410 || response.status === 404)
|
|
390
|
+
return { state: "expired", message: EXPIRED };
|
|
391
|
+
if (!response.ok) {
|
|
392
|
+
if (response.status >= 500)
|
|
393
|
+
return { state: "waiting", delayMs: code.interval * 2 };
|
|
394
|
+
return { state: "failed", message: detailMessage(await readJson(response)) ?? `Sign-in failed (HTTP ${response.status}).` };
|
|
395
|
+
}
|
|
396
|
+
const body = await readJson(response);
|
|
397
|
+
const access = String(body?.access_token ?? "");
|
|
398
|
+
const refresh = String(body?.refresh_token ?? "");
|
|
399
|
+
if (!access.startsWith("ak_") || refresh !== "" && !refresh.startsWith("akr_"))
|
|
400
|
+
return { state: "failed", message: "AdaptiqAI returned an invalid credential." };
|
|
401
|
+
return {
|
|
402
|
+
state: "approved",
|
|
403
|
+
access,
|
|
404
|
+
refresh: refresh === "" ? null : refresh,
|
|
405
|
+
expiresIn: Math.max(60, Number(body?.expires_in ?? 3600)),
|
|
406
|
+
workspaceLabel: typeof body?.workspace_label === "string" && body.workspace_label !== "" ? body.workspace_label : "Your workspace"
|
|
407
|
+
};
|
|
408
|
+
}
|
|
324
409
|
var AuthManager = class {
|
|
325
410
|
options;
|
|
326
411
|
fetch;
|
|
@@ -363,6 +448,9 @@ var AuthManager = class {
|
|
|
363
448
|
this.state = phase;
|
|
364
449
|
this.options.onChange?.();
|
|
365
450
|
}
|
|
451
|
+
client(signal) {
|
|
452
|
+
return { apiBase: this.options.apiBase, fetch: this.fetch, now: this.now, signal };
|
|
453
|
+
}
|
|
366
454
|
/** Read the stored state once at startup. */
|
|
367
455
|
async init() {
|
|
368
456
|
const stored = await this.options.store.read();
|
|
@@ -496,39 +584,11 @@ var AuthManager = class {
|
|
|
496
584
|
const clientProof = `${randomUUID()}${randomUUID()}`;
|
|
497
585
|
const binding = createHash("sha256").update(clientProof).digest("hex");
|
|
498
586
|
const app = this.options.appName ?? "DeepSeek Harness";
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
body: JSON.stringify({
|
|
505
|
-
client_app: app,
|
|
506
|
-
client_name: app,
|
|
507
|
-
device_name: deviceLabel(this.options.deviceName ?? hostname()),
|
|
508
|
-
client_binding: binding
|
|
509
|
-
}),
|
|
510
|
-
signal: AbortSignal.any([flow.signal, AbortSignal.timeout(2e4)]),
|
|
511
|
-
redirect: "error"
|
|
512
|
-
});
|
|
513
|
-
} catch (error45) {
|
|
514
|
-
if (flow.signal.aborted)
|
|
515
|
-
throw error45;
|
|
516
|
-
throw new Error(UNREACHABLE, { cause: error45 });
|
|
517
|
-
}
|
|
518
|
-
if (!response.ok) {
|
|
519
|
-
const message = detailMessage(await readJson(response));
|
|
520
|
-
if (response.status >= 500)
|
|
521
|
-
throw new Error("AdaptiqAI isn't answering right now. Try again in a minute.");
|
|
522
|
-
throw new Error(message ?? `AdaptiqAI sign-in could not start (HTTP ${response.status}).`);
|
|
523
|
-
}
|
|
524
|
-
const body = await readJson(response);
|
|
525
|
-
const deviceCode = String(body?.device_code ?? "");
|
|
526
|
-
const userCode = String(body?.user_code ?? "");
|
|
527
|
-
const url2 = String(body?.verification_uri_complete ?? body?.verification_uri ?? "");
|
|
528
|
-
if (deviceCode === "" || userCode === "" || !/^https?:\/\//u.test(url2))
|
|
529
|
-
throw new Error("AdaptiqAI returned an incomplete sign-in.");
|
|
530
|
-
const expiresAt = this.now() + Math.min(DEVICE_TIMEOUT_MS, Math.max(60, Number(body?.expires_in ?? 600)) * 1e3);
|
|
531
|
-
const interval = Math.max(1, Number(body?.interval ?? 2)) * 1e3;
|
|
587
|
+
const { deviceCode, userCode, url: url2, expiresAt, interval } = await startDeviceCode(this.client(flow.signal), {
|
|
588
|
+
app,
|
|
589
|
+
device: deviceLabel(this.options.deviceName ?? hostname()),
|
|
590
|
+
binding
|
|
591
|
+
});
|
|
532
592
|
this.pendingSignIn = { url: url2, userCode, expiresAt };
|
|
533
593
|
this.set("signing-in");
|
|
534
594
|
void this.poll(flow, deviceCode, clientProof, interval, expiresAt);
|
|
@@ -540,61 +600,27 @@ var AuthManager = class {
|
|
|
540
600
|
while (this.now() < expiresAt) {
|
|
541
601
|
await this.sleep(delay, flow.signal);
|
|
542
602
|
delay = interval;
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
method: "POST",
|
|
547
|
-
headers: { "content-type": "application/json", accept: "application/json" },
|
|
548
|
-
body: JSON.stringify({ device_code: deviceCode }),
|
|
549
|
-
signal: AbortSignal.any([flow.signal, AbortSignal.timeout(2e4)]),
|
|
550
|
-
redirect: "error"
|
|
551
|
-
});
|
|
552
|
-
} catch {
|
|
553
|
-
if (flow.signal.aborted)
|
|
554
|
-
return;
|
|
555
|
-
delay = interval * 2;
|
|
603
|
+
const answer = await pollDeviceCode(this.client(flow.signal), { deviceCode, interval });
|
|
604
|
+
if (answer.state === "waiting") {
|
|
605
|
+
delay = answer.delayMs;
|
|
556
606
|
continue;
|
|
557
607
|
}
|
|
558
|
-
if (
|
|
559
|
-
|
|
560
|
-
if (response.status === 429) {
|
|
561
|
-
const retry = Number(response.headers.get("retry-after") ?? 0) * 1e3;
|
|
562
|
-
delay = Math.max(interval, retry) + 250;
|
|
563
|
-
continue;
|
|
564
|
-
}
|
|
565
|
-
if (response.status === 403) {
|
|
566
|
-
const raw = await readJson(response);
|
|
567
|
-
this.fail(flow, { code: "denied", message: detailCode(raw) === "access_denied" ? "The connection was declined in the browser." : detailMessage(raw) ?? "The connection was declined." });
|
|
568
|
-
return;
|
|
569
|
-
}
|
|
570
|
-
if (response.status === 410 || response.status === 404) {
|
|
571
|
-
this.fail(flow, { code: "expired", message: "The sign-in link expired. Start again." });
|
|
572
|
-
return;
|
|
573
|
-
}
|
|
574
|
-
if (!response.ok) {
|
|
575
|
-
if (response.status >= 500) {
|
|
576
|
-
delay = interval * 2;
|
|
577
|
-
continue;
|
|
578
|
-
}
|
|
579
|
-
this.fail(flow, { code: "failed", message: detailMessage(await readJson(response)) ?? `Sign-in failed (HTTP ${response.status}).` });
|
|
608
|
+
if (answer.state !== "approved") {
|
|
609
|
+
this.fail(flow, { code: answer.state, message: answer.message });
|
|
580
610
|
return;
|
|
581
611
|
}
|
|
582
|
-
|
|
583
|
-
const access = String(body?.access_token ?? "");
|
|
584
|
-
const refresh = String(body?.refresh_token ?? "");
|
|
585
|
-
if (!access.startsWith("ak_") || !refresh.startsWith("akr_")) {
|
|
612
|
+
if (answer.refresh === null) {
|
|
586
613
|
this.fail(flow, { code: "failed", message: "AdaptiqAI returned an invalid credential." });
|
|
587
614
|
return;
|
|
588
615
|
}
|
|
589
|
-
const workspaceLabel = typeof body?.workspace_label === "string" && body.workspace_label !== "" ? body.workspace_label : "Your workspace";
|
|
590
616
|
const grant = {
|
|
591
617
|
schema: "adaptiq-dsh-grant-v1",
|
|
592
618
|
apiBase: this.options.apiBase,
|
|
593
|
-
access,
|
|
594
|
-
refresh,
|
|
619
|
+
access: answer.access,
|
|
620
|
+
refresh: answer.refresh,
|
|
595
621
|
clientProof,
|
|
596
|
-
expiresAt: this.now() +
|
|
597
|
-
workspaceLabel,
|
|
622
|
+
expiresAt: this.now() + answer.expiresIn * 1e3,
|
|
623
|
+
workspaceLabel: answer.workspaceLabel,
|
|
598
624
|
obtainedAt: this.now()
|
|
599
625
|
};
|
|
600
626
|
await this.options.store.modify(async () => grant);
|
|
@@ -603,12 +629,12 @@ var AuthManager = class {
|
|
|
603
629
|
this.flow = null;
|
|
604
630
|
this.pendingSignIn = null;
|
|
605
631
|
this.source = "device";
|
|
606
|
-
this.label = workspaceLabel;
|
|
632
|
+
this.label = answer.workspaceLabel;
|
|
607
633
|
this.set("connected");
|
|
608
|
-
this.options.onSignedIn?.(workspaceLabel);
|
|
634
|
+
this.options.onSignedIn?.(answer.workspaceLabel);
|
|
609
635
|
return;
|
|
610
636
|
}
|
|
611
|
-
this.fail(flow, { code: "expired", message:
|
|
637
|
+
this.fail(flow, { code: "expired", message: EXPIRED });
|
|
612
638
|
} catch (error45) {
|
|
613
639
|
if (flow.signal.aborted)
|
|
614
640
|
return;
|
|
@@ -733,6 +759,20 @@ var obj = (value) => typeof value === "object" && value !== null && !Array.isArr
|
|
|
733
759
|
var str = (value) => typeof value === "string" && value.trim() !== "" ? value.trim() : null;
|
|
734
760
|
var num = (value) => typeof value === "number" && Number.isFinite(value) && value > 0 ? value : null;
|
|
735
761
|
var count = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
|
|
762
|
+
function pricesFromRow(raw) {
|
|
763
|
+
const pricing = obj(raw);
|
|
764
|
+
const price = (value) => {
|
|
765
|
+
const parsed = typeof value === "string" && value.trim() !== "" ? Number(value) : typeof value === "number" ? value : Number.NaN;
|
|
766
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
|
|
767
|
+
};
|
|
768
|
+
const input = price(pricing?.prompt);
|
|
769
|
+
const memoryWrite = price(pricing?.input_cache_write);
|
|
770
|
+
const memoryRead = price(pricing?.input_cache_read);
|
|
771
|
+
const output = price(pricing?.completion);
|
|
772
|
+
if (input === null || memoryWrite === null || memoryRead === null || output === null)
|
|
773
|
+
return null;
|
|
774
|
+
return { input, memoryWrite, memoryRead, output };
|
|
775
|
+
}
|
|
736
776
|
var LEVEL_NAMES = {
|
|
737
777
|
off: "Off",
|
|
738
778
|
minimal: "Minimal",
|
|
@@ -821,17 +861,17 @@ function placementsFromRoutes(routes) {
|
|
|
821
861
|
const model2 = str(route?.model);
|
|
822
862
|
if (route === null || model2 === null || route.enabled !== true)
|
|
823
863
|
continue;
|
|
824
|
-
const
|
|
864
|
+
const add2 = (a, b) => a === null ? b : b === null ? a : a + b;
|
|
825
865
|
const current = out.get(model2);
|
|
826
866
|
const running = count(route.running);
|
|
827
867
|
const waiting = count(route.waiting);
|
|
828
868
|
out.set(model2, {
|
|
829
869
|
name: current?.name ?? (str(route.name) !== null ? routeModelName(str(route.name)) : null),
|
|
830
870
|
status: "running",
|
|
831
|
-
lanes:
|
|
832
|
-
maxParallel:
|
|
833
|
-
runningNow:
|
|
834
|
-
waitingNow:
|
|
871
|
+
lanes: add2(current?.lanes ?? null, count(route.lanes)),
|
|
872
|
+
maxParallel: add2(current?.maxParallel ?? null, count(route.max_parallel_agents)),
|
|
873
|
+
runningNow: add2(current?.runningNow ?? null, running),
|
|
874
|
+
waitingNow: add2(current?.waitingNow ?? null, waiting),
|
|
835
875
|
region: current?.region ?? str(route.region),
|
|
836
876
|
card: null,
|
|
837
877
|
contextWindow: null,
|
|
@@ -892,7 +932,8 @@ function model(id, row, placement) {
|
|
|
892
932
|
maxParallel: placement?.maxParallel ?? count(gateway?.max_parallel_agents),
|
|
893
933
|
runningNow: placement?.runningNow ?? null,
|
|
894
934
|
waitingNow: placement?.waitingNow ?? null,
|
|
895
|
-
region: placement?.region ?? str(gateway?.region)
|
|
935
|
+
region: placement?.region ?? str(gateway?.region),
|
|
936
|
+
prices: pricesFromRow(row?.pricing)
|
|
896
937
|
};
|
|
897
938
|
}
|
|
898
939
|
function selectableLevels(reasoning) {
|
|
@@ -961,6 +1002,60 @@ function laneLimit(offer) {
|
|
|
961
1002
|
return free < offer.minLanes ? 0 : Math.min(offer.maxLanes, free);
|
|
962
1003
|
}
|
|
963
1004
|
|
|
1005
|
+
// ../core/lib/processed.js
|
|
1006
|
+
var NOTHING_PROCESSED = { requests: 0, newInput: 0, fromMemory: 0, written: 0, output: 0, reasoning: 0, costUsd: 0 };
|
|
1007
|
+
function stepCost(usage, prices) {
|
|
1008
|
+
if (prices === null)
|
|
1009
|
+
return null;
|
|
1010
|
+
return usage.inputTokens * prices.input + (usage.cacheWriteTokens ?? 0) * prices.memoryWrite + (usage.cacheReadTokens ?? 0) * prices.memoryRead + usage.outputTokens * prices.output;
|
|
1011
|
+
}
|
|
1012
|
+
function add(view, step) {
|
|
1013
|
+
return {
|
|
1014
|
+
requests: view.requests + 1,
|
|
1015
|
+
newInput: view.newInput + step.usage.inputTokens,
|
|
1016
|
+
fromMemory: view.fromMemory + (step.usage.cacheReadTokens ?? 0),
|
|
1017
|
+
written: view.written + (step.usage.cacheWriteTokens ?? 0),
|
|
1018
|
+
output: view.output + step.usage.outputTokens,
|
|
1019
|
+
reasoning: view.reasoning + (step.usage.reasoningTokens ?? 0),
|
|
1020
|
+
costUsd: view.costUsd === null || step.costUsd === null ? null : view.costUsd + step.costUsd
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
function addStep(record2, step, now) {
|
|
1024
|
+
const turnKey = step.turn ?? record2?.turnKey ?? null;
|
|
1025
|
+
const fresh = record2 == null || step.turn !== void 0 && step.turn !== record2.turnKey;
|
|
1026
|
+
return {
|
|
1027
|
+
since: record2?.since ?? now,
|
|
1028
|
+
at: now,
|
|
1029
|
+
turnKey,
|
|
1030
|
+
turn: add(fresh ? NOTHING_PROCESSED : record2.turn, step),
|
|
1031
|
+
session: add(record2?.session ?? NOTHING_PROCESSED, step)
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
function readProcessed(raw) {
|
|
1035
|
+
const record2 = typeof raw === "object" && raw !== null ? raw : null;
|
|
1036
|
+
const view = (value) => {
|
|
1037
|
+
const fields = typeof value === "object" && value !== null ? value : null;
|
|
1038
|
+
if (fields === null)
|
|
1039
|
+
return null;
|
|
1040
|
+
const count3 = (name) => {
|
|
1041
|
+
const field = fields[name];
|
|
1042
|
+
return typeof field === "number" && Number.isFinite(field) && field >= 0 ? field : null;
|
|
1043
|
+
};
|
|
1044
|
+
const numbers = { requests: count3("requests"), newInput: count3("newInput"), fromMemory: count3("fromMemory"), written: count3("written"), output: count3("output"), reasoning: count3("reasoning") };
|
|
1045
|
+
if (Object.values(numbers).some((value2) => value2 === null))
|
|
1046
|
+
return null;
|
|
1047
|
+
return { ...numbers, costUsd: count3("costUsd") };
|
|
1048
|
+
};
|
|
1049
|
+
const turn = view(record2?.turn);
|
|
1050
|
+
const session = view(record2?.session);
|
|
1051
|
+
const since = record2?.since;
|
|
1052
|
+
const at = record2?.at;
|
|
1053
|
+
if (turn === null || session === null || typeof since !== "number" || typeof at !== "number")
|
|
1054
|
+
return null;
|
|
1055
|
+
const turnKey = typeof record2?.turnKey === "number" ? record2.turnKey : null;
|
|
1056
|
+
return { since, at, turnKey, turn, session };
|
|
1057
|
+
}
|
|
1058
|
+
|
|
964
1059
|
// ../core/lib/relations.js
|
|
965
1060
|
var PARENT_SESSION_HEADER = "X-Adaptiq-Parent-Session-Id";
|
|
966
1061
|
var SESSION_ROLE_HEADER = "X-Adaptiq-Session-Role";
|
|
@@ -1049,6 +1144,22 @@ var en = {
|
|
|
1049
1144
|
popKnowledge: "Knowledge",
|
|
1050
1145
|
addKnowledge: "Add knowledge",
|
|
1051
1146
|
popWorkspace: "All memory on {model}",
|
|
1147
|
+
popSeesNow: "What the model sees now",
|
|
1148
|
+
procTitle: "Processed",
|
|
1149
|
+
procTurn: "Last turn",
|
|
1150
|
+
procSession: "This conversation",
|
|
1151
|
+
procSteps: "{count} steps",
|
|
1152
|
+
procStepsOne: "1 step",
|
|
1153
|
+
procNewInput: "New input",
|
|
1154
|
+
procFromMemory: "Read from memory",
|
|
1155
|
+
procWritten: "Saved to memory",
|
|
1156
|
+
procOutput: "Output",
|
|
1157
|
+
procThinking: "Thinking, part of output",
|
|
1158
|
+
procCost: "Cost",
|
|
1159
|
+
procExplain: "Each step reads the whole memory again. {percent} came from memory.",
|
|
1160
|
+
procExplainFree: "Each step reads the whole memory again. {percent} came from memory, which is free.",
|
|
1161
|
+
procExplainCheaper: "Each step reads the whole memory again. {percent} came from memory, at a fraction of the input price.",
|
|
1162
|
+
procSince: "Counted on this computer since {date}",
|
|
1052
1163
|
openInKnowledge: "Open in Knowledge",
|
|
1053
1164
|
kTitle: "Knowledge",
|
|
1054
1165
|
kSearch: "Search your knowledge",
|
|
@@ -1533,6 +1644,21 @@ function button(id, label, extra = {}) {
|
|
|
1533
1644
|
return { id, label: en[label], labelKey: label, ...extra };
|
|
1534
1645
|
}
|
|
1535
1646
|
var count2 = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
|
|
1647
|
+
var PROCESSED_SAVE_MS = 2e3;
|
|
1648
|
+
function savedProcessed(raw) {
|
|
1649
|
+
const out = {};
|
|
1650
|
+
if (typeof raw !== "object" || raw === null)
|
|
1651
|
+
return out;
|
|
1652
|
+
for (const [id, value] of Object.entries(raw)) {
|
|
1653
|
+
const record2 = readProcessed(value);
|
|
1654
|
+
if (record2 !== null)
|
|
1655
|
+
out[id] = record2;
|
|
1656
|
+
}
|
|
1657
|
+
return out;
|
|
1658
|
+
}
|
|
1659
|
+
function processedSummary(record2) {
|
|
1660
|
+
return record2 === void 0 ? null : { turn: record2.turn, session: record2.session, since: record2.since };
|
|
1661
|
+
}
|
|
1536
1662
|
var PRIVATE_HELD = "A private conversation is not kept on AdaptiqAI, so it can't use this before it exists there. Turn Remember on for this conversation to add it.";
|
|
1537
1663
|
var ATTACH_TRIES = 3;
|
|
1538
1664
|
function transient(error45) {
|
|
@@ -1898,6 +2024,7 @@ var Workspace = class {
|
|
|
1898
2024
|
listeners = /* @__PURE__ */ new Set();
|
|
1899
2025
|
state = { preferences: DEFAULT_PREFERENCES, sessions: {} };
|
|
1900
2026
|
saving = Promise.resolve();
|
|
2027
|
+
persistTimer;
|
|
1901
2028
|
refreshing = null;
|
|
1902
2029
|
timer;
|
|
1903
2030
|
pendingAttach = /* @__PURE__ */ new Map();
|
|
@@ -1957,7 +2084,8 @@ var Workspace = class {
|
|
|
1957
2084
|
preferences: { ...DEFAULT_PREFERENCES, ...saved.preferences },
|
|
1958
2085
|
sessions: typeof saved.sessions === "object" && saved.sessions !== null ? saved.sessions : {},
|
|
1959
2086
|
purchase: saved.purchase ?? null,
|
|
1960
|
-
...saved.defaultModel === "pending" || saved.defaultModel === "done" ? { defaultModel: saved.defaultModel } : {}
|
|
2087
|
+
...saved.defaultModel === "pending" || saved.defaultModel === "done" ? { defaultModel: saved.defaultModel } : {},
|
|
2088
|
+
processed: savedProcessed(saved.processed)
|
|
1961
2089
|
};
|
|
1962
2090
|
this.purchases.restore(saved.purchase);
|
|
1963
2091
|
}
|
|
@@ -1973,6 +2101,10 @@ var Workspace = class {
|
|
|
1973
2101
|
this.purchases.dispose();
|
|
1974
2102
|
clearTimeout(this.timer);
|
|
1975
2103
|
clearTimeout(this.attachTimer);
|
|
2104
|
+
if (this.persistTimer !== void 0) {
|
|
2105
|
+
clearTimeout(this.persistTimer);
|
|
2106
|
+
void this.persist();
|
|
2107
|
+
}
|
|
1976
2108
|
for (const session of this.sessions.values()) {
|
|
1977
2109
|
clearTimeout(session.timer);
|
|
1978
2110
|
clearTimeout(session.waitTimer);
|
|
@@ -2040,7 +2172,8 @@ var Workspace = class {
|
|
|
2040
2172
|
region: model2.region,
|
|
2041
2173
|
nativeMemory: model2.nativeMemory,
|
|
2042
2174
|
tools: model2.tools,
|
|
2043
|
-
memory: this.modelMemory.get(model2.id) ?? null
|
|
2175
|
+
memory: this.modelMemory.get(model2.id) ?? null,
|
|
2176
|
+
prices: model2.prices
|
|
2044
2177
|
};
|
|
2045
2178
|
}
|
|
2046
2179
|
/**
|
|
@@ -2650,12 +2783,16 @@ var Workspace = class {
|
|
|
2650
2783
|
return next;
|
|
2651
2784
|
}
|
|
2652
2785
|
async persist() {
|
|
2786
|
+
clearTimeout(this.persistTimer);
|
|
2787
|
+
this.persistTimer = void 0;
|
|
2653
2788
|
const entries = Object.entries(this.state.sessions).sort((a, b) => b[1].at - a[1].at).slice(0, 500);
|
|
2789
|
+
const processed = Object.entries(this.state.processed ?? {}).sort((a, b) => b[1].at - a[1].at).slice(0, 500);
|
|
2654
2790
|
const snapshot = {
|
|
2655
2791
|
preferences: this.state.preferences,
|
|
2656
2792
|
sessions: Object.fromEntries(entries),
|
|
2657
2793
|
purchase: this.state.purchase ?? null,
|
|
2658
|
-
...this.state.defaultModel === void 0 ? {} : { defaultModel: this.state.defaultModel }
|
|
2794
|
+
...this.state.defaultModel === void 0 ? {} : { defaultModel: this.state.defaultModel },
|
|
2795
|
+
...processed.length === 0 ? {} : { processed: Object.fromEntries(processed) }
|
|
2659
2796
|
};
|
|
2660
2797
|
this.saving = this.saving.then(() => this.deps.store.save(snapshot)).catch((error45) => {
|
|
2661
2798
|
this.deps.log?.("warn", `adaptiq: could not save preferences: ${String(error45)}`);
|
|
@@ -2766,6 +2903,7 @@ var Workspace = class {
|
|
|
2766
2903
|
saved: session.saved,
|
|
2767
2904
|
optimizing: session.optimizing,
|
|
2768
2905
|
lastTurn: session.lastTurn,
|
|
2906
|
+
processed: processedSummary(this.state.processed?.[id]),
|
|
2769
2907
|
knowledge: [...session.knowledge, ...[...session.pendingKnowledge.values()].filter((item) => !session.knowledge.some((entry) => entry.id === item.id))],
|
|
2770
2908
|
conversations: [...session.conversations, ...[...session.pendingConversations.values()].filter((item) => !session.conversations.some((entry) => entry.id === item.id))],
|
|
2771
2909
|
known: session.exists,
|
|
@@ -3145,10 +3283,19 @@ var Workspace = class {
|
|
|
3145
3283
|
this.setPhase(session, "responding");
|
|
3146
3284
|
this.clearNotices("busy", "preparing", "offline", "retrying");
|
|
3147
3285
|
}
|
|
3148
|
-
usage(sessionId, model2, usage) {
|
|
3286
|
+
usage(sessionId, model2, usage, step = {}) {
|
|
3149
3287
|
if (sessionId === void 0)
|
|
3150
3288
|
return;
|
|
3151
3289
|
const session = this.session(sessionId);
|
|
3290
|
+
const costUsd = step.costUsd ?? stepCost(usage, this.catalog.find((entry) => entry.id === model2)?.prices ?? null);
|
|
3291
|
+
const counted = addStep(this.state.processed?.[sessionId], { usage, costUsd, ...step.turn === void 0 ? {} : { turn: step.turn } }, this.now());
|
|
3292
|
+
this.state = { ...this.state, processed: { ...this.state.processed, [sessionId]: counted } };
|
|
3293
|
+
if (this.persistTimer === void 0) {
|
|
3294
|
+
this.persistTimer = setTimeout(() => {
|
|
3295
|
+
void this.persist();
|
|
3296
|
+
}, PROCESSED_SAVE_MS);
|
|
3297
|
+
this.persistTimer.unref?.();
|
|
3298
|
+
}
|
|
3152
3299
|
session.lastTurn = {
|
|
3153
3300
|
fromMemory: usage.cacheReadTokens ?? 0,
|
|
3154
3301
|
newInput: usage.inputTokens,
|
|
@@ -4863,7 +5010,7 @@ function fileModelCache(folder) {
|
|
|
4863
5010
|
}
|
|
4864
5011
|
|
|
4865
5012
|
// src/adaptiq.ts
|
|
4866
|
-
var VERSION = true ? "0.1.
|
|
5013
|
+
var VERSION = true ? "0.1.1" : "0.0.0";
|
|
4867
5014
|
var ROUTE = "adaptiqai";
|
|
4868
5015
|
var COMMAND_TEMPLATE = "Show where AdaptiqAI memory stands for this conversation: use adaptiq_knowledge with action status and adaptiq_memory with action status, and answer in two or three short sentences. $ARGUMENTS";
|
|
4869
5016
|
var COMMAND_QUESTION = COMMAND_TEMPLATE.slice(0, COMMAND_TEMPLATE.indexOf("$ARGUMENTS")).trim();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adaptiqai/opencode",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "AdaptiqAI for OpenCode and Kilo: your workspace's models, conversations AdaptiqAI remembers, and your saved knowledge, in one plugin.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"typescript": "~5.9.3",
|
|
36
36
|
"vitest": "~3.2.7",
|
|
37
37
|
"zod": "4.1.8",
|
|
38
|
-
"@adaptiqai/core": "0.2.
|
|
38
|
+
"@adaptiqai/core": "0.2.11"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "node scripts/build.mjs",
|