@adaptiqai/openclaw 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
@@ -99,7 +99,7 @@ function classifyFailure(status, raw, headers, model2, options = {}) {
99
99
  if (effective === 503 && detail.code !== null && MODEL_OFFLINE_CODES.includes(detail.code)) {
100
100
  return make(CODES.server, `${model2 ?? "This model"} is offline right now. It will retry automatically.`, "retrying");
101
101
  }
102
- if (has(detail, "AdaptiqNativePreflightBusy", "session_route_unavailable", "conversation_preparing", "preparing", "upload_capacity_exhausted") || effective === 503 && !has(detail, "offline")) {
102
+ if (has(detail, "AdaptiqNativePreflightBusy", "session_route_unavailable", "conversation_preparing", "preparing", "upload_capacity_exhausted", "AdaptiqMemoryNotReady") || effective === 425 || effective === 503 && !has(detail, "offline")) {
103
103
  return make(CODES.preparing, "AdaptiqAI is getting your conversation ready. It will retry automatically.", "preparing");
104
104
  }
105
105
  if (effective === 409 && has(detail, "AdaptiqNativePreflight", "conversation")) {
@@ -316,6 +316,91 @@ function detailMessage(raw) {
316
316
  }
317
317
  return null;
318
318
  }
319
+ var EXPIRED = "The sign-in link expired. Start again.";
320
+ async function startDeviceCode(client, request) {
321
+ let response;
322
+ try {
323
+ response = await client.fetch(`${client.apiBase}/v1/cli-auth/device`, {
324
+ method: "POST",
325
+ headers: { "content-type": "application/json", accept: "application/json" },
326
+ body: JSON.stringify({
327
+ client_app: request.app,
328
+ client_name: request.app,
329
+ device_name: request.device,
330
+ client_binding: request.binding,
331
+ ...request.tokenLifetime === void 0 ? {} : { token_lifetime: request.tokenLifetime }
332
+ }),
333
+ signal: client.signal === void 0 ? AbortSignal.timeout(2e4) : AbortSignal.any([client.signal, AbortSignal.timeout(2e4)]),
334
+ redirect: "error"
335
+ });
336
+ } catch (error) {
337
+ if (client.signal?.aborted === true)
338
+ throw error;
339
+ throw new Error(UNREACHABLE, { cause: error });
340
+ }
341
+ if (!response.ok) {
342
+ const message = detailMessage(await readJson(response));
343
+ if (response.status >= 500)
344
+ throw new Error("AdaptiqAI isn't answering right now. Try again in a minute.");
345
+ throw new Error(message ?? `AdaptiqAI sign-in could not start (HTTP ${response.status}).`);
346
+ }
347
+ const body = await readJson(response);
348
+ const deviceCode = String(body?.device_code ?? "");
349
+ const userCode = String(body?.user_code ?? "");
350
+ const url = String(body?.verification_uri_complete ?? body?.verification_uri ?? "");
351
+ if (deviceCode === "" || userCode === "" || !/^https?:\/\//u.test(url))
352
+ throw new Error("AdaptiqAI returned an incomplete sign-in.");
353
+ return {
354
+ deviceCode,
355
+ userCode,
356
+ url,
357
+ expiresAt: client.now() + Math.min(DEVICE_TIMEOUT_MS, Math.max(60, Number(body?.expires_in ?? 600)) * 1e3),
358
+ interval: Math.max(1, Number(body?.interval ?? 2)) * 1e3
359
+ };
360
+ }
361
+ async function pollDeviceCode(client, code, extra = {}) {
362
+ let response;
363
+ try {
364
+ response = await client.fetch(`${client.apiBase}/v1/cli-auth/device/token`, {
365
+ method: "POST",
366
+ headers: { "content-type": "application/json", accept: "application/json" },
367
+ body: JSON.stringify({ ...extra, device_code: code.deviceCode }),
368
+ signal: client.signal === void 0 ? AbortSignal.timeout(2e4) : AbortSignal.any([client.signal, AbortSignal.timeout(2e4)]),
369
+ redirect: "error"
370
+ });
371
+ } catch {
372
+ return { state: "waiting", delayMs: code.interval * 2 };
373
+ }
374
+ if (response.status === 202)
375
+ return { state: "waiting", delayMs: code.interval };
376
+ if (response.status === 429) {
377
+ const retry = Number(response.headers.get("retry-after") ?? 0) * 1e3;
378
+ return { state: "waiting", delayMs: Math.max(code.interval, retry) + 250 };
379
+ }
380
+ if (response.status === 403) {
381
+ const raw = await readJson(response);
382
+ return { state: "denied", message: detailCode(raw) === "access_denied" ? "The connection was declined in the browser." : detailMessage(raw) ?? "The connection was declined." };
383
+ }
384
+ if (response.status === 410 || response.status === 404)
385
+ return { state: "expired", message: EXPIRED };
386
+ if (!response.ok) {
387
+ if (response.status >= 500)
388
+ return { state: "waiting", delayMs: code.interval * 2 };
389
+ return { state: "failed", message: detailMessage(await readJson(response)) ?? `Sign-in failed (HTTP ${response.status}).` };
390
+ }
391
+ const body = await readJson(response);
392
+ const access = String(body?.access_token ?? "");
393
+ const refresh = String(body?.refresh_token ?? "");
394
+ if (!access.startsWith("ak_") || refresh !== "" && !refresh.startsWith("akr_"))
395
+ return { state: "failed", message: "AdaptiqAI returned an invalid credential." };
396
+ return {
397
+ state: "approved",
398
+ access,
399
+ refresh: refresh === "" ? null : refresh,
400
+ expiresIn: Math.max(60, Number(body?.expires_in ?? 3600)),
401
+ workspaceLabel: typeof body?.workspace_label === "string" && body.workspace_label !== "" ? body.workspace_label : "Your workspace"
402
+ };
403
+ }
319
404
  var AuthManager = class {
320
405
  options;
321
406
  fetch;
@@ -358,6 +443,9 @@ var AuthManager = class {
358
443
  this.state = phase;
359
444
  this.options.onChange?.();
360
445
  }
446
+ client(signal) {
447
+ return { apiBase: this.options.apiBase, fetch: this.fetch, now: this.now, signal };
448
+ }
361
449
  /** Read the stored state once at startup. */
362
450
  async init() {
363
451
  const stored = await this.options.store.read();
@@ -491,39 +579,11 @@ var AuthManager = class {
491
579
  const clientProof = `${randomUUID()}${randomUUID()}`;
492
580
  const binding = createHash("sha256").update(clientProof).digest("hex");
493
581
  const app = this.options.appName ?? "DeepSeek Harness";
494
- let response;
495
- try {
496
- response = await this.fetch(`${this.options.apiBase}/v1/cli-auth/device`, {
497
- method: "POST",
498
- headers: { "content-type": "application/json", accept: "application/json" },
499
- body: JSON.stringify({
500
- client_app: app,
501
- client_name: app,
502
- device_name: deviceLabel(this.options.deviceName ?? hostname()),
503
- client_binding: binding
504
- }),
505
- signal: AbortSignal.any([flow.signal, AbortSignal.timeout(2e4)]),
506
- redirect: "error"
507
- });
508
- } catch (error) {
509
- if (flow.signal.aborted)
510
- throw error;
511
- throw new Error(UNREACHABLE, { cause: error });
512
- }
513
- if (!response.ok) {
514
- const message = detailMessage(await readJson(response));
515
- if (response.status >= 500)
516
- throw new Error("AdaptiqAI isn't answering right now. Try again in a minute.");
517
- throw new Error(message ?? `AdaptiqAI sign-in could not start (HTTP ${response.status}).`);
518
- }
519
- const body = await readJson(response);
520
- const deviceCode = String(body?.device_code ?? "");
521
- const userCode = String(body?.user_code ?? "");
522
- const url = String(body?.verification_uri_complete ?? body?.verification_uri ?? "");
523
- if (deviceCode === "" || userCode === "" || !/^https?:\/\//u.test(url))
524
- throw new Error("AdaptiqAI returned an incomplete sign-in.");
525
- const expiresAt = this.now() + Math.min(DEVICE_TIMEOUT_MS, Math.max(60, Number(body?.expires_in ?? 600)) * 1e3);
526
- const interval = Math.max(1, Number(body?.interval ?? 2)) * 1e3;
582
+ const { deviceCode, userCode, url, expiresAt, interval } = await startDeviceCode(this.client(flow.signal), {
583
+ app,
584
+ device: deviceLabel(this.options.deviceName ?? hostname()),
585
+ binding
586
+ });
527
587
  this.pendingSignIn = { url, userCode, expiresAt };
528
588
  this.set("signing-in");
529
589
  void this.poll(flow, deviceCode, clientProof, interval, expiresAt);
@@ -535,61 +595,27 @@ var AuthManager = class {
535
595
  while (this.now() < expiresAt) {
536
596
  await this.sleep(delay, flow.signal);
537
597
  delay = interval;
538
- let response;
539
- try {
540
- response = await this.fetch(`${this.options.apiBase}/v1/cli-auth/device/token`, {
541
- method: "POST",
542
- headers: { "content-type": "application/json", accept: "application/json" },
543
- body: JSON.stringify({ device_code: deviceCode }),
544
- signal: AbortSignal.any([flow.signal, AbortSignal.timeout(2e4)]),
545
- redirect: "error"
546
- });
547
- } catch {
548
- if (flow.signal.aborted)
549
- return;
550
- delay = interval * 2;
551
- continue;
552
- }
553
- if (response.status === 202)
554
- continue;
555
- if (response.status === 429) {
556
- const retry = Number(response.headers.get("retry-after") ?? 0) * 1e3;
557
- delay = Math.max(interval, retry) + 250;
598
+ const answer = await pollDeviceCode(this.client(flow.signal), { deviceCode, interval });
599
+ if (answer.state === "waiting") {
600
+ delay = answer.delayMs;
558
601
  continue;
559
602
  }
560
- if (response.status === 403) {
561
- const raw = await readJson(response);
562
- this.fail(flow, { code: "denied", message: detailCode(raw) === "access_denied" ? "The connection was declined in the browser." : detailMessage(raw) ?? "The connection was declined." });
603
+ if (answer.state !== "approved") {
604
+ this.fail(flow, { code: answer.state, message: answer.message });
563
605
  return;
564
606
  }
565
- if (response.status === 410 || response.status === 404) {
566
- this.fail(flow, { code: "expired", message: "The sign-in link expired. Start again." });
567
- return;
568
- }
569
- if (!response.ok) {
570
- if (response.status >= 500) {
571
- delay = interval * 2;
572
- continue;
573
- }
574
- this.fail(flow, { code: "failed", message: detailMessage(await readJson(response)) ?? `Sign-in failed (HTTP ${response.status}).` });
575
- return;
576
- }
577
- const body = await readJson(response);
578
- const access = String(body?.access_token ?? "");
579
- const refresh = String(body?.refresh_token ?? "");
580
- if (!access.startsWith("ak_") || !refresh.startsWith("akr_")) {
607
+ if (answer.refresh === null) {
581
608
  this.fail(flow, { code: "failed", message: "AdaptiqAI returned an invalid credential." });
582
609
  return;
583
610
  }
584
- const workspaceLabel = typeof body?.workspace_label === "string" && body.workspace_label !== "" ? body.workspace_label : "Your workspace";
585
611
  const grant = {
586
612
  schema: "adaptiq-dsh-grant-v1",
587
613
  apiBase: this.options.apiBase,
588
- access,
589
- refresh,
614
+ access: answer.access,
615
+ refresh: answer.refresh,
590
616
  clientProof,
591
- expiresAt: this.now() + Math.max(60, Number(body?.expires_in ?? 3600)) * 1e3,
592
- workspaceLabel,
617
+ expiresAt: this.now() + answer.expiresIn * 1e3,
618
+ workspaceLabel: answer.workspaceLabel,
593
619
  obtainedAt: this.now()
594
620
  };
595
621
  await this.options.store.modify(async () => grant);
@@ -598,12 +624,12 @@ var AuthManager = class {
598
624
  this.flow = null;
599
625
  this.pendingSignIn = null;
600
626
  this.source = "device";
601
- this.label = workspaceLabel;
627
+ this.label = answer.workspaceLabel;
602
628
  this.set("connected");
603
- this.options.onSignedIn?.(workspaceLabel);
629
+ this.options.onSignedIn?.(answer.workspaceLabel);
604
630
  return;
605
631
  }
606
- this.fail(flow, { code: "expired", message: "The sign-in link expired. Start again." });
632
+ this.fail(flow, { code: "expired", message: EXPIRED });
607
633
  } catch (error) {
608
634
  if (flow.signal.aborted)
609
635
  return;
@@ -728,6 +754,20 @@ var obj = (value) => typeof value === "object" && value !== null && !Array.isArr
728
754
  var str = (value) => typeof value === "string" && value.trim() !== "" ? value.trim() : null;
729
755
  var num = (value) => typeof value === "number" && Number.isFinite(value) && value > 0 ? value : null;
730
756
  var count = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
757
+ function pricesFromRow(raw) {
758
+ const pricing = obj(raw);
759
+ const price = (value) => {
760
+ const parsed = typeof value === "string" && value.trim() !== "" ? Number(value) : typeof value === "number" ? value : Number.NaN;
761
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
762
+ };
763
+ const input = price(pricing?.prompt);
764
+ const memoryWrite = price(pricing?.input_cache_write);
765
+ const memoryRead = price(pricing?.input_cache_read);
766
+ const output = price(pricing?.completion);
767
+ if (input === null || memoryWrite === null || memoryRead === null || output === null)
768
+ return null;
769
+ return { input, memoryWrite, memoryRead, output };
770
+ }
731
771
  var LEVEL_NAMES = {
732
772
  off: "Off",
733
773
  minimal: "Minimal",
@@ -816,17 +856,17 @@ function placementsFromRoutes(routes) {
816
856
  const model2 = str(route?.model);
817
857
  if (route === null || model2 === null || route.enabled !== true)
818
858
  continue;
819
- const add = (a, b) => a === null ? b : b === null ? a : a + b;
859
+ const add2 = (a, b) => a === null ? b : b === null ? a : a + b;
820
860
  const current = out.get(model2);
821
861
  const running = count(route.running);
822
862
  const waiting = count(route.waiting);
823
863
  out.set(model2, {
824
864
  name: current?.name ?? (str(route.name) !== null ? routeModelName(str(route.name)) : null),
825
865
  status: "running",
826
- lanes: add(current?.lanes ?? null, count(route.lanes)),
827
- maxParallel: add(current?.maxParallel ?? null, count(route.max_parallel_agents)),
828
- runningNow: add(current?.runningNow ?? null, running),
829
- waitingNow: add(current?.waitingNow ?? null, waiting),
866
+ lanes: add2(current?.lanes ?? null, count(route.lanes)),
867
+ maxParallel: add2(current?.maxParallel ?? null, count(route.max_parallel_agents)),
868
+ runningNow: add2(current?.runningNow ?? null, running),
869
+ waitingNow: add2(current?.waitingNow ?? null, waiting),
830
870
  region: current?.region ?? str(route.region),
831
871
  card: null,
832
872
  contextWindow: null,
@@ -887,7 +927,8 @@ function model(id, row, placement) {
887
927
  maxParallel: placement?.maxParallel ?? count(gateway?.max_parallel_agents),
888
928
  runningNow: placement?.runningNow ?? null,
889
929
  waitingNow: placement?.waitingNow ?? null,
890
- region: placement?.region ?? str(gateway?.region)
930
+ region: placement?.region ?? str(gateway?.region),
931
+ prices: pricesFromRow(row?.pricing)
891
932
  };
892
933
  }
893
934
  function selectableLevels(reasoning) {
@@ -956,6 +997,60 @@ function laneLimit(offer) {
956
997
  return free < offer.minLanes ? 0 : Math.min(offer.maxLanes, free);
957
998
  }
958
999
 
1000
+ // ../core/lib/processed.js
1001
+ var NOTHING_PROCESSED = { requests: 0, newInput: 0, fromMemory: 0, written: 0, output: 0, reasoning: 0, costUsd: 0 };
1002
+ function stepCost(usage, prices) {
1003
+ if (prices === null)
1004
+ return null;
1005
+ return usage.inputTokens * prices.input + (usage.cacheWriteTokens ?? 0) * prices.memoryWrite + (usage.cacheReadTokens ?? 0) * prices.memoryRead + usage.outputTokens * prices.output;
1006
+ }
1007
+ function add(view, step) {
1008
+ return {
1009
+ requests: view.requests + 1,
1010
+ newInput: view.newInput + step.usage.inputTokens,
1011
+ fromMemory: view.fromMemory + (step.usage.cacheReadTokens ?? 0),
1012
+ written: view.written + (step.usage.cacheWriteTokens ?? 0),
1013
+ output: view.output + step.usage.outputTokens,
1014
+ reasoning: view.reasoning + (step.usage.reasoningTokens ?? 0),
1015
+ costUsd: view.costUsd === null || step.costUsd === null ? null : view.costUsd + step.costUsd
1016
+ };
1017
+ }
1018
+ function addStep(record, step, now) {
1019
+ const turnKey = step.turn ?? record?.turnKey ?? null;
1020
+ const fresh = record == null || step.turn !== void 0 && step.turn !== record.turnKey;
1021
+ return {
1022
+ since: record?.since ?? now,
1023
+ at: now,
1024
+ turnKey,
1025
+ turn: add(fresh ? NOTHING_PROCESSED : record.turn, step),
1026
+ session: add(record?.session ?? NOTHING_PROCESSED, step)
1027
+ };
1028
+ }
1029
+ function readProcessed(raw) {
1030
+ const record = typeof raw === "object" && raw !== null ? raw : null;
1031
+ const view = (value) => {
1032
+ const fields = typeof value === "object" && value !== null ? value : null;
1033
+ if (fields === null)
1034
+ return null;
1035
+ const count3 = (name) => {
1036
+ const field = fields[name];
1037
+ return typeof field === "number" && Number.isFinite(field) && field >= 0 ? field : null;
1038
+ };
1039
+ const numbers = { requests: count3("requests"), newInput: count3("newInput"), fromMemory: count3("fromMemory"), written: count3("written"), output: count3("output"), reasoning: count3("reasoning") };
1040
+ if (Object.values(numbers).some((value2) => value2 === null))
1041
+ return null;
1042
+ return { ...numbers, costUsd: count3("costUsd") };
1043
+ };
1044
+ const turn = view(record?.turn);
1045
+ const session = view(record?.session);
1046
+ const since = record?.since;
1047
+ const at = record?.at;
1048
+ if (turn === null || session === null || typeof since !== "number" || typeof at !== "number")
1049
+ return null;
1050
+ const turnKey = typeof record?.turnKey === "number" ? record.turnKey : null;
1051
+ return { since, at, turnKey, turn, session };
1052
+ }
1053
+
959
1054
  // ../core/lib/relations.js
960
1055
  var PARENT_SESSION_HEADER = "X-Adaptiq-Parent-Session-Id";
961
1056
  var SESSION_ROLE_HEADER = "X-Adaptiq-Session-Role";
@@ -1044,6 +1139,22 @@ var en = {
1044
1139
  popKnowledge: "Knowledge",
1045
1140
  addKnowledge: "Add knowledge",
1046
1141
  popWorkspace: "All memory on {model}",
1142
+ popSeesNow: "What the model sees now",
1143
+ procTitle: "Processed",
1144
+ procTurn: "Last turn",
1145
+ procSession: "This conversation",
1146
+ procSteps: "{count} steps",
1147
+ procStepsOne: "1 step",
1148
+ procNewInput: "New input",
1149
+ procFromMemory: "Read from memory",
1150
+ procWritten: "Saved to memory",
1151
+ procOutput: "Output",
1152
+ procThinking: "Thinking, part of output",
1153
+ procCost: "Cost",
1154
+ procExplain: "Each step reads the whole memory again. {percent} came from memory.",
1155
+ procExplainFree: "Each step reads the whole memory again. {percent} came from memory, which is free.",
1156
+ procExplainCheaper: "Each step reads the whole memory again. {percent} came from memory, at a fraction of the input price.",
1157
+ procSince: "Counted on this computer since {date}",
1047
1158
  openInKnowledge: "Open in Knowledge",
1048
1159
  kTitle: "Knowledge",
1049
1160
  kSearch: "Search your knowledge",
@@ -1528,6 +1639,21 @@ function button(id, label, extra = {}) {
1528
1639
  return { id, label: en[label], labelKey: label, ...extra };
1529
1640
  }
1530
1641
  var count2 = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
1642
+ var PROCESSED_SAVE_MS = 2e3;
1643
+ function savedProcessed(raw) {
1644
+ const out = {};
1645
+ if (typeof raw !== "object" || raw === null)
1646
+ return out;
1647
+ for (const [id, value] of Object.entries(raw)) {
1648
+ const record = readProcessed(value);
1649
+ if (record !== null)
1650
+ out[id] = record;
1651
+ }
1652
+ return out;
1653
+ }
1654
+ function processedSummary(record) {
1655
+ return record === void 0 ? null : { turn: record.turn, session: record.session, since: record.since };
1656
+ }
1531
1657
  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.";
1532
1658
  var ATTACH_TRIES = 3;
1533
1659
  function transient(error) {
@@ -1893,6 +2019,7 @@ var Workspace = class {
1893
2019
  listeners = /* @__PURE__ */ new Set();
1894
2020
  state = { preferences: DEFAULT_PREFERENCES, sessions: {} };
1895
2021
  saving = Promise.resolve();
2022
+ persistTimer;
1896
2023
  refreshing = null;
1897
2024
  timer;
1898
2025
  pendingAttach = /* @__PURE__ */ new Map();
@@ -1952,7 +2079,8 @@ var Workspace = class {
1952
2079
  preferences: { ...DEFAULT_PREFERENCES, ...saved.preferences },
1953
2080
  sessions: typeof saved.sessions === "object" && saved.sessions !== null ? saved.sessions : {},
1954
2081
  purchase: saved.purchase ?? null,
1955
- ...saved.defaultModel === "pending" || saved.defaultModel === "done" ? { defaultModel: saved.defaultModel } : {}
2082
+ ...saved.defaultModel === "pending" || saved.defaultModel === "done" ? { defaultModel: saved.defaultModel } : {},
2083
+ processed: savedProcessed(saved.processed)
1956
2084
  };
1957
2085
  this.purchases.restore(saved.purchase);
1958
2086
  }
@@ -1968,6 +2096,10 @@ var Workspace = class {
1968
2096
  this.purchases.dispose();
1969
2097
  clearTimeout(this.timer);
1970
2098
  clearTimeout(this.attachTimer);
2099
+ if (this.persistTimer !== void 0) {
2100
+ clearTimeout(this.persistTimer);
2101
+ void this.persist();
2102
+ }
1971
2103
  for (const session of this.sessions.values()) {
1972
2104
  clearTimeout(session.timer);
1973
2105
  clearTimeout(session.waitTimer);
@@ -2035,7 +2167,8 @@ var Workspace = class {
2035
2167
  region: model2.region,
2036
2168
  nativeMemory: model2.nativeMemory,
2037
2169
  tools: model2.tools,
2038
- memory: this.modelMemory.get(model2.id) ?? null
2170
+ memory: this.modelMemory.get(model2.id) ?? null,
2171
+ prices: model2.prices
2039
2172
  };
2040
2173
  }
2041
2174
  /**
@@ -2645,12 +2778,16 @@ var Workspace = class {
2645
2778
  return next;
2646
2779
  }
2647
2780
  async persist() {
2781
+ clearTimeout(this.persistTimer);
2782
+ this.persistTimer = void 0;
2648
2783
  const entries = Object.entries(this.state.sessions).sort((a, b) => b[1].at - a[1].at).slice(0, 500);
2784
+ const processed = Object.entries(this.state.processed ?? {}).sort((a, b) => b[1].at - a[1].at).slice(0, 500);
2649
2785
  const snapshot = {
2650
2786
  preferences: this.state.preferences,
2651
2787
  sessions: Object.fromEntries(entries),
2652
2788
  purchase: this.state.purchase ?? null,
2653
- ...this.state.defaultModel === void 0 ? {} : { defaultModel: this.state.defaultModel }
2789
+ ...this.state.defaultModel === void 0 ? {} : { defaultModel: this.state.defaultModel },
2790
+ ...processed.length === 0 ? {} : { processed: Object.fromEntries(processed) }
2654
2791
  };
2655
2792
  this.saving = this.saving.then(() => this.deps.store.save(snapshot)).catch((error) => {
2656
2793
  this.deps.log?.("warn", `adaptiq: could not save preferences: ${String(error)}`);
@@ -2761,6 +2898,7 @@ var Workspace = class {
2761
2898
  saved: session.saved,
2762
2899
  optimizing: session.optimizing,
2763
2900
  lastTurn: session.lastTurn,
2901
+ processed: processedSummary(this.state.processed?.[id]),
2764
2902
  knowledge: [...session.knowledge, ...[...session.pendingKnowledge.values()].filter((item) => !session.knowledge.some((entry) => entry.id === item.id))],
2765
2903
  conversations: [...session.conversations, ...[...session.pendingConversations.values()].filter((item) => !session.conversations.some((entry) => entry.id === item.id))],
2766
2904
  known: session.exists,
@@ -3140,10 +3278,19 @@ var Workspace = class {
3140
3278
  this.setPhase(session, "responding");
3141
3279
  this.clearNotices("busy", "preparing", "offline", "retrying");
3142
3280
  }
3143
- usage(sessionId, model2, usage) {
3281
+ usage(sessionId, model2, usage, step = {}) {
3144
3282
  if (sessionId === void 0)
3145
3283
  return;
3146
3284
  const session = this.session(sessionId);
3285
+ const costUsd = step.costUsd ?? stepCost(usage, this.catalog.find((entry) => entry.id === model2)?.prices ?? null);
3286
+ const counted = addStep(this.state.processed?.[sessionId], { usage, costUsd, ...step.turn === void 0 ? {} : { turn: step.turn } }, this.now());
3287
+ this.state = { ...this.state, processed: { ...this.state.processed, [sessionId]: counted } };
3288
+ if (this.persistTimer === void 0) {
3289
+ this.persistTimer = setTimeout(() => {
3290
+ void this.persist();
3291
+ }, PROCESSED_SAVE_MS);
3292
+ this.persistTimer.unref?.();
3293
+ }
3147
3294
  session.lastTurn = {
3148
3295
  fromMemory: usage.cacheReadTokens ?? 0,
3149
3296
  newInput: usage.inputTokens,
@@ -5111,7 +5258,7 @@ function userTexts(messages) {
5111
5258
  }
5112
5259
 
5113
5260
  // src/index.ts
5114
- var VERSION = true ? "0.1.0" : "0.0.0";
5261
+ var VERSION = true ? "0.1.1" : "0.0.0";
5115
5262
  var PROVIDER = "adaptiq";
5116
5263
  var DEVICE_PROFILE = `${PROVIDER}:device`;
5117
5264
  var DEVICE_TOKEN = "adaptiq-device-sign-in";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adaptiqai/openclaw",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "AdaptiqAI for OpenClaw: your workspace's models, conversations AdaptiqAI remembers, and your saved knowledge, in one OpenClaw plugin.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -46,7 +46,7 @@
46
46
  "esbuild": "~0.25.12",
47
47
  "typescript": "~5.9.3",
48
48
  "vitest": "~3.2.7",
49
- "@adaptiqai/core": "0.2.10"
49
+ "@adaptiqai/core": "0.2.11"
50
50
  },
51
51
  "scripts": {
52
52
  "build": "node scripts/build.mjs",