@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.
Files changed (2) hide show
  1. package/lib/index.js +239 -92
  2. 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
- let response;
500
- try {
501
- response = await this.fetch(`${this.options.apiBase}/v1/cli-auth/device`, {
502
- method: "POST",
503
- headers: { "content-type": "application/json", accept: "application/json" },
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
- let response;
544
- try {
545
- response = await this.fetch(`${this.options.apiBase}/v1/cli-auth/device/token`, {
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 (response.status === 202)
559
- continue;
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
- const body = await readJson(response);
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() + Math.max(60, Number(body?.expires_in ?? 3600)) * 1e3,
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: "The sign-in link expired. Start again." });
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 add = (a, b) => a === null ? b : b === null ? a : a + b;
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: add(current?.lanes ?? null, count(route.lanes)),
832
- maxParallel: add(current?.maxParallel ?? null, count(route.max_parallel_agents)),
833
- runningNow: add(current?.runningNow ?? null, running),
834
- waitingNow: add(current?.waitingNow ?? null, waiting),
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.0" : "0.0.0";
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.0",
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.10"
38
+ "@adaptiqai/core": "0.2.11"
39
39
  },
40
40
  "scripts": {
41
41
  "build": "node scripts/build.mjs",