brainjar 0.3.975 → 0.3.976

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/brainjar.js +355 -69
  2. package/package.json +1 -1
package/brainjar.js CHANGED
@@ -107,7 +107,7 @@ var CLI_VERSION, CACHE, DAY;
107
107
  var init_version_check = __esm({
108
108
  "packages/cli/src/version-check.ts"() {
109
109
  "use strict";
110
- CLI_VERSION = "0.3.975";
110
+ CLI_VERSION = "0.3.976";
111
111
  CACHE = path.join(os.homedir(), ".brainjar", "version-check.json");
112
112
  DAY = 864e5;
113
113
  }
@@ -5895,6 +5895,11 @@ function loadEnv() {
5895
5895
  }
5896
5896
  return { ...cachedFileVars, ...process.env };
5897
5897
  }
5898
+ function estimateCostUsd(model, tokens) {
5899
+ const [base = model, tier] = model.split(":");
5900
+ const p = MODEL_PRICES[base] ?? MODEL_PRICES[MODEL_TIERS.standard];
5901
+ return (tier === "flex" ? 0.5 : 1) * (tokens.input * p.input + tokens.cachedInput * p.cachedInput + tokens.output * p.output) / 1e6;
5902
+ }
5898
5903
  function loadConfig(env = loadEnv()) {
5899
5904
  return new Config(env);
5900
5905
  }
@@ -5907,10 +5912,22 @@ function redactUrlPassword(url) {
5907
5912
  return url;
5908
5913
  }
5909
5914
  }
5910
- var cachedFileVars, DEFAULT_TENANT_ID, SECRETS, CORPORATE_API_KEY_ENV, DEFAULT_DATABASE_URL, MissingSecretError, Config;
5915
+ var cachedFileVars, MODEL_TIERS, MODEL_PRICES, DEFAULT_TENANT_ID, SECRETS, CORPORATE_API_KEY_ENV, DEFAULT_DATABASE_URL, MissingSecretError, Config;
5911
5916
  var init_config = __esm({
5912
5917
  "packages/shared/src/config.ts"() {
5913
5918
  "use strict";
5919
+ MODEL_TIERS = {
5920
+ fast: "claude-haiku-4-5",
5921
+ standard: "claude-sonnet-5",
5922
+ premium: "claude-opus-5"
5923
+ };
5924
+ MODEL_PRICES = {
5925
+ "claude-opus-5": { input: 5, cachedInput: 0.5, output: 25 },
5926
+ // Launch pricing made permanent (official pricing page, checked 2026-08-18).
5927
+ "claude-sonnet-5": { input: 2, cachedInput: 0.2, output: 10 },
5928
+ "claude-haiku-4-5": { input: 1, cachedInput: 0.1, output: 5 },
5929
+ "gpt-5.6-luna": { input: 0.2, cachedInput: 0.02, output: 1.2 }
5930
+ };
5914
5931
  DEFAULT_TENANT_ID = "00000000-0000-0000-0000-000000000002";
5915
5932
  SECRETS = {
5916
5933
  anthropicApiKey: {
@@ -6016,13 +6033,6 @@ var init_config = __esm({
6016
6033
  }
6017
6034
  });
6018
6035
 
6019
- // packages/shared/src/ratelimit.ts
6020
- var init_ratelimit = __esm({
6021
- "packages/shared/src/ratelimit.ts"() {
6022
- "use strict";
6023
- }
6024
- });
6025
-
6026
6036
  // packages/shared/src/sanitize.ts
6027
6037
  var init_sanitize = __esm({
6028
6038
  "packages/shared/src/sanitize.ts"() {
@@ -6216,7 +6226,6 @@ var init_src = __esm({
6216
6226
  init_memory();
6217
6227
  init_confidence();
6218
6228
  init_config();
6219
- init_ratelimit();
6220
6229
  init_sanitize();
6221
6230
  init_flow();
6222
6231
  init_capture_events();
@@ -6238,8 +6247,11 @@ function getPool() {
6238
6247
  }
6239
6248
  pool = new esm_default.Pool({
6240
6249
  connectionString: config.databaseUrl,
6250
+ // Per process (BJ-137): many instances times the default would fill Postgres; each role
6251
+ // sets its own in the compose file. PgBouncer in front is the cloud ticket's (BJ-140).
6252
+ max: Number(process.env.DATABASE_POOL_MAX) || 10,
6241
6253
  // Session GUC set in the startup packet — race-free, applies before any query.
6242
- options: `-c app.tenant_id=${config.tenantId}`
6254
+ options: `-c app.tenant_id=${config.tenantId} -c hnsw.iterative_scan=strict_order`
6243
6255
  });
6244
6256
  pool.on("error", () => {
6245
6257
  });
@@ -7599,7 +7611,7 @@ var init_repository = __esm({
7599
7611
  });
7600
7612
 
7601
7613
  // packages/db/src/ratelimit.ts
7602
- var init_ratelimit2 = __esm({
7614
+ var init_ratelimit = __esm({
7603
7615
  "packages/db/src/ratelimit.ts"() {
7604
7616
  "use strict";
7605
7617
  init_pool();
@@ -7629,7 +7641,7 @@ var init_auth = __esm({
7629
7641
  "use strict";
7630
7642
  init_src();
7631
7643
  init_pool();
7632
- init_ratelimit2();
7644
+ init_ratelimit();
7633
7645
  init_auth_event();
7634
7646
  init_email_token();
7635
7647
  DEFAULT_TTL_HOURS = 24 * 90;
@@ -7776,7 +7788,22 @@ var init_settings = __esm({
7776
7788
  // Voice chat (voice handoff, 2026-09-21): an explicit "true" or "false"; absent means on.
7777
7789
  "voice_enabled",
7778
7790
  // The GPT-Live voice (voice picker, 2026-09-22): an id from VOICE_OPTIONS; absent = default.
7779
- "voice_name"
7791
+ "voice_name",
7792
+ // AI cost cap (BJ-139), set on the Operator page; env overrides, defaults in ingest/allowance.
7793
+ "ai_contingent",
7794
+ // "on" | "off": monthly contingent per workspace (default on in SaaS)
7795
+ "ai_free_monthly_usd",
7796
+ // Free workspace, USD per month
7797
+ "ai_team_seat_monthly_usd",
7798
+ // Team, USD per seat and month (applies once licenses exist)
7799
+ "ai_operator_exempt",
7800
+ // "true" | "false": operator accounts are never capped
7801
+ "ai_daily_budget_usd",
7802
+ // the instance's emergency brake on the company key; empty = off
7803
+ "ai_budget_released_day",
7804
+ // YYYY-MM-DD the operator lifted today's brake
7805
+ "ai_budget_alerted_day"
7806
+ // YYYY-MM-DD the operator was told the brake tripped
7780
7807
  ];
7781
7808
  PROVIDER_KEY_SETTINGS = {
7782
7809
  anthropic: "anthropic_api_key",
@@ -7810,10 +7837,102 @@ var init_tickets = __esm({
7810
7837
  });
7811
7838
 
7812
7839
  // packages/db/src/jobs.ts
7840
+ var CAPTURE_JOB_KINDS, USER_JOB_KINDS;
7813
7841
  var init_jobs = __esm({
7814
7842
  "packages/db/src/jobs.ts"() {
7815
7843
  "use strict";
7816
7844
  init_pool();
7845
+ CAPTURE_JOB_KINDS = [
7846
+ "capture-extract",
7847
+ "capture-session",
7848
+ "capture-transcript",
7849
+ "capture-structure"
7850
+ ];
7851
+ USER_JOB_KINDS = [
7852
+ ...CAPTURE_JOB_KINDS,
7853
+ "deepscan",
7854
+ "verify",
7855
+ "summary-report",
7856
+ "resynthesize"
7857
+ ];
7858
+ }
7859
+ });
7860
+
7861
+ // packages/db/src/spend.ts
7862
+ function dayKey(at = /* @__PURE__ */ new Date()) {
7863
+ return `day:${at.toISOString().slice(0, 10)}`;
7864
+ }
7865
+ async function addSpend(brainId, usd, at = /* @__PURE__ */ new Date(), pool2 = getPool()) {
7866
+ if (!(usd > 0)) return;
7867
+ await pool2.query(
7868
+ `INSERT INTO llm_spend (key, usd)
7869
+ SELECT k, $2 FROM (
7870
+ SELECT $3::text AS k
7871
+ UNION ALL
7872
+ SELECT 'ws:' || b.owner_id || ':' || $4 FROM brain b
7873
+ WHERE b.id = $1 AND b.owner_id IS NOT NULL
7874
+ ) keys
7875
+ ON CONFLICT (key) DO UPDATE SET usd = llm_spend.usd + EXCLUDED.usd, updated_at = now()`,
7876
+ [brainId, usd, dayKey(at), at.toISOString().slice(0, 7)]
7877
+ );
7878
+ }
7879
+ var init_spend = __esm({
7880
+ "packages/db/src/spend.ts"() {
7881
+ "use strict";
7882
+ init_pool();
7883
+ }
7884
+ });
7885
+
7886
+ // packages/db/src/provider-rate.ts
7887
+ async function saveRate(keyId2, model, s, pool2 = getPool()) {
7888
+ await pool2.query(
7889
+ `INSERT INTO llm_rate (key_id, model, requests_limit, requests_remaining, requests_reset_at,
7890
+ tokens_limit, tokens_remaining, tokens_reset_at, observed_at)
7891
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
7892
+ ON CONFLICT (key_id, model) DO UPDATE SET
7893
+ requests_limit = EXCLUDED.requests_limit,
7894
+ requests_remaining = EXCLUDED.requests_remaining,
7895
+ requests_reset_at = EXCLUDED.requests_reset_at,
7896
+ tokens_limit = EXCLUDED.tokens_limit,
7897
+ tokens_remaining = EXCLUDED.tokens_remaining,
7898
+ tokens_reset_at = EXCLUDED.tokens_reset_at,
7899
+ observed_at = EXCLUDED.observed_at
7900
+ WHERE llm_rate.observed_at <= EXCLUDED.observed_at`,
7901
+ [
7902
+ keyId2,
7903
+ model,
7904
+ s.requestsLimit,
7905
+ s.requestsRemaining,
7906
+ s.requestsResetAt,
7907
+ s.tokensLimit,
7908
+ s.tokensRemaining,
7909
+ s.tokensResetAt,
7910
+ s.observedAt
7911
+ ]
7912
+ );
7913
+ }
7914
+ async function loadRate(keyId2, model, pool2 = getPool()) {
7915
+ const r = await pool2.query(
7916
+ "SELECT * FROM llm_rate WHERE key_id = $1 AND model = $2",
7917
+ [keyId2, model]
7918
+ );
7919
+ const x = r.rows[0];
7920
+ if (!x) return null;
7921
+ const n2 = (v) => v === null ? null : Number(v);
7922
+ return {
7923
+ requestsLimit: n2(x.requests_limit),
7924
+ requestsRemaining: n2(x.requests_remaining),
7925
+ requestsResetAt: x.requests_reset_at,
7926
+ tokensLimit: n2(x.tokens_limit),
7927
+ tokensRemaining: n2(x.tokens_remaining),
7928
+ tokensResetAt: x.tokens_reset_at,
7929
+ observedAt: x.observed_at
7930
+ };
7931
+ }
7932
+ var init_provider_rate = __esm({
7933
+ "packages/db/src/provider-rate.ts"() {
7934
+ "use strict";
7935
+ init_pool();
7817
7936
  }
7818
7937
  });
7819
7938
 
@@ -7841,11 +7960,21 @@ async function recordLlmUsage(u) {
7841
7960
  Math.max(0, Math.round(u.outputTokens))
7842
7961
  ]
7843
7962
  );
7963
+ await addSpend(
7964
+ u.brainId ?? null,
7965
+ estimateCostUsd(u.model, {
7966
+ input: u.inputTokens,
7967
+ cachedInput: u.cachedInputTokens,
7968
+ output: u.outputTokens
7969
+ })
7970
+ );
7844
7971
  }
7845
7972
  var init_usage = __esm({
7846
7973
  "packages/db/src/usage.ts"() {
7847
7974
  "use strict";
7975
+ init_src();
7848
7976
  init_pool();
7977
+ init_spend();
7849
7978
  }
7850
7979
  });
7851
7980
 
@@ -7859,7 +7988,7 @@ var init_src2 = __esm({
7859
7988
  init_auth();
7860
7989
  init_api_token();
7861
7990
  init_oauth();
7862
- init_ratelimit2();
7991
+ init_ratelimit();
7863
7992
  init_auth_event();
7864
7993
  init_email_token();
7865
7994
  init_totp2();
@@ -7872,6 +8001,8 @@ var init_src2 = __esm({
7872
8001
  init_conversations();
7873
8002
  init_tickets();
7874
8003
  init_jobs();
8004
+ init_spend();
8005
+ init_provider_rate();
7875
8006
  init_ruleset();
7876
8007
  init_usage();
7877
8008
  init_audit_origin();
@@ -12740,11 +12871,118 @@ var init_sdk = __esm({
12740
12871
  }
12741
12872
  });
12742
12873
 
12874
+ // packages/ingest/src/provider-rate.ts
12875
+ import { createHash as createHash2 } from "node:crypto";
12876
+ function parseDuration(s) {
12877
+ if (!s) return null;
12878
+ let ms = 0;
12879
+ let hit = false;
12880
+ for (const [, n2, unit] of s.matchAll(/(\d+(?:\.\d+)?)(ms|h|m|s)/g)) {
12881
+ hit = true;
12882
+ ms += Number(n2) * UNIT_MS[unit];
12883
+ }
12884
+ return hit ? ms : null;
12885
+ }
12886
+ function parseRateHeaders(h, now = /* @__PURE__ */ new Date()) {
12887
+ const num = (a, b) => {
12888
+ const v = h.get(a) ?? h.get(b);
12889
+ const n2 = v === null ? NaN : Number(v);
12890
+ return Number.isFinite(n2) ? n2 : null;
12891
+ };
12892
+ const at = (openai, anthropic) => {
12893
+ const d = parseDuration(h.get(openai));
12894
+ if (d !== null) return new Date(now.getTime() + d);
12895
+ const t = Date.parse(h.get(anthropic) ?? "");
12896
+ return Number.isFinite(t) ? new Date(t) : null;
12897
+ };
12898
+ const s = {
12899
+ requestsLimit: num(
12900
+ "x-ratelimit-limit-requests",
12901
+ "anthropic-ratelimit-requests-limit"
12902
+ ),
12903
+ requestsRemaining: num(
12904
+ "x-ratelimit-remaining-requests",
12905
+ "anthropic-ratelimit-requests-remaining"
12906
+ ),
12907
+ requestsResetAt: at(
12908
+ "x-ratelimit-reset-requests",
12909
+ "anthropic-ratelimit-requests-reset"
12910
+ ),
12911
+ tokensLimit: num(
12912
+ "x-ratelimit-limit-tokens",
12913
+ "anthropic-ratelimit-tokens-limit"
12914
+ ),
12915
+ tokensRemaining: num(
12916
+ "x-ratelimit-remaining-tokens",
12917
+ "anthropic-ratelimit-tokens-remaining"
12918
+ ),
12919
+ tokensResetAt: at(
12920
+ "x-ratelimit-reset-tokens",
12921
+ "anthropic-ratelimit-tokens-reset"
12922
+ ),
12923
+ observedAt: now
12924
+ };
12925
+ return s.requestsLimit === null && s.tokensLimit === null ? null : s;
12926
+ }
12927
+ function roomWaitMs(s, now = /* @__PURE__ */ new Date(), reserve = RESERVE) {
12928
+ const wait = (limit, remaining, resetAt) => {
12929
+ if (!limit || remaining === null || !resetAt) return 0;
12930
+ const need = reserve * limit;
12931
+ if (remaining >= need || resetAt <= now) return 0;
12932
+ const span = resetAt.getTime() - s.observedAt.getTime();
12933
+ const okAt = s.observedAt.getTime() + span * (need - remaining) / (limit - remaining);
12934
+ return Math.max(0, Math.ceil(okAt - now.getTime()));
12935
+ };
12936
+ return Math.max(
12937
+ wait(s.requestsLimit, s.requestsRemaining, s.requestsResetAt),
12938
+ wait(s.tokensLimit, s.tokensRemaining, s.tokensResetAt)
12939
+ );
12940
+ }
12941
+ async function awaitRoom(id, model) {
12942
+ for (; ; ) {
12943
+ const s = await loadRate(id, model).catch(() => null);
12944
+ const ms = s ? roomWaitMs(s) : 0;
12945
+ if (ms <= 0) return;
12946
+ await sleep2(Math.min(ms, MAX_NAP_MS) + Math.random() * 250);
12947
+ }
12948
+ }
12949
+ function noteRate(id, model, headers) {
12950
+ const s = headers ? parseRateHeaders(headers) : null;
12951
+ if (s) void saveRate(id, model, s).catch(() => {
12952
+ });
12953
+ }
12954
+ function rateFetch(apiKey, model, automatic) {
12955
+ const id = keyId(apiKey);
12956
+ return async (input, init2) => {
12957
+ if (automatic) await awaitRoom(id, model);
12958
+ const res = await fetch(input, init2);
12959
+ noteRate(id, model, res.headers);
12960
+ return res;
12961
+ };
12962
+ }
12963
+ var RESERVE, MAX_NAP_MS, sleep2, isAutomatic, keyId, UNIT_MS;
12964
+ var init_provider_rate2 = __esm({
12965
+ "packages/ingest/src/provider-rate.ts"() {
12966
+ "use strict";
12967
+ init_src2();
12968
+ RESERVE = 0.2;
12969
+ MAX_NAP_MS = 6e4;
12970
+ sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
12971
+ isAutomatic = (meta) => !meta?.actor && !meta?.waiting;
12972
+ keyId = (apiKey) => createHash2("sha256").update(apiKey).digest("hex").slice(0, 16);
12973
+ UNIT_MS = { ms: 1, s: 1e3, m: 6e4, h: 36e5 };
12974
+ }
12975
+ });
12976
+
12743
12977
  // packages/ingest/src/llm.ts
12744
12978
  function rationaleModelId(env = process.env) {
12745
12979
  return env.BRAINJAR_RATIONALE_MODEL?.trim() || "claude-opus-5";
12746
12980
  }
12747
12981
  function logUsage(meta, model, usage) {
12982
+ if (!meta?.brainId)
12983
+ console.warn(
12984
+ `brainjar: AI call without a Brain (workload ${meta?.workload ?? "other"}); it counts only toward the daily budget`
12985
+ );
12748
12986
  void recordLlmUsage({
12749
12987
  brainId: meta?.brainId ?? null,
12750
12988
  workload: meta?.workload ?? "other",
@@ -12770,6 +13008,7 @@ var init_llm = __esm({
12770
13008
  "use strict";
12771
13009
  init_sdk();
12772
13010
  init_src2();
13011
+ init_provider_rate2();
12773
13012
  cachedSystem = (system) => [
12774
13013
  {
12775
13014
  type: "text",
@@ -12782,9 +13021,12 @@ var init_llm = __esm({
12782
13021
  model;
12783
13022
  meta;
12784
13023
  constructor(opts) {
12785
- this.client = new Anthropic({ apiKey: opts.apiKey });
12786
13024
  this.model = opts.model ?? rationaleModelId();
12787
13025
  this.meta = opts.meta;
13026
+ this.client = new Anthropic({
13027
+ apiKey: opts.apiKey,
13028
+ fetch: rateFetch(opts.apiKey, this.model, isAutomatic(opts.meta))
13029
+ });
12788
13030
  }
12789
13031
  async complete({
12790
13032
  system,
@@ -12831,7 +13073,18 @@ var init_llm = __esm({
12831
13073
  var init_openai = __esm({
12832
13074
  "packages/ingest/src/openai.ts"() {
12833
13075
  "use strict";
13076
+ init_src();
12834
13077
  init_llm();
13078
+ init_provider_rate2();
13079
+ }
13080
+ });
13081
+
13082
+ // packages/ingest/src/allowance.ts
13083
+ var init_allowance = __esm({
13084
+ "packages/ingest/src/allowance.ts"() {
13085
+ "use strict";
13086
+ init_src2();
13087
+ init_src();
12835
13088
  }
12836
13089
  });
12837
13090
 
@@ -12841,6 +13094,7 @@ var init_routing = __esm({
12841
13094
  "use strict";
12842
13095
  init_src2();
12843
13096
  init_src();
13097
+ init_allowance();
12844
13098
  init_llm();
12845
13099
  init_openai();
12846
13100
  }
@@ -14394,6 +14648,8 @@ var init_summaries = __esm({
14394
14648
  "use strict";
14395
14649
  init_src2();
14396
14650
  init_src();
14651
+ init_routing();
14652
+ init_allowance();
14397
14653
  REPORT_SYSTEM = [
14398
14654
  "You write the period report of a software project for the people who work on it: what changed, why it matters, what was decided, what is still open.",
14399
14655
  "Input: the captured records inside the period, numbered, oldest first, each with its date, time, type and title. Record types: feature (behavior that was built or changed), decision (a recorded direction, NOT a delivery), failure (an approach that did not work), event (a commit or another recorded event), note (a note or follow-up).",
@@ -15094,7 +15350,7 @@ var init_doku_model = __esm({
15094
15350
  });
15095
15351
 
15096
15352
  // packages/ingest/src/doku.ts
15097
- import { createHash as createHash2 } from "node:crypto";
15353
+ import { createHash as createHash3 } from "node:crypto";
15098
15354
  import { mkdir as mkdir10, readFile as readFile10, rm as rm6, stat, writeFile as writeFile10 } from "node:fs/promises";
15099
15355
  import path15 from "node:path";
15100
15356
  function bodyLocations(o) {
@@ -16094,7 +16350,7 @@ var init_doku = __esm({
16094
16350
  DOKU_DIR = "brainjar-doku";
16095
16351
  MANIFEST = ".manifest.json";
16096
16352
  CHANGELOG_DAYS = 60;
16097
- sha1 = (s) => createHash2("sha1").update(s).digest("hex");
16353
+ sha1 = (s) => createHash3("sha1").update(s).digest("hex");
16098
16354
  exists = (p) => stat(p).then(
16099
16355
  () => true,
16100
16356
  () => false
@@ -16311,10 +16567,12 @@ var init_verify_agent = __esm({
16311
16567
  init_sdk();
16312
16568
  init_src2();
16313
16569
  init_src();
16570
+ init_allowance();
16314
16571
  init_product_facts();
16315
16572
  init_doku();
16316
16573
  init_llm();
16317
16574
  init_openai();
16575
+ init_provider_rate2();
16318
16576
  init_routing();
16319
16577
  VERIFY_SYSTEM = [
16320
16578
  "You are BrainJar's verification agent. You read a software repository and record durable product knowledge, with one iron rule: prose is never truth, code is.",
@@ -16363,17 +16621,40 @@ var init_verify_poll = __esm({
16363
16621
  }
16364
16622
  });
16365
16623
 
16624
+ // packages/ingest/src/scheduler.ts
16625
+ var init_scheduler = __esm({
16626
+ "packages/ingest/src/scheduler.ts"() {
16627
+ "use strict";
16628
+ init_src2();
16629
+ init_daily_log();
16630
+ init_summaries();
16631
+ }
16632
+ });
16633
+
16366
16634
  // packages/ingest/src/job-worker.ts
16367
16635
  var init_job_worker = __esm({
16368
16636
  "packages/ingest/src/job-worker.ts"() {
16369
16637
  "use strict";
16370
16638
  init_src2();
16371
16639
  init_deepscan();
16640
+ init_doku();
16641
+ init_scheduler();
16642
+ init_summaries();
16643
+ init_verify_poll();
16372
16644
  init_routing();
16645
+ init_allowance();
16373
16646
  init_verify_start();
16374
16647
  }
16375
16648
  });
16376
16649
 
16650
+ // packages/ingest/src/job-clock.ts
16651
+ var init_job_clock = __esm({
16652
+ "packages/ingest/src/job-clock.ts"() {
16653
+ "use strict";
16654
+ init_src2();
16655
+ }
16656
+ });
16657
+
16377
16658
  // packages/ingest/src/ask-agent-model.ts
16378
16659
  var init_ask_agent_model = __esm({
16379
16660
  "packages/ingest/src/ask-agent-model.ts"() {
@@ -16381,18 +16662,10 @@ var init_ask_agent_model = __esm({
16381
16662
  init_sdk();
16382
16663
  init_src2();
16383
16664
  init_src();
16665
+ init_allowance();
16384
16666
  init_llm();
16385
16667
  init_openai();
16386
- }
16387
- });
16388
-
16389
- // packages/ingest/src/scheduler.ts
16390
- var init_scheduler = __esm({
16391
- "packages/ingest/src/scheduler.ts"() {
16392
- "use strict";
16393
- init_src2();
16394
- init_daily_log();
16395
- init_summaries();
16668
+ init_provider_rate2();
16396
16669
  }
16397
16670
  });
16398
16671
 
@@ -16415,6 +16688,7 @@ var init_src3 = __esm({
16415
16688
  init_llm();
16416
16689
  init_openai();
16417
16690
  init_routing();
16691
+ init_allowance();
16418
16692
  init_rationale_extractor();
16419
16693
  init_extraction();
16420
16694
  init_entities();
@@ -16453,6 +16727,7 @@ var init_src3 = __esm({
16453
16727
  init_verify_poll();
16454
16728
  init_verify_start();
16455
16729
  init_job_worker();
16730
+ init_job_clock();
16456
16731
  init_ask_agent_model();
16457
16732
  init_scheduler();
16458
16733
  registerConnector(
@@ -16505,7 +16780,24 @@ async function captureApi(pathAndQuery, init2 = {}) {
16505
16780
  }
16506
16781
  return body;
16507
16782
  }
16508
- var TokenError, TOKEN_HELP;
16783
+ async function uploadArtifacts(brain, sourceId, artifacts, cursor3) {
16784
+ let inserted = 0;
16785
+ for (let i = 0; i < artifacts.length; i += UPLOAD_PORTION) {
16786
+ const last = i + UPLOAD_PORTION >= artifacts.length;
16787
+ const r = await captureApi("/capture/artifacts", {
16788
+ method: "POST",
16789
+ body: {
16790
+ brain,
16791
+ sourceId,
16792
+ artifacts: artifacts.slice(i, i + UPLOAD_PORTION),
16793
+ cursor: last ? cursor3 : null
16794
+ }
16795
+ });
16796
+ inserted += r.inserted;
16797
+ }
16798
+ return inserted;
16799
+ }
16800
+ var TokenError, TOKEN_HELP, UPLOAD_PORTION;
16509
16801
  var init_api_client = __esm({
16510
16802
  "packages/cli/src/api-client.ts"() {
16511
16803
  "use strict";
@@ -16513,6 +16805,7 @@ var init_api_client = __esm({
16513
16805
  TokenError = class extends Error {
16514
16806
  };
16515
16807
  TOKEN_HELP = "BrainJar token revoked or invalid. Create a new token under Account in the web app, then run `npx brainjar init --brain <id> --token <token>` once in any project on this machine.";
16808
+ UPLOAD_PORTION = 2e3;
16516
16809
  }
16517
16810
  });
16518
16811
 
@@ -16745,7 +17038,7 @@ var init_debt = __esm({
16745
17038
  });
16746
17039
 
16747
17040
  // packages/cli/src/adapters/claude-code.ts
16748
- import { createHash as createHash3 } from "node:crypto";
17041
+ import { createHash as createHash4 } from "node:crypto";
16749
17042
  function targetOf(input) {
16750
17043
  const i = input ?? {};
16751
17044
  for (const k of ["command", "file_path", "notebook_path"]) {
@@ -16790,7 +17083,7 @@ var init_claude_code = __esm({
16790
17083
  "use strict";
16791
17084
  init_src();
16792
17085
  EDIT_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
16793
- short2 = (s) => createHash3("sha256").update(s).digest("hex").slice(0, 16);
17086
+ short2 = (s) => createHash4("sha256").update(s).digest("hex").slice(0, 16);
16794
17087
  claudeCode2 = {
16795
17088
  id: "claude-code",
16796
17089
  map(payload, now) {
@@ -16889,7 +17182,7 @@ var init_claude_code = __esm({
16889
17182
  });
16890
17183
 
16891
17184
  // packages/cli/src/adapters/cursor.ts
16892
- import { createHash as createHash4 } from "node:crypto";
17185
+ import { createHash as createHash5 } from "node:crypto";
16893
17186
  function targetOf2(input) {
16894
17187
  const i = input ?? {};
16895
17188
  for (const k of ["command", "file_path", "path", "target_file"]) {
@@ -16910,7 +17203,7 @@ var init_cursor = __esm({
16910
17203
  "packages/cli/src/adapters/cursor.ts"() {
16911
17204
  "use strict";
16912
17205
  init_src();
16913
- short3 = (s) => createHash4("sha256").update(s).digest("hex").slice(0, 16);
17206
+ short3 = (s) => createHash5("sha256").update(s).digest("hex").slice(0, 16);
16914
17207
  cursor2 = {
16915
17208
  id: "cursor",
16916
17209
  map(payload, now) {
@@ -17028,7 +17321,7 @@ var init_cursor = __esm({
17028
17321
  });
17029
17322
 
17030
17323
  // packages/cli/src/adapters/codex.ts
17031
- import { createHash as createHash5 } from "node:crypto";
17324
+ import { createHash as createHash6 } from "node:crypto";
17032
17325
  function patchFiles(patch) {
17033
17326
  const out = /* @__PURE__ */ new Set();
17034
17327
  for (const m of patch.matchAll(
@@ -17064,7 +17357,7 @@ var init_codex = __esm({
17064
17357
  "use strict";
17065
17358
  init_src();
17066
17359
  PATCH_TOOLS = /* @__PURE__ */ new Set(["apply_patch", "Edit", "Write"]);
17067
- short4 = (s) => createHash5("sha256").update(s).digest("hex").slice(0, 16);
17360
+ short4 = (s) => createHash6("sha256").update(s).digest("hex").slice(0, 16);
17068
17361
  codex2 = {
17069
17362
  id: "codex",
17070
17363
  map(payload, now) {
@@ -17161,7 +17454,7 @@ var init_codex = __esm({
17161
17454
  });
17162
17455
 
17163
17456
  // packages/cli/src/adapters/copilot.ts
17164
- import { createHash as createHash6 } from "node:crypto";
17457
+ import { createHash as createHash7 } from "node:crypto";
17165
17458
  function targetOf3(args) {
17166
17459
  if (!args || typeof args !== "object") return "";
17167
17460
  const a = args;
@@ -17194,7 +17487,7 @@ var init_copilot = __esm({
17194
17487
  "use strict";
17195
17488
  init_src();
17196
17489
  EDIT_TOOLS2 = /* @__PURE__ */ new Set(["edit", "create"]);
17197
- short5 = (s) => createHash6("sha256").update(s).digest("hex").slice(0, 16);
17490
+ short5 = (s) => createHash7("sha256").update(s).digest("hex").slice(0, 16);
17198
17491
  EVENT_ALIASES = {
17199
17492
  SessionStart: "sessionStart",
17200
17493
  SessionEnd: "sessionEnd",
@@ -17303,7 +17596,7 @@ var init_copilot = __esm({
17303
17596
  });
17304
17597
 
17305
17598
  // packages/cli/src/adapters/gemini.ts
17306
- import { createHash as createHash7 } from "node:crypto";
17599
+ import { createHash as createHash8 } from "node:crypto";
17307
17600
  function targetOf4(input) {
17308
17601
  const i = input ?? {};
17309
17602
  for (const k of ["command", "file_path"]) {
@@ -17325,7 +17618,7 @@ var init_gemini = __esm({
17325
17618
  "use strict";
17326
17619
  init_src();
17327
17620
  EDIT_TOOLS3 = /* @__PURE__ */ new Set(["write_file", "replace"]);
17328
- short6 = (s) => createHash7("sha256").update(s).digest("hex").slice(0, 16);
17621
+ short6 = (s) => createHash8("sha256").update(s).digest("hex").slice(0, 16);
17329
17622
  gemini2 = {
17330
17623
  id: "gemini",
17331
17624
  map(payload, now) {
@@ -17421,7 +17714,7 @@ var init_gemini = __esm({
17421
17714
  });
17422
17715
 
17423
17716
  // packages/cli/src/adapters/kiro.ts
17424
- import { createHash as createHash8 } from "node:crypto";
17717
+ import { createHash as createHash9 } from "node:crypto";
17425
17718
  function targetOf5(input) {
17426
17719
  const i = input ?? {};
17427
17720
  for (const k of ["command", "cmd", "path", "file_path", "filePath"]) {
@@ -17452,7 +17745,7 @@ var init_kiro = __esm({
17452
17745
  "use strict";
17453
17746
  init_src();
17454
17747
  EDIT_TOOLS4 = /* @__PURE__ */ new Set(["fs_write", "write", "str_replace", "fs_append"]);
17455
- short7 = (s) => createHash8("sha256").update(s).digest("hex").slice(0, 16);
17748
+ short7 = (s) => createHash9("sha256").update(s).digest("hex").slice(0, 16);
17456
17749
  EVENTS5 = {
17457
17750
  agentSpawn: "start",
17458
17751
  sessionStart: "start",
@@ -17568,7 +17861,7 @@ var init_kiro = __esm({
17568
17861
  });
17569
17862
 
17570
17863
  // packages/cli/src/adapters/opencode.ts
17571
- import { createHash as createHash9 } from "node:crypto";
17864
+ import { createHash as createHash10 } from "node:crypto";
17572
17865
  function commitShas7(text) {
17573
17866
  const out = /* @__PURE__ */ new Set();
17574
17867
  for (const m of (text ?? "").matchAll(/\[[^\]\n]* ([0-9a-f]{7,40})\]/g)) {
@@ -17582,7 +17875,7 @@ var init_opencode = __esm({
17582
17875
  "use strict";
17583
17876
  init_src();
17584
17877
  EDIT_TOOLS5 = /* @__PURE__ */ new Set(["edit", "write"]);
17585
- short8 = (s) => createHash9("sha256").update(s).digest("hex").slice(0, 16);
17878
+ short8 = (s) => createHash10("sha256").update(s).digest("hex").slice(0, 16);
17586
17879
  opencode2 = {
17587
17880
  id: "opencode",
17588
17881
  map(payload, now) {
@@ -17682,7 +17975,7 @@ var init_opencode = __esm({
17682
17975
  });
17683
17976
 
17684
17977
  // packages/cli/src/adapters/cline.ts
17685
- import { createHash as createHash10 } from "node:crypto";
17978
+ import { createHash as createHash11 } from "node:crypto";
17686
17979
  function targetOf6(params) {
17687
17980
  const i = params ?? {};
17688
17981
  for (const k of ["command", "path"]) {
@@ -17714,7 +18007,7 @@ var init_cline = __esm({
17714
18007
  "use strict";
17715
18008
  init_src();
17716
18009
  EDIT_TOOLS6 = /* @__PURE__ */ new Set(["write_to_file", "replace_in_file", "apply_patch"]);
17717
- short9 = (s) => createHash10("sha256").update(s).digest("hex").slice(0, 16);
18010
+ short9 = (s) => createHash11("sha256").update(s).digest("hex").slice(0, 16);
17718
18011
  CLINE_REPLY = JSON.stringify({ cancel: false });
17719
18012
  cline2 = {
17720
18013
  id: "cline",
@@ -18016,18 +18309,15 @@ async function syncRepo(root) {
18016
18309
  const connector = createConnector("git_local", { cwd: root }, {});
18017
18310
  const artifacts = await connector.sync(project.cursor ?? void 0);
18018
18311
  if (artifacts.length > 0) {
18019
- const r = await captureApi("/capture/artifacts", {
18020
- method: "POST",
18021
- body: {
18022
- brain: link.brainId,
18023
- sourceId: project.sourceId,
18024
- artifacts,
18025
- cursor: maxUpdatedAt(artifacts) ?? project.cursor
18026
- }
18027
- });
18028
- if (r.inserted > 0) {
18312
+ const inserted = await uploadArtifacts(
18313
+ link.brainId,
18314
+ project.sourceId,
18315
+ artifacts,
18316
+ maxUpdatedAt(artifacts) ?? project.cursor
18317
+ );
18318
+ if (inserted > 0) {
18029
18319
  console.error(
18030
- `brainjar: ${r.inserted} new commits sent, the server reads them in the background`
18320
+ `brainjar: ${inserted} new commits sent, the server reads them in the background`
18031
18321
  );
18032
18322
  const graph = await scanStructure(root);
18033
18323
  await captureApi("/capture/structure", {
@@ -18095,7 +18385,7 @@ async function waitForServer(brainId, since, opts = {}) {
18095
18385
  const poll = opts.poll ?? (() => captureApi(
18096
18386
  `/capture/progress?brain=${brainId}&since=${encodeURIComponent(since.toISOString())}`
18097
18387
  ));
18098
- const sleep2 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
18388
+ const sleep3 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
18099
18389
  const write = opts.write ?? ((s) => void process.stdout.write(s));
18100
18390
  const tty = opts.tty ?? Boolean(process.stdout.isTTY);
18101
18391
  const intervalMs = opts.intervalMs ?? 2e3;
@@ -18112,7 +18402,7 @@ async function waitForServer(brainId, since, opts = {}) {
18112
18402
  if (tty && shown) write("\n");
18113
18403
  return null;
18114
18404
  }
18115
- await sleep2(intervalMs);
18405
+ await sleep3(intervalMs);
18116
18406
  continue;
18117
18407
  }
18118
18408
  if (p.queued + p.running === 0) {
@@ -18127,7 +18417,7 @@ async function waitForServer(brainId, since, opts = {}) {
18127
18417
  shownAt = Date.now();
18128
18418
  }
18129
18419
  shown = line;
18130
- await sleep2(intervalMs);
18420
+ await sleep3(intervalMs);
18131
18421
  }
18132
18422
  }
18133
18423
  var n;
@@ -18213,16 +18503,12 @@ async function init(argv) {
18213
18503
  const artifacts = await connector.sync(conn.cursor ?? void 0);
18214
18504
  let commits = 0;
18215
18505
  if (artifacts.length > 0) {
18216
- const r = await captureApi("/capture/artifacts", {
18217
- method: "POST",
18218
- body: {
18219
- brain: conn.brainId,
18220
- sourceId: conn.sourceId,
18221
- artifacts,
18222
- cursor: maxUpdatedAt(artifacts) ?? conn.cursor
18223
- }
18224
- });
18225
- commits = r.inserted;
18506
+ commits = await uploadArtifacts(
18507
+ conn.brainId,
18508
+ conn.sourceId,
18509
+ artifacts,
18510
+ maxUpdatedAt(artifacts) ?? conn.cursor
18511
+ );
18226
18512
  }
18227
18513
  ok(
18228
18514
  `${graph.modules.length} modules, ${commits} new commits sent to the server`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brainjar",
3
- "version": "0.3.975",
3
+ "version": "0.3.976",
4
4
  "description": "BrainJar CLI: turn a repository into a live, self-documenting Brain.",
5
5
  "type": "module",
6
6
  "bin": {