@ai-ecoverse/slicc-agent 7.0.0 → 7.1.0

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.
@@ -1,20 +1,36 @@
1
+ import { selectedBudget } from "./budget.js";
1
2
  import { Emitter } from "./emitter.js";
2
3
  class TrayAdapter extends Emitter {
3
4
  #settings;
4
- constructor(settings) {
5
+ #selected;
6
+ #shown = "";
7
+ constructor(settings, selected) {
5
8
  super();
6
9
  this.#settings = settings;
10
+ this.#selected = selected;
7
11
  settings.state.subscribe(() => this.emit("status", this.status()));
12
+ selected?.onChange(() => {
13
+ const before = this.#shown;
14
+ const status = this.status();
15
+ if (this.#shown !== before) this.emit("status", status);
16
+ });
8
17
  }
9
18
  status() {
10
- const budget = this.#settings.state.value?.budget;
19
+ const budget = selectedBudget(this.#settings.state.value, this.#selected?.provider());
20
+ const shown = budget ? {
21
+ percent: budget.percent,
22
+ window: budget.window,
23
+ resets: budget.resets,
24
+ provider: budget.provider
25
+ } : { percent: 0, window: "weekly", resets: "" };
26
+ this.#shown = JSON.stringify(shown);
11
27
  return {
12
28
  name: "slicc",
13
29
  kind: "hosted",
14
30
  connection: "offline",
15
31
  role: "none",
16
32
  followers: [],
17
- budget: budget ? { percent: budget.percent, window: budget.window, resets: budget.resets } : { percent: 0, window: "weekly", resets: "" },
33
+ budget: shown,
18
34
  joinUrl: ""
19
35
  };
20
36
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/spectrum/tray-port.ts"],
4
- "sourcesContent": ["import type { TrayEvents, TrayPort, TrayStatus } from '@ai-ecoverse/slicc-spectrum/ui';\nimport type { AgentSettings } from '../services.ts';\nimport { Emitter } from './emitter.ts';\n\nexport class TrayAdapter extends Emitter<TrayEvents> implements TrayPort {\n readonly #settings: AgentSettings;\n\n constructor(settings: AgentSettings) {\n super();\n this.#settings = settings;\n settings.state.subscribe(() => this.emit('status', this.status()));\n }\n\n status(): TrayStatus {\n const budget = this.#settings.state.value?.budget;\n return {\n name: 'slicc',\n kind: 'hosted',\n connection: 'offline',\n role: 'none',\n followers: [],\n budget: budget\n ? { percent: budget.percent, window: budget.window, resets: budget.resets }\n : { percent: 0, window: 'weekly', resets: '' },\n joinUrl: '',\n };\n }\n\n reconnect(): void {}\n\n disconnect(): void {}\n}\n"],
5
- "mappings": "AAEA,SAAS,eAAe;AAEjB,MAAM,oBAAoB,QAAwC;AAAA,EAC9D;AAAA,EAET,YAAY,UAAyB;AACnC,UAAM;AACN,SAAK,YAAY;AACjB,aAAS,MAAM,UAAU,MAAM,KAAK,KAAK,UAAU,KAAK,OAAO,CAAC,CAAC;AAAA,EACnE;AAAA,EAEA,SAAqB;AACnB,UAAM,SAAS,KAAK,UAAU,MAAM,OAAO;AAC3C,WAAO;AAAA,MACL,MAAM;AAAA,MACN,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,MAAM;AAAA,MACN,WAAW,CAAC;AAAA,MACZ,QAAQ,SACJ,EAAE,SAAS,OAAO,SAAS,QAAQ,OAAO,QAAQ,QAAQ,OAAO,OAAO,IACxE,EAAE,SAAS,GAAG,QAAQ,UAAU,QAAQ,GAAG;AAAA,MAC/C,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EAEA,YAAkB;AAAA,EAAC;AAAA,EAEnB,aAAmB;AAAA,EAAC;AACtB;",
4
+ "sourcesContent": ["import type { TrayEvents, TrayPort, TrayStatus } from '@ai-ecoverse/slicc-spectrum/ui';\nimport type { AgentSettings } from '../services.ts';\nimport { type Selected, selectedBudget } from './budget.ts';\nimport { Emitter } from './emitter.ts';\n\nexport class TrayAdapter extends Emitter<TrayEvents> implements TrayPort {\n readonly #settings: AgentSettings;\n readonly #selected: Selected | undefined;\n #shown = '';\n\n constructor(settings: AgentSettings, selected?: Selected) {\n super();\n this.#settings = settings;\n this.#selected = selected;\n settings.state.subscribe(() => this.emit('status', this.status()));\n selected?.onChange(() => {\n const before = this.#shown;\n const status = this.status();\n if (this.#shown !== before) this.emit('status', status);\n });\n }\n\n status(): TrayStatus {\n const budget = selectedBudget(this.#settings.state.value, this.#selected?.provider());\n const shown = budget\n ? {\n percent: budget.percent,\n window: budget.window,\n resets: budget.resets,\n provider: budget.provider,\n }\n : { percent: 0, window: 'weekly' as const, resets: '' };\n this.#shown = JSON.stringify(shown);\n return {\n name: 'slicc',\n kind: 'hosted',\n connection: 'offline',\n role: 'none',\n followers: [],\n budget: shown,\n joinUrl: '',\n };\n }\n\n reconnect(): void {}\n\n disconnect(): void {}\n}\n"],
5
+ "mappings": "AAEA,SAAwB,sBAAsB;AAC9C,SAAS,eAAe;AAEjB,MAAM,oBAAoB,QAAwC;AAAA,EAC9D;AAAA,EACA;AAAA,EACT,SAAS;AAAA,EAET,YAAY,UAAyB,UAAqB;AACxD,UAAM;AACN,SAAK,YAAY;AACjB,SAAK,YAAY;AACjB,aAAS,MAAM,UAAU,MAAM,KAAK,KAAK,UAAU,KAAK,OAAO,CAAC,CAAC;AACjE,cAAU,SAAS,MAAM;AACvB,YAAM,SAAS,KAAK;AACpB,YAAM,SAAS,KAAK,OAAO;AAC3B,UAAI,KAAK,WAAW,OAAQ,MAAK,KAAK,UAAU,MAAM;AAAA,IACxD,CAAC;AAAA,EACH;AAAA,EAEA,SAAqB;AACnB,UAAM,SAAS,eAAe,KAAK,UAAU,MAAM,OAAO,KAAK,WAAW,SAAS,CAAC;AACpF,UAAM,QAAQ,SACV;AAAA,MACE,SAAS,OAAO;AAAA,MAChB,QAAQ,OAAO;AAAA,MACf,QAAQ,OAAO;AAAA,MACf,UAAU,OAAO;AAAA,IACnB,IACA,EAAE,SAAS,GAAG,QAAQ,UAAmB,QAAQ,GAAG;AACxD,SAAK,SAAS,KAAK,UAAU,KAAK;AAClC,WAAO;AAAA,MACL,MAAM;AAAA,MACN,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,MAAM;AAAA,MACN,WAAW,CAAC;AAAA,MACZ,QAAQ;AAAA,MACR,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EAEA,YAAkB;AAAA,EAAC;AAAA,EAEnB,aAAmB;AAAA,EAAC;AACtB;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,73 @@
1
+ const XAI = "xai";
2
+ const XAI_USAGE_URL = "https://cli-chat-proxy.grok.com/v1/billing?format=credits";
3
+ const USAGE_TIMEOUT_MS = 8e3;
4
+ const MAX_USAGE_BYTES = 64 * 1024;
5
+ const WEEKLY = "USAGE_PERIOD_TYPE_WEEKLY";
6
+ async function capped(response, limit) {
7
+ const reader = response.body?.getReader();
8
+ if (!reader) return null;
9
+ const parts = [];
10
+ let size = 0;
11
+ for (; ; ) {
12
+ const { done, value } = await reader.read();
13
+ if (done) break;
14
+ size += value.byteLength;
15
+ if (size > limit) {
16
+ await reader.cancel().catch(() => void 0);
17
+ return null;
18
+ }
19
+ parts.push(value);
20
+ }
21
+ const bytes = new Uint8Array(size);
22
+ let at = 0;
23
+ for (const part of parts) {
24
+ bytes.set(part, at);
25
+ at += part.byteLength;
26
+ }
27
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
28
+ }
29
+ const record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
30
+ function grokBudget(text) {
31
+ let parsed;
32
+ try {
33
+ parsed = JSON.parse(text);
34
+ } catch {
35
+ return null;
36
+ }
37
+ const config = record(record(parsed)?.config);
38
+ const period = record(config?.currentPeriod);
39
+ const percent = config?.creditUsagePercent;
40
+ if (period?.type !== WEEKLY || typeof percent !== "number" || !Number.isFinite(percent))
41
+ return null;
42
+ const end = typeof period.end === "string" ? new Date(period.end) : void 0;
43
+ if (!end || Number.isNaN(end.getTime())) return null;
44
+ return {
45
+ percent: Math.round(Math.min(100, Math.max(0, percent)) * 10) / 10,
46
+ window: "weekly",
47
+ resets: end.toISOString()
48
+ };
49
+ }
50
+ async function xaiUsage(token, {
51
+ fetch: fetcher = (input, init) => globalThis.fetch(input, init),
52
+ timeout = USAGE_TIMEOUT_MS
53
+ } = {}) {
54
+ const response = await fetcher(XAI_USAGE_URL, {
55
+ headers: { Accept: "application/json", Authorization: `Bearer ${token}` },
56
+ signal: AbortSignal.timeout(timeout)
57
+ });
58
+ if (!response.ok) {
59
+ await response.body?.cancel().catch(() => void 0);
60
+ return null;
61
+ }
62
+ const text = await capped(response, MAX_USAGE_BYTES);
63
+ return text === null ? null : grokBudget(text);
64
+ }
65
+ export {
66
+ MAX_USAGE_BYTES,
67
+ USAGE_TIMEOUT_MS,
68
+ XAI,
69
+ XAI_USAGE_URL,
70
+ grokBudget,
71
+ xaiUsage
72
+ };
73
+ //# sourceMappingURL=supergrok.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/supergrok.ts"],
4
+ "sourcesContent": ["import type { Budget } from './adobe.ts';\n\nexport const XAI = 'xai';\nexport const XAI_USAGE_URL = 'https://cli-chat-proxy.grok.com/v1/billing?format=credits';\nexport const USAGE_TIMEOUT_MS = 8_000;\nexport const MAX_USAGE_BYTES = 64 * 1024;\n\nconst WEEKLY = 'USAGE_PERIOD_TYPE_WEEKLY';\n\ntype Fetch = typeof fetch;\n\nasync function capped(response: Response, limit: number): Promise<string | null> {\n const reader = response.body?.getReader();\n if (!reader) return null;\n const parts: Uint8Array[] = [];\n let size = 0;\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n size += value.byteLength;\n if (size > limit) {\n await reader.cancel().catch(() => undefined);\n return null;\n }\n parts.push(value);\n }\n const bytes = new Uint8Array(size);\n let at = 0;\n for (const part of parts) {\n bytes.set(part, at);\n at += part.byteLength;\n }\n return new TextDecoder('utf-8', { fatal: true }).decode(bytes);\n}\n\nconst record = (value: unknown): Record<string, unknown> | undefined =>\n value && typeof value === 'object' && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n\nexport function grokBudget(text: string): Budget | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n return null;\n }\n const config = record(record(parsed)?.config);\n const period = record(config?.currentPeriod);\n const percent = config?.creditUsagePercent;\n if (period?.type !== WEEKLY || typeof percent !== 'number' || !Number.isFinite(percent))\n return null;\n const end = typeof period.end === 'string' ? new Date(period.end) : undefined;\n if (!end || Number.isNaN(end.getTime())) return null;\n return {\n percent: Math.round(Math.min(100, Math.max(0, percent)) * 10) / 10,\n window: 'weekly',\n resets: end.toISOString(),\n };\n}\n\nexport async function xaiUsage(\n token: string,\n {\n fetch: fetcher = (input, init) => globalThis.fetch(input, init),\n timeout = USAGE_TIMEOUT_MS,\n }: { fetch?: Fetch; timeout?: number } = {}\n): Promise<Budget | null> {\n const response = await fetcher(XAI_USAGE_URL, {\n headers: { Accept: 'application/json', Authorization: `Bearer ${token}` },\n signal: AbortSignal.timeout(timeout),\n });\n if (!response.ok) {\n await response.body?.cancel().catch(() => undefined);\n return null;\n }\n const text = await capped(response, MAX_USAGE_BYTES);\n return text === null ? null : grokBudget(text);\n}\n"],
5
+ "mappings": "AAEO,MAAM,MAAM;AACZ,MAAM,gBAAgB;AACtB,MAAM,mBAAmB;AACzB,MAAM,kBAAkB,KAAK;AAEpC,MAAM,SAAS;AAIf,eAAe,OAAO,UAAoB,OAAuC;AAC/E,QAAM,SAAS,SAAS,MAAM,UAAU;AACxC,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,QAAsB,CAAC;AAC7B,MAAI,OAAO;AACX,aAAS;AACP,UAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,QAAI,KAAM;AACV,YAAQ,MAAM;AACd,QAAI,OAAO,OAAO;AAChB,YAAM,OAAO,OAAO,EAAE,MAAM,MAAM,MAAS;AAC3C,aAAO;AAAA,IACT;AACA,UAAM,KAAK,KAAK;AAAA,EAClB;AACA,QAAM,QAAQ,IAAI,WAAW,IAAI;AACjC,MAAI,KAAK;AACT,aAAW,QAAQ,OAAO;AACxB,UAAM,IAAI,MAAM,EAAE;AAClB,UAAM,KAAK;AAAA,EACb;AACA,SAAO,IAAI,YAAY,SAAS,EAAE,OAAO,KAAK,CAAC,EAAE,OAAO,KAAK;AAC/D;AAEA,MAAM,SAAS,CAAC,UACd,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IACrD,QACD;AAEC,SAAS,WAAW,MAA6B;AACtD,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,IAAI;AAAA,EAC1B,QAAQ;AACN,WAAO;AAAA,EACT;AACA,QAAM,SAAS,OAAO,OAAO,MAAM,GAAG,MAAM;AAC5C,QAAM,SAAS,OAAO,QAAQ,aAAa;AAC3C,QAAM,UAAU,QAAQ;AACxB,MAAI,QAAQ,SAAS,UAAU,OAAO,YAAY,YAAY,CAAC,OAAO,SAAS,OAAO;AACpF,WAAO;AACT,QAAM,MAAM,OAAO,OAAO,QAAQ,WAAW,IAAI,KAAK,OAAO,GAAG,IAAI;AACpE,MAAI,CAAC,OAAO,OAAO,MAAM,IAAI,QAAQ,CAAC,EAAG,QAAO;AAChD,SAAO;AAAA,IACL,SAAS,KAAK,MAAM,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,IAAI;AAAA,IAChE,QAAQ;AAAA,IACR,QAAQ,IAAI,YAAY;AAAA,EAC1B;AACF;AAEA,eAAsB,SACpB,OACA;AAAA,EACE,OAAO,UAAU,CAAC,OAAO,SAAS,WAAW,MAAM,OAAO,IAAI;AAAA,EAC9D,UAAU;AACZ,IAAyC,CAAC,GAClB;AACxB,QAAM,WAAW,MAAM,QAAQ,eAAe;AAAA,IAC5C,SAAS,EAAE,QAAQ,oBAAoB,eAAe,UAAU,KAAK,GAAG;AAAA,IACxE,QAAQ,YAAY,QAAQ,OAAO;AAAA,EACrC,CAAC;AACD,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,SAAS,MAAM,OAAO,EAAE,MAAM,MAAM,MAAS;AACnD,WAAO;AAAA,EACT;AACA,QAAM,OAAO,MAAM,OAAO,UAAU,eAAe;AACnD,SAAO,SAAS,OAAO,OAAO,WAAW,IAAI;AAC/C;",
6
+ "names": []
7
+ }
@@ -401,6 +401,7 @@ export interface SettingsState {
401
401
  accounts: AccountState[];
402
402
  providers?: ProviderOption[];
403
403
  budget?: BudgetState | null;
404
+ budgets?: BudgetState[];
404
405
  moved?: {
405
406
  kind: 'model' | 'mcp';
406
407
  label: string;
@@ -8,7 +8,7 @@ import type { KeysMoved } from './secrets/legacy.ts';
8
8
  import type { VaultRecords } from './secrets/records.ts';
9
9
  import type { PlainStore } from './secrets/store.ts';
10
10
  import type { Vault } from './secrets/vault.ts';
11
- import type { AccountField, AgentSettings } from './services.ts';
11
+ import type { AccountField, AgentSettings, BudgetState } from './services.ts';
12
12
  export { BEDROCK, PREFERRED_MODELS, preferredModel } from './preferred.ts';
13
13
  export declare const DEFAULT_MODEL: {
14
14
  provider: string;
@@ -37,6 +37,11 @@ export interface ProviderFields {
37
37
  }
38
38
  export declare function providerFields(store: PlainStore, providers: readonly Provider[]): Promise<ProviderFields>;
39
39
  export type UsageReader = (token: string) => Promise<Budget | null>;
40
+ export declare function grokBudgets(records: Pick<VaultRecords, 'model'>, usage: UsageReader, { reach, every, now }: {
41
+ reach?: Reach;
42
+ every: number;
43
+ now: () => number;
44
+ }): () => Promise<BudgetState | null>;
40
45
  export interface SettingsSources {
41
46
  records: VaultRecords;
42
47
  fields: ProviderFields;
@@ -49,4 +54,6 @@ export declare function createAgentSettings(models: MutableModels, sources: Sett
49
54
  reach?: Reach;
50
55
  vault?: Pick<Vault, 'connected' | 'needed' | 'grantProvider'>;
51
56
  flows?: DeviceFlows;
57
+ grok?: UsageReader;
58
+ now?: () => number;
52
59
  }): Promise<AgentSettings>;
@@ -0,0 +1,16 @@
1
+ import type { BudgetState, SettingsState } from '../services.ts';
2
+ export interface Selected {
3
+ provider(): string | undefined;
4
+ onChange(listener: () => void): () => void;
5
+ }
6
+ export declare const BUDGET_NAMES: Readonly<Record<string, string>>;
7
+ export declare const budgetName: (provider: string) => string;
8
+ export declare function selectedBudget(state: SettingsState | undefined, provider: string | undefined): BudgetState | null;
9
+ export declare function agentSelection(agent: {
10
+ active(): string;
11
+ list(): readonly {
12
+ id: string;
13
+ model: string;
14
+ }[];
15
+ on(type: 'agents' | 'active', listener: () => void): () => void;
16
+ }): Selected;
@@ -1,6 +1,7 @@
1
1
  import type { MonitorAlert, MonitorEvents, MonitorPort, MonitorSection, MonitorSnapshot } from '@ai-ecoverse/slicc-spectrum/ui';
2
2
  import type { AgentConnection } from '../client.ts';
3
3
  import type { BudgetState, ModelChoice, MonitorFeed, MonitorProcess, MonitorTask } from '../services.ts';
4
+ import { type Selected } from './budget.ts';
4
5
  export declare const RENEW_MS = 10000;
5
6
  export declare const SAMPLE_MS = 2000;
6
7
  export declare const ROWS_MAX = 200;
@@ -83,6 +84,7 @@ export interface MonitorAdapterOptions {
83
84
  sampleMs?: number;
84
85
  intervals?: Intervals;
85
86
  now?: () => number;
87
+ selected?: Selected;
86
88
  }
87
89
  export declare class MonitorAdapter implements MonitorPort {
88
90
  #private;
@@ -1,9 +1,10 @@
1
1
  import type { TrayEvents, TrayPort, TrayStatus } from '@ai-ecoverse/slicc-spectrum/ui';
2
2
  import type { AgentSettings } from '../services.ts';
3
+ import { type Selected } from './budget.ts';
3
4
  import { Emitter } from './emitter.ts';
4
5
  export declare class TrayAdapter extends Emitter<TrayEvents> implements TrayPort {
5
6
  #private;
6
- constructor(settings: AgentSettings);
7
+ constructor(settings: AgentSettings, selected?: Selected);
7
8
  status(): TrayStatus;
8
9
  reconnect(): void;
9
10
  disconnect(): void;
@@ -0,0 +1,12 @@
1
+ import type { Budget } from './adobe.ts';
2
+ export declare const XAI = "xai";
3
+ export declare const XAI_USAGE_URL = "https://cli-chat-proxy.grok.com/v1/billing?format=credits";
4
+ export declare const USAGE_TIMEOUT_MS = 8000;
5
+ export declare const MAX_USAGE_BYTES: number;
6
+ type Fetch = typeof fetch;
7
+ export declare function grokBudget(text: string): Budget | null;
8
+ export declare function xaiUsage(token: string, { fetch: fetcher, timeout, }?: {
9
+ fetch?: Fetch;
10
+ timeout?: number;
11
+ }): Promise<Budget | null>;
12
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc-agent",
3
- "version": "7.0.0",
3
+ "version": "7.1.0",
4
4
  "description": "SLICC's agent core on pi durable: an agent worker, session storage, remoting and pi extensions",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -67,7 +67,7 @@
67
67
  "devDependencies": {
68
68
  "@ai-ecoverse/slicc-kernel": "1.146.2",
69
69
  "@ai-ecoverse/slicc-scrub": "1.0.0",
70
- "@ai-ecoverse/slicc-shared-web": "2.9.0",
70
+ "@ai-ecoverse/slicc-shared-web": "2.10.2",
71
71
  "@ai-ecoverse/slicc-spectrum": "1.107.0",
72
72
  "@ai-ecoverse/wasi-fnox": "1.39.0-6",
73
73
  "@ai-ecoverse/wasm-bash": "5.3.0-23",
@@ -86,7 +86,7 @@
86
86
  },
87
87
  "dependencies": {
88
88
  "@ai-ecoverse/slicc-browsers": "1.18.7",
89
- "@ai-ecoverse/slicc-vault": "1.15.5",
89
+ "@ai-ecoverse/slicc-vault": "1.16.0",
90
90
  "@aws-sdk/middleware-websocket": "^3.972.54",
91
91
  "@earendil-works/chord": "1.4.0",
92
92
  "@earendil-works/pi-ai": "1.4.0",