brainjar 0.3.975 → 0.3.977
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/brainjar.js +818 -155
- 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.
|
|
110
|
+
CLI_VERSION = "0.3.977";
|
|
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,24 @@ 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
|
+
// Released 2026-09-22 (official pricing page, checked 2026-09-25).
|
|
5931
|
+
"gpt-6-luna": { input: 0.1, cachedInput: 0.01, output: 0.5 }
|
|
5932
|
+
};
|
|
5914
5933
|
DEFAULT_TENANT_ID = "00000000-0000-0000-0000-000000000002";
|
|
5915
5934
|
SECRETS = {
|
|
5916
5935
|
anthropicApiKey: {
|
|
@@ -6016,13 +6035,6 @@ var init_config = __esm({
|
|
|
6016
6035
|
}
|
|
6017
6036
|
});
|
|
6018
6037
|
|
|
6019
|
-
// packages/shared/src/ratelimit.ts
|
|
6020
|
-
var init_ratelimit = __esm({
|
|
6021
|
-
"packages/shared/src/ratelimit.ts"() {
|
|
6022
|
-
"use strict";
|
|
6023
|
-
}
|
|
6024
|
-
});
|
|
6025
|
-
|
|
6026
6038
|
// packages/shared/src/sanitize.ts
|
|
6027
6039
|
var init_sanitize = __esm({
|
|
6028
6040
|
"packages/shared/src/sanitize.ts"() {
|
|
@@ -6073,7 +6085,7 @@ function validateFlowDiagram(d) {
|
|
|
6073
6085
|
if (n2.evidence.length > 12)
|
|
6074
6086
|
errs.push(`nodes: ${n2.id} too many evidence entries`);
|
|
6075
6087
|
for (const ev of n2.evidence)
|
|
6076
|
-
if (!short(ev, 200) || /[
|
|
6088
|
+
if (!short(ev, 200) || /[\x00-\x1f]/.test(ev))
|
|
6077
6089
|
errs.push(`nodes: ${n2.id} evidence entry is not a short string`);
|
|
6078
6090
|
}
|
|
6079
6091
|
if (n2.description !== void 0 && !short(n2.description, 600))
|
|
@@ -6216,7 +6228,6 @@ var init_src = __esm({
|
|
|
6216
6228
|
init_memory();
|
|
6217
6229
|
init_confidence();
|
|
6218
6230
|
init_config();
|
|
6219
|
-
init_ratelimit();
|
|
6220
6231
|
init_sanitize();
|
|
6221
6232
|
init_flow();
|
|
6222
6233
|
init_capture_events();
|
|
@@ -6238,8 +6249,11 @@ function getPool() {
|
|
|
6238
6249
|
}
|
|
6239
6250
|
pool = new esm_default.Pool({
|
|
6240
6251
|
connectionString: config.databaseUrl,
|
|
6252
|
+
// Per process (BJ-137): many instances times the default would fill Postgres; each role
|
|
6253
|
+
// sets its own in the compose file. PgBouncer in front is the cloud ticket's (BJ-140).
|
|
6254
|
+
max: Number(process.env.DATABASE_POOL_MAX) || 10,
|
|
6241
6255
|
// Session GUC set in the startup packet — race-free, applies before any query.
|
|
6242
|
-
options: `-c app.tenant_id=${config.tenantId}`
|
|
6256
|
+
options: `-c app.tenant_id=${config.tenantId} -c hnsw.iterative_scan=strict_order`
|
|
6243
6257
|
});
|
|
6244
6258
|
pool.on("error", () => {
|
|
6245
6259
|
});
|
|
@@ -7599,7 +7613,7 @@ var init_repository = __esm({
|
|
|
7599
7613
|
});
|
|
7600
7614
|
|
|
7601
7615
|
// packages/db/src/ratelimit.ts
|
|
7602
|
-
var
|
|
7616
|
+
var init_ratelimit = __esm({
|
|
7603
7617
|
"packages/db/src/ratelimit.ts"() {
|
|
7604
7618
|
"use strict";
|
|
7605
7619
|
init_pool();
|
|
@@ -7629,7 +7643,7 @@ var init_auth = __esm({
|
|
|
7629
7643
|
"use strict";
|
|
7630
7644
|
init_src();
|
|
7631
7645
|
init_pool();
|
|
7632
|
-
|
|
7646
|
+
init_ratelimit();
|
|
7633
7647
|
init_auth_event();
|
|
7634
7648
|
init_email_token();
|
|
7635
7649
|
DEFAULT_TTL_HOURS = 24 * 90;
|
|
@@ -7776,7 +7790,22 @@ var init_settings = __esm({
|
|
|
7776
7790
|
// Voice chat (voice handoff, 2026-09-21): an explicit "true" or "false"; absent means on.
|
|
7777
7791
|
"voice_enabled",
|
|
7778
7792
|
// The GPT-Live voice (voice picker, 2026-09-22): an id from VOICE_OPTIONS; absent = default.
|
|
7779
|
-
"voice_name"
|
|
7793
|
+
"voice_name",
|
|
7794
|
+
// AI cost cap (BJ-139), set on the Operator page; env overrides, defaults in ingest/allowance.
|
|
7795
|
+
"ai_contingent",
|
|
7796
|
+
// "on" | "off": monthly contingent per workspace (default on in SaaS)
|
|
7797
|
+
"ai_free_monthly_usd",
|
|
7798
|
+
// Free workspace, USD per month
|
|
7799
|
+
"ai_team_seat_monthly_usd",
|
|
7800
|
+
// Team, USD per seat and month (applies once licenses exist)
|
|
7801
|
+
"ai_operator_exempt",
|
|
7802
|
+
// "true" | "false": operator accounts are never capped
|
|
7803
|
+
"ai_daily_budget_usd",
|
|
7804
|
+
// the instance's emergency brake on the company key; empty = off
|
|
7805
|
+
"ai_budget_released_day",
|
|
7806
|
+
// YYYY-MM-DD the operator lifted today's brake
|
|
7807
|
+
"ai_budget_alerted_day"
|
|
7808
|
+
// YYYY-MM-DD the operator was told the brake tripped
|
|
7780
7809
|
];
|
|
7781
7810
|
PROVIDER_KEY_SETTINGS = {
|
|
7782
7811
|
anthropic: "anthropic_api_key",
|
|
@@ -7810,10 +7839,102 @@ var init_tickets = __esm({
|
|
|
7810
7839
|
});
|
|
7811
7840
|
|
|
7812
7841
|
// packages/db/src/jobs.ts
|
|
7842
|
+
var CAPTURE_JOB_KINDS, USER_JOB_KINDS;
|
|
7813
7843
|
var init_jobs = __esm({
|
|
7814
7844
|
"packages/db/src/jobs.ts"() {
|
|
7815
7845
|
"use strict";
|
|
7816
7846
|
init_pool();
|
|
7847
|
+
CAPTURE_JOB_KINDS = [
|
|
7848
|
+
"capture-extract",
|
|
7849
|
+
"capture-session",
|
|
7850
|
+
"capture-transcript",
|
|
7851
|
+
"capture-structure"
|
|
7852
|
+
];
|
|
7853
|
+
USER_JOB_KINDS = [
|
|
7854
|
+
...CAPTURE_JOB_KINDS,
|
|
7855
|
+
"deepscan",
|
|
7856
|
+
"verify",
|
|
7857
|
+
"summary-report",
|
|
7858
|
+
"resynthesize"
|
|
7859
|
+
];
|
|
7860
|
+
}
|
|
7861
|
+
});
|
|
7862
|
+
|
|
7863
|
+
// packages/db/src/spend.ts
|
|
7864
|
+
function dayKey(at = /* @__PURE__ */ new Date()) {
|
|
7865
|
+
return `day:${at.toISOString().slice(0, 10)}`;
|
|
7866
|
+
}
|
|
7867
|
+
async function addSpend(brainId, usd, at = /* @__PURE__ */ new Date(), pool2 = getPool()) {
|
|
7868
|
+
if (!(usd > 0)) return;
|
|
7869
|
+
await pool2.query(
|
|
7870
|
+
`INSERT INTO llm_spend (key, usd)
|
|
7871
|
+
SELECT k, $2 FROM (
|
|
7872
|
+
SELECT $3::text AS k
|
|
7873
|
+
UNION ALL
|
|
7874
|
+
SELECT 'ws:' || b.owner_id || ':' || $4 FROM brain b
|
|
7875
|
+
WHERE b.id = $1 AND b.owner_id IS NOT NULL
|
|
7876
|
+
) keys
|
|
7877
|
+
ON CONFLICT (key) DO UPDATE SET usd = llm_spend.usd + EXCLUDED.usd, updated_at = now()`,
|
|
7878
|
+
[brainId, usd, dayKey(at), at.toISOString().slice(0, 7)]
|
|
7879
|
+
);
|
|
7880
|
+
}
|
|
7881
|
+
var init_spend = __esm({
|
|
7882
|
+
"packages/db/src/spend.ts"() {
|
|
7883
|
+
"use strict";
|
|
7884
|
+
init_pool();
|
|
7885
|
+
}
|
|
7886
|
+
});
|
|
7887
|
+
|
|
7888
|
+
// packages/db/src/provider-rate.ts
|
|
7889
|
+
async function saveRate(keyId2, model, s, pool2 = getPool()) {
|
|
7890
|
+
await pool2.query(
|
|
7891
|
+
`INSERT INTO llm_rate (key_id, model, requests_limit, requests_remaining, requests_reset_at,
|
|
7892
|
+
tokens_limit, tokens_remaining, tokens_reset_at, observed_at)
|
|
7893
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
|
7894
|
+
ON CONFLICT (key_id, model) DO UPDATE SET
|
|
7895
|
+
requests_limit = EXCLUDED.requests_limit,
|
|
7896
|
+
requests_remaining = EXCLUDED.requests_remaining,
|
|
7897
|
+
requests_reset_at = EXCLUDED.requests_reset_at,
|
|
7898
|
+
tokens_limit = EXCLUDED.tokens_limit,
|
|
7899
|
+
tokens_remaining = EXCLUDED.tokens_remaining,
|
|
7900
|
+
tokens_reset_at = EXCLUDED.tokens_reset_at,
|
|
7901
|
+
observed_at = EXCLUDED.observed_at
|
|
7902
|
+
WHERE llm_rate.observed_at <= EXCLUDED.observed_at`,
|
|
7903
|
+
[
|
|
7904
|
+
keyId2,
|
|
7905
|
+
model,
|
|
7906
|
+
s.requestsLimit,
|
|
7907
|
+
s.requestsRemaining,
|
|
7908
|
+
s.requestsResetAt,
|
|
7909
|
+
s.tokensLimit,
|
|
7910
|
+
s.tokensRemaining,
|
|
7911
|
+
s.tokensResetAt,
|
|
7912
|
+
s.observedAt
|
|
7913
|
+
]
|
|
7914
|
+
);
|
|
7915
|
+
}
|
|
7916
|
+
async function loadRate(keyId2, model, pool2 = getPool()) {
|
|
7917
|
+
const r = await pool2.query(
|
|
7918
|
+
"SELECT * FROM llm_rate WHERE key_id = $1 AND model = $2",
|
|
7919
|
+
[keyId2, model]
|
|
7920
|
+
);
|
|
7921
|
+
const x = r.rows[0];
|
|
7922
|
+
if (!x) return null;
|
|
7923
|
+
const n2 = (v) => v === null ? null : Number(v);
|
|
7924
|
+
return {
|
|
7925
|
+
requestsLimit: n2(x.requests_limit),
|
|
7926
|
+
requestsRemaining: n2(x.requests_remaining),
|
|
7927
|
+
requestsResetAt: x.requests_reset_at,
|
|
7928
|
+
tokensLimit: n2(x.tokens_limit),
|
|
7929
|
+
tokensRemaining: n2(x.tokens_remaining),
|
|
7930
|
+
tokensResetAt: x.tokens_reset_at,
|
|
7931
|
+
observedAt: x.observed_at
|
|
7932
|
+
};
|
|
7933
|
+
}
|
|
7934
|
+
var init_provider_rate = __esm({
|
|
7935
|
+
"packages/db/src/provider-rate.ts"() {
|
|
7936
|
+
"use strict";
|
|
7937
|
+
init_pool();
|
|
7817
7938
|
}
|
|
7818
7939
|
});
|
|
7819
7940
|
|
|
@@ -7841,11 +7962,21 @@ async function recordLlmUsage(u) {
|
|
|
7841
7962
|
Math.max(0, Math.round(u.outputTokens))
|
|
7842
7963
|
]
|
|
7843
7964
|
);
|
|
7965
|
+
await addSpend(
|
|
7966
|
+
u.brainId ?? null,
|
|
7967
|
+
estimateCostUsd(u.model, {
|
|
7968
|
+
input: u.inputTokens,
|
|
7969
|
+
cachedInput: u.cachedInputTokens,
|
|
7970
|
+
output: u.outputTokens
|
|
7971
|
+
})
|
|
7972
|
+
);
|
|
7844
7973
|
}
|
|
7845
7974
|
var init_usage = __esm({
|
|
7846
7975
|
"packages/db/src/usage.ts"() {
|
|
7847
7976
|
"use strict";
|
|
7977
|
+
init_src();
|
|
7848
7978
|
init_pool();
|
|
7979
|
+
init_spend();
|
|
7849
7980
|
}
|
|
7850
7981
|
});
|
|
7851
7982
|
|
|
@@ -7859,7 +7990,7 @@ var init_src2 = __esm({
|
|
|
7859
7990
|
init_auth();
|
|
7860
7991
|
init_api_token();
|
|
7861
7992
|
init_oauth();
|
|
7862
|
-
|
|
7993
|
+
init_ratelimit();
|
|
7863
7994
|
init_auth_event();
|
|
7864
7995
|
init_email_token();
|
|
7865
7996
|
init_totp2();
|
|
@@ -7872,6 +8003,8 @@ var init_src2 = __esm({
|
|
|
7872
8003
|
init_conversations();
|
|
7873
8004
|
init_tickets();
|
|
7874
8005
|
init_jobs();
|
|
8006
|
+
init_spend();
|
|
8007
|
+
init_provider_rate();
|
|
7875
8008
|
init_ruleset();
|
|
7876
8009
|
init_usage();
|
|
7877
8010
|
init_audit_origin();
|
|
@@ -12740,11 +12873,118 @@ var init_sdk = __esm({
|
|
|
12740
12873
|
}
|
|
12741
12874
|
});
|
|
12742
12875
|
|
|
12876
|
+
// packages/ingest/src/provider-rate.ts
|
|
12877
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
12878
|
+
function parseDuration(s) {
|
|
12879
|
+
if (!s) return null;
|
|
12880
|
+
let ms = 0;
|
|
12881
|
+
let hit = false;
|
|
12882
|
+
for (const [, n2, unit] of s.matchAll(/(\d+(?:\.\d+)?)(ms|h|m|s)/g)) {
|
|
12883
|
+
hit = true;
|
|
12884
|
+
ms += Number(n2) * UNIT_MS[unit];
|
|
12885
|
+
}
|
|
12886
|
+
return hit ? ms : null;
|
|
12887
|
+
}
|
|
12888
|
+
function parseRateHeaders(h, now = /* @__PURE__ */ new Date()) {
|
|
12889
|
+
const num = (a, b) => {
|
|
12890
|
+
const v = h.get(a) ?? h.get(b);
|
|
12891
|
+
const n2 = v === null ? NaN : Number(v);
|
|
12892
|
+
return Number.isFinite(n2) ? n2 : null;
|
|
12893
|
+
};
|
|
12894
|
+
const at = (openai, anthropic) => {
|
|
12895
|
+
const d = parseDuration(h.get(openai));
|
|
12896
|
+
if (d !== null) return new Date(now.getTime() + d);
|
|
12897
|
+
const t = Date.parse(h.get(anthropic) ?? "");
|
|
12898
|
+
return Number.isFinite(t) ? new Date(t) : null;
|
|
12899
|
+
};
|
|
12900
|
+
const s = {
|
|
12901
|
+
requestsLimit: num(
|
|
12902
|
+
"x-ratelimit-limit-requests",
|
|
12903
|
+
"anthropic-ratelimit-requests-limit"
|
|
12904
|
+
),
|
|
12905
|
+
requestsRemaining: num(
|
|
12906
|
+
"x-ratelimit-remaining-requests",
|
|
12907
|
+
"anthropic-ratelimit-requests-remaining"
|
|
12908
|
+
),
|
|
12909
|
+
requestsResetAt: at(
|
|
12910
|
+
"x-ratelimit-reset-requests",
|
|
12911
|
+
"anthropic-ratelimit-requests-reset"
|
|
12912
|
+
),
|
|
12913
|
+
tokensLimit: num(
|
|
12914
|
+
"x-ratelimit-limit-tokens",
|
|
12915
|
+
"anthropic-ratelimit-tokens-limit"
|
|
12916
|
+
),
|
|
12917
|
+
tokensRemaining: num(
|
|
12918
|
+
"x-ratelimit-remaining-tokens",
|
|
12919
|
+
"anthropic-ratelimit-tokens-remaining"
|
|
12920
|
+
),
|
|
12921
|
+
tokensResetAt: at(
|
|
12922
|
+
"x-ratelimit-reset-tokens",
|
|
12923
|
+
"anthropic-ratelimit-tokens-reset"
|
|
12924
|
+
),
|
|
12925
|
+
observedAt: now
|
|
12926
|
+
};
|
|
12927
|
+
return s.requestsLimit === null && s.tokensLimit === null ? null : s;
|
|
12928
|
+
}
|
|
12929
|
+
function roomWaitMs(s, now = /* @__PURE__ */ new Date(), reserve = RESERVE) {
|
|
12930
|
+
const wait = (limit, remaining, resetAt) => {
|
|
12931
|
+
if (!limit || remaining === null || !resetAt) return 0;
|
|
12932
|
+
const need = reserve * limit;
|
|
12933
|
+
if (remaining >= need || resetAt <= now) return 0;
|
|
12934
|
+
const span = resetAt.getTime() - s.observedAt.getTime();
|
|
12935
|
+
const okAt = s.observedAt.getTime() + span * (need - remaining) / (limit - remaining);
|
|
12936
|
+
return Math.max(0, Math.ceil(okAt - now.getTime()));
|
|
12937
|
+
};
|
|
12938
|
+
return Math.max(
|
|
12939
|
+
wait(s.requestsLimit, s.requestsRemaining, s.requestsResetAt),
|
|
12940
|
+
wait(s.tokensLimit, s.tokensRemaining, s.tokensResetAt)
|
|
12941
|
+
);
|
|
12942
|
+
}
|
|
12943
|
+
async function awaitRoom(id, model) {
|
|
12944
|
+
for (; ; ) {
|
|
12945
|
+
const s = await loadRate(id, model).catch(() => null);
|
|
12946
|
+
const ms = s ? roomWaitMs(s) : 0;
|
|
12947
|
+
if (ms <= 0) return;
|
|
12948
|
+
await sleep2(Math.min(ms, MAX_NAP_MS) + Math.random() * 250);
|
|
12949
|
+
}
|
|
12950
|
+
}
|
|
12951
|
+
function noteRate(id, model, headers) {
|
|
12952
|
+
const s = headers ? parseRateHeaders(headers) : null;
|
|
12953
|
+
if (s) void saveRate(id, model, s).catch(() => {
|
|
12954
|
+
});
|
|
12955
|
+
}
|
|
12956
|
+
function rateFetch(apiKey, model, automatic) {
|
|
12957
|
+
const id = keyId(apiKey);
|
|
12958
|
+
return async (input, init2) => {
|
|
12959
|
+
if (automatic) await awaitRoom(id, model);
|
|
12960
|
+
const res = await fetch(input, init2);
|
|
12961
|
+
noteRate(id, model, res.headers);
|
|
12962
|
+
return res;
|
|
12963
|
+
};
|
|
12964
|
+
}
|
|
12965
|
+
var RESERVE, MAX_NAP_MS, sleep2, isAutomatic, keyId, UNIT_MS;
|
|
12966
|
+
var init_provider_rate2 = __esm({
|
|
12967
|
+
"packages/ingest/src/provider-rate.ts"() {
|
|
12968
|
+
"use strict";
|
|
12969
|
+
init_src2();
|
|
12970
|
+
RESERVE = 0.2;
|
|
12971
|
+
MAX_NAP_MS = 6e4;
|
|
12972
|
+
sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
12973
|
+
isAutomatic = (meta) => !meta?.actor && !meta?.waiting;
|
|
12974
|
+
keyId = (apiKey) => createHash2("sha256").update(apiKey).digest("hex").slice(0, 16);
|
|
12975
|
+
UNIT_MS = { ms: 1, s: 1e3, m: 6e4, h: 36e5 };
|
|
12976
|
+
}
|
|
12977
|
+
});
|
|
12978
|
+
|
|
12743
12979
|
// packages/ingest/src/llm.ts
|
|
12744
12980
|
function rationaleModelId(env = process.env) {
|
|
12745
12981
|
return env.BRAINJAR_RATIONALE_MODEL?.trim() || "claude-opus-5";
|
|
12746
12982
|
}
|
|
12747
12983
|
function logUsage(meta, model, usage) {
|
|
12984
|
+
if (!meta?.brainId)
|
|
12985
|
+
console.warn(
|
|
12986
|
+
`brainjar: AI call without a Brain (workload ${meta?.workload ?? "other"}); it counts only toward the daily budget`
|
|
12987
|
+
);
|
|
12748
12988
|
void recordLlmUsage({
|
|
12749
12989
|
brainId: meta?.brainId ?? null,
|
|
12750
12990
|
workload: meta?.workload ?? "other",
|
|
@@ -12770,6 +13010,7 @@ var init_llm = __esm({
|
|
|
12770
13010
|
"use strict";
|
|
12771
13011
|
init_sdk();
|
|
12772
13012
|
init_src2();
|
|
13013
|
+
init_provider_rate2();
|
|
12773
13014
|
cachedSystem = (system) => [
|
|
12774
13015
|
{
|
|
12775
13016
|
type: "text",
|
|
@@ -12782,9 +13023,12 @@ var init_llm = __esm({
|
|
|
12782
13023
|
model;
|
|
12783
13024
|
meta;
|
|
12784
13025
|
constructor(opts) {
|
|
12785
|
-
this.client = new Anthropic({ apiKey: opts.apiKey });
|
|
12786
13026
|
this.model = opts.model ?? rationaleModelId();
|
|
12787
13027
|
this.meta = opts.meta;
|
|
13028
|
+
this.client = new Anthropic({
|
|
13029
|
+
apiKey: opts.apiKey,
|
|
13030
|
+
fetch: rateFetch(opts.apiKey, this.model, isAutomatic(opts.meta))
|
|
13031
|
+
});
|
|
12788
13032
|
}
|
|
12789
13033
|
async complete({
|
|
12790
13034
|
system,
|
|
@@ -12831,7 +13075,18 @@ var init_llm = __esm({
|
|
|
12831
13075
|
var init_openai = __esm({
|
|
12832
13076
|
"packages/ingest/src/openai.ts"() {
|
|
12833
13077
|
"use strict";
|
|
13078
|
+
init_src();
|
|
12834
13079
|
init_llm();
|
|
13080
|
+
init_provider_rate2();
|
|
13081
|
+
}
|
|
13082
|
+
});
|
|
13083
|
+
|
|
13084
|
+
// packages/ingest/src/allowance.ts
|
|
13085
|
+
var init_allowance = __esm({
|
|
13086
|
+
"packages/ingest/src/allowance.ts"() {
|
|
13087
|
+
"use strict";
|
|
13088
|
+
init_src2();
|
|
13089
|
+
init_src();
|
|
12835
13090
|
}
|
|
12836
13091
|
});
|
|
12837
13092
|
|
|
@@ -12841,6 +13096,7 @@ var init_routing = __esm({
|
|
|
12841
13096
|
"use strict";
|
|
12842
13097
|
init_src2();
|
|
12843
13098
|
init_src();
|
|
13099
|
+
init_allowance();
|
|
12844
13100
|
init_llm();
|
|
12845
13101
|
init_openai();
|
|
12846
13102
|
}
|
|
@@ -12928,42 +13184,280 @@ function resolveSpecifier(fromFile, spec, known) {
|
|
|
12928
13184
|
for (const probe of [
|
|
12929
13185
|
clean,
|
|
12930
13186
|
c,
|
|
12931
|
-
...
|
|
12932
|
-
...
|
|
13187
|
+
...JS_EXTS.map((e) => clean + e),
|
|
13188
|
+
...JS_EXTS.map((e) => `${clean}/index${e}`)
|
|
12933
13189
|
]) {
|
|
12934
13190
|
if (known.has(probe)) return probe;
|
|
12935
13191
|
}
|
|
12936
13192
|
}
|
|
12937
13193
|
return null;
|
|
12938
13194
|
}
|
|
13195
|
+
function resolveBySuffix(segments, exts, fromFile, idx, inits = []) {
|
|
13196
|
+
if (segments.length === 0) return null;
|
|
13197
|
+
const want = segments.join("/");
|
|
13198
|
+
const last = segments[segments.length - 1];
|
|
13199
|
+
const hits = [
|
|
13200
|
+
...(idx.byName.get(last) ?? []).filter(
|
|
13201
|
+
(f) => exts.includes(ext(f)) && (stem(f) === want || stem(f).endsWith(`/${want}`))
|
|
13202
|
+
),
|
|
13203
|
+
...inits.flatMap(
|
|
13204
|
+
(init2) => (idx.byName.get(stem(init2)) ?? []).filter(
|
|
13205
|
+
(f) => f.endsWith(`/${want}/${init2}`) || f === `${want}/${init2}`
|
|
13206
|
+
)
|
|
13207
|
+
)
|
|
13208
|
+
];
|
|
13209
|
+
if (hits.length === 0) return null;
|
|
13210
|
+
return hits.sort(
|
|
13211
|
+
(a, b) => nearness(b, fromFile) - nearness(a, fromFile) || a.length - b.length
|
|
13212
|
+
)[0];
|
|
13213
|
+
}
|
|
13214
|
+
function pythonTargets(file, src, idx) {
|
|
13215
|
+
const out = [];
|
|
13216
|
+
const mod = (base, segs) => resolveBySuffix([...base, ...segs], [".py"], file, idx, ["__init__.py"]);
|
|
13217
|
+
for (const m of src.matchAll(PY_FROM)) {
|
|
13218
|
+
const dots = m[1].length;
|
|
13219
|
+
const segs = m[2] ? m[2].split(".") : [];
|
|
13220
|
+
const names = m[3].split(",").map((n2) => n2.trim().split(/\s+/)[0]);
|
|
13221
|
+
if (dots > 0) {
|
|
13222
|
+
let dir = path4.posix.dirname(file);
|
|
13223
|
+
for (let i = 1; i < dots; i++) dir = path4.posix.dirname(dir);
|
|
13224
|
+
const base = dir === "." ? [] : dir.split("/");
|
|
13225
|
+
const exact = (s) => {
|
|
13226
|
+
const p = [...base, ...s].join("/");
|
|
13227
|
+
return idx.known.has(`${p}.py`) ? `${p}.py` : idx.known.has(`${p}/__init__.py`) ? `${p}/__init__.py` : null;
|
|
13228
|
+
};
|
|
13229
|
+
const target = segs.length ? exact(segs) : null;
|
|
13230
|
+
if (target) out.push(target);
|
|
13231
|
+
for (const n2 of names) {
|
|
13232
|
+
const sub = exact([...segs, n2]);
|
|
13233
|
+
if (sub) out.push(sub);
|
|
13234
|
+
}
|
|
13235
|
+
} else if (segs.length) {
|
|
13236
|
+
const sub = names.map((n2) => mod([], [...segs, n2])).filter(Boolean);
|
|
13237
|
+
const target = resolveLongest(segs, (s) => mod([], s));
|
|
13238
|
+
out.push(...sub, ...target ? [target] : []);
|
|
13239
|
+
}
|
|
13240
|
+
}
|
|
13241
|
+
for (const m of src.matchAll(PY_IMPORT))
|
|
13242
|
+
for (const spec of m[1].split(",")) {
|
|
13243
|
+
const target = resolveLongest(spec.trim().split("."), (s) => mod([], s));
|
|
13244
|
+
if (target) out.push(target);
|
|
13245
|
+
}
|
|
13246
|
+
return out;
|
|
13247
|
+
}
|
|
13248
|
+
function rustTargets(file, src, idx) {
|
|
13249
|
+
const out = [];
|
|
13250
|
+
const dir = path4.posix.dirname(file);
|
|
13251
|
+
const base = path4.posix.basename(file, ".rs");
|
|
13252
|
+
const own = ["lib", "main", "mod"].includes(base) ? dir : `${dir}/${base}`;
|
|
13253
|
+
const crateSrc = [...idx.crates.values()].filter((s) => file.startsWith(`${s}/`)).sort((a, b) => b.length - a.length)[0];
|
|
13254
|
+
const inDir = (d, segs) => {
|
|
13255
|
+
const p = [d, ...segs].join("/");
|
|
13256
|
+
return idx.known.has(`${p}.rs`) ? `${p}.rs` : idx.known.has(`${p}/mod.rs`) ? `${p}/mod.rs` : null;
|
|
13257
|
+
};
|
|
13258
|
+
for (const m of src.matchAll(RS_MOD)) {
|
|
13259
|
+
const hit = inDir(own, [m[1]]);
|
|
13260
|
+
if (hit) out.push(hit);
|
|
13261
|
+
}
|
|
13262
|
+
for (const m of src.matchAll(RS_USE))
|
|
13263
|
+
for (const use of expandUse(m[1])) {
|
|
13264
|
+
const [head, ...rest] = use.replace(/^::/, "").split("::");
|
|
13265
|
+
let root;
|
|
13266
|
+
if (head === "crate") root = crateSrc;
|
|
13267
|
+
else if (head === "self") root = own;
|
|
13268
|
+
else if (head === "super")
|
|
13269
|
+
root = ["lib", "main", "mod"].includes(base) ? path4.posix.dirname(dir) : dir;
|
|
13270
|
+
else if (head && idx.crates.has(head)) {
|
|
13271
|
+
root = idx.crates.get(head);
|
|
13272
|
+
if (rest.length === 0) {
|
|
13273
|
+
const lib = `${root}/lib.rs`;
|
|
13274
|
+
if (idx.known.has(lib)) out.push(lib);
|
|
13275
|
+
continue;
|
|
13276
|
+
}
|
|
13277
|
+
}
|
|
13278
|
+
if (!root) continue;
|
|
13279
|
+
const hit = resolveLongest(rest, (s) => inDir(root, s));
|
|
13280
|
+
if (hit) out.push(hit);
|
|
13281
|
+
else if (head !== "self" && head !== "super") {
|
|
13282
|
+
const lib = `${root}/lib.rs`;
|
|
13283
|
+
if (idx.known.has(lib)) out.push(lib);
|
|
13284
|
+
}
|
|
13285
|
+
}
|
|
13286
|
+
return out;
|
|
13287
|
+
}
|
|
13288
|
+
function goTargets(file, src, idx) {
|
|
13289
|
+
const specs = [
|
|
13290
|
+
...[...src.matchAll(GO_BLOCK)].flatMap(
|
|
13291
|
+
(m) => [...m[1].matchAll(/"([^"]+)"/g)].map((x) => x[1])
|
|
13292
|
+
),
|
|
13293
|
+
...[...src.matchAll(GO_ONE)].map((m) => m[1])
|
|
13294
|
+
];
|
|
13295
|
+
const out = [];
|
|
13296
|
+
for (const spec of specs)
|
|
13297
|
+
for (const [mod, dir] of idx.goModules) {
|
|
13298
|
+
if (spec !== mod && !spec.startsWith(`${mod}/`)) continue;
|
|
13299
|
+
const pkgDir = [dir === "." ? "" : dir, spec.slice(mod.length + 1)].filter(Boolean).join("/");
|
|
13300
|
+
const rep = (idx.byDir.get(pkgDir) ?? []).find(
|
|
13301
|
+
(f) => f.endsWith(".go") && !f.endsWith("_test.go")
|
|
13302
|
+
);
|
|
13303
|
+
if (rep) out.push(rep);
|
|
13304
|
+
}
|
|
13305
|
+
return out;
|
|
13306
|
+
}
|
|
13307
|
+
function swiftTargets(src, idx) {
|
|
13308
|
+
const out = [];
|
|
13309
|
+
for (const m of src.matchAll(
|
|
13310
|
+
/^[ \t]*(?:@\w+[ \t]+)*import[ \t]+(?:\w+[ \t]+)?(\w+)/gm
|
|
13311
|
+
)) {
|
|
13312
|
+
for (const [dir, files] of idx.byDir) {
|
|
13313
|
+
if (!(dir === `Sources/${m[1]}` || dir.endsWith(`/Sources/${m[1]}`) || dir.includes(`/Sources/${m[1]}/`)))
|
|
13314
|
+
continue;
|
|
13315
|
+
const rep = files.find((f) => f.endsWith(".swift") || f.endsWith(".h"));
|
|
13316
|
+
if (rep) {
|
|
13317
|
+
out.push(rep);
|
|
13318
|
+
break;
|
|
13319
|
+
}
|
|
13320
|
+
}
|
|
13321
|
+
}
|
|
13322
|
+
return out;
|
|
13323
|
+
}
|
|
13324
|
+
function jvmTargets(file, src, idx) {
|
|
13325
|
+
const out = [];
|
|
13326
|
+
for (const m of src.matchAll(
|
|
13327
|
+
/^[ \t]*import[ \t]+(?:static[ \t]+)?([\w.]+)/gm
|
|
13328
|
+
)) {
|
|
13329
|
+
const hit = resolveLongest(
|
|
13330
|
+
m[1].split("."),
|
|
13331
|
+
(s) => resolveBySuffix(s, [".java", ".kt"], file, idx)
|
|
13332
|
+
);
|
|
13333
|
+
if (hit) out.push(hit);
|
|
13334
|
+
}
|
|
13335
|
+
return out;
|
|
13336
|
+
}
|
|
13337
|
+
function cTargets(file, src, idx) {
|
|
13338
|
+
const out = [];
|
|
13339
|
+
for (const m of src.matchAll(
|
|
13340
|
+
/^[ \t]*#[ \t]*(?:include|import)[ \t]*"([^"]+)"/gm
|
|
13341
|
+
)) {
|
|
13342
|
+
const rel = path4.posix.normalize(
|
|
13343
|
+
path4.posix.join(path4.posix.dirname(file), m[1])
|
|
13344
|
+
);
|
|
13345
|
+
if (idx.known.has(rel)) out.push(rel);
|
|
13346
|
+
else {
|
|
13347
|
+
const hit = resolveBySuffix(
|
|
13348
|
+
stem(m[1]).split("/"),
|
|
13349
|
+
[ext(m[1])],
|
|
13350
|
+
file,
|
|
13351
|
+
idx
|
|
13352
|
+
);
|
|
13353
|
+
if (hit) out.push(hit);
|
|
13354
|
+
}
|
|
13355
|
+
}
|
|
13356
|
+
return out;
|
|
13357
|
+
}
|
|
12939
13358
|
async function scanStructure(root) {
|
|
12940
13359
|
const entries = await readdir(root, { recursive: true, withFileTypes: true });
|
|
12941
13360
|
const files = [];
|
|
13361
|
+
const cargo = [];
|
|
13362
|
+
const gomod = [];
|
|
12942
13363
|
for (const e of entries) {
|
|
12943
13364
|
if (!e.isFile()) continue;
|
|
12944
13365
|
const parent = e.parentPath ?? e.path ?? root;
|
|
12945
13366
|
const rel = toPosix(path4.relative(root, path4.join(parent, e.name)));
|
|
12946
13367
|
if (rel.split("/").some((seg) => SKIP_DIRS.has(seg))) continue;
|
|
12947
|
-
if (SOURCE_EXTS.some((
|
|
13368
|
+
if (SOURCE_EXTS.some((x) => rel.endsWith(x))) files.push(rel);
|
|
13369
|
+
else if (e.name === "Cargo.toml") cargo.push(rel);
|
|
13370
|
+
else if (e.name === "go.mod") gomod.push(rel);
|
|
13371
|
+
}
|
|
13372
|
+
const read = (f) => readFile(path4.join(root, f), "utf8");
|
|
13373
|
+
const idx = {
|
|
13374
|
+
known: new Set(files),
|
|
13375
|
+
byName: /* @__PURE__ */ new Map(),
|
|
13376
|
+
byDir: /* @__PURE__ */ new Map(),
|
|
13377
|
+
crates: /* @__PURE__ */ new Map(),
|
|
13378
|
+
goModules: /* @__PURE__ */ new Map()
|
|
13379
|
+
};
|
|
13380
|
+
for (const f of [...files].sort()) {
|
|
13381
|
+
const name = path4.posix.basename(stem(f));
|
|
13382
|
+
idx.byName.set(name, [...idx.byName.get(name) ?? [], f]);
|
|
13383
|
+
const d = path4.posix.dirname(f);
|
|
13384
|
+
idx.byDir.set(d, [...idx.byDir.get(d) ?? [], f]);
|
|
13385
|
+
}
|
|
13386
|
+
for (const c of cargo) {
|
|
13387
|
+
const pkg = (await read(c)).match(
|
|
13388
|
+
/\[package\][^[]*?\bname\s*=\s*"([^"]+)"/
|
|
13389
|
+
);
|
|
13390
|
+
const dir = path4.posix.dirname(c);
|
|
13391
|
+
if (pkg)
|
|
13392
|
+
idx.crates.set(
|
|
13393
|
+
pkg[1].replace(/-/g, "_"),
|
|
13394
|
+
`${dir === "." ? "" : `${dir}/`}src`.replace(/^\//, "")
|
|
13395
|
+
);
|
|
12948
13396
|
}
|
|
12949
|
-
const
|
|
13397
|
+
for (const g of gomod) {
|
|
13398
|
+
const mod = (await read(g)).match(/^module\s+(\S+)/m);
|
|
13399
|
+
if (mod) idx.goModules.set(mod[1], path4.posix.dirname(g));
|
|
13400
|
+
}
|
|
13401
|
+
const exported = /* @__PURE__ */ new Map();
|
|
13402
|
+
for (const f of files.filter((x) => x.endsWith(".rs")))
|
|
13403
|
+
for (const m of (await read(f)).matchAll(RS_EXPORT))
|
|
13404
|
+
if (!exported.has(m[1])) exported.set(m[1], f);
|
|
13405
|
+
const names = [...exported.keys()].slice(0, 2e3);
|
|
13406
|
+
const callRe = names.length ? new RegExp(
|
|
13407
|
+
`\\b(${names.map((n2) => n2.replace(/[^\w]/g, "")).join("|")})\\s*\\(`,
|
|
13408
|
+
"g"
|
|
13409
|
+
) : null;
|
|
13410
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12950
13411
|
const edges = [];
|
|
13412
|
+
const add = (from, to) => {
|
|
13413
|
+
if (!to || to === from || !idx.known.has(to)) return;
|
|
13414
|
+
const k = JSON.stringify([from, to]);
|
|
13415
|
+
if (seen.has(k)) return;
|
|
13416
|
+
seen.add(k);
|
|
13417
|
+
edges.push({ from, to });
|
|
13418
|
+
};
|
|
12951
13419
|
for (const file of files) {
|
|
12952
|
-
const
|
|
12953
|
-
|
|
12954
|
-
|
|
12955
|
-
|
|
12956
|
-
|
|
13420
|
+
const src = await read(file);
|
|
13421
|
+
const x = ext(file);
|
|
13422
|
+
if (JS_EXTS.includes(x))
|
|
13423
|
+
for (const spec of extractImports(src))
|
|
13424
|
+
add(file, resolveSpecifier(file, spec, idx.known));
|
|
13425
|
+
else if (x === ".py")
|
|
13426
|
+
for (const t of pythonTargets(file, src, idx)) add(file, t);
|
|
13427
|
+
else if (x === ".rs")
|
|
13428
|
+
for (const t of rustTargets(file, src, idx)) add(file, t);
|
|
13429
|
+
else if (x === ".go")
|
|
13430
|
+
for (const t of goTargets(file, src, idx)) add(file, t);
|
|
13431
|
+
else if (x === ".swift")
|
|
13432
|
+
for (const t of swiftTargets(src, idx)) add(file, t);
|
|
13433
|
+
else if (x === ".java" || x === ".kt")
|
|
13434
|
+
for (const t of jvmTargets(file, src, idx)) add(file, t);
|
|
13435
|
+
else if (C_EXTS.includes(x))
|
|
13436
|
+
for (const t of cTargets(file, src, idx)) add(file, t);
|
|
13437
|
+
if (callRe && x !== ".rs")
|
|
13438
|
+
for (const m of src.matchAll(callRe))
|
|
13439
|
+
add(file, exported.get(m[1]) ?? null);
|
|
12957
13440
|
}
|
|
12958
13441
|
return { modules: files.sort(), edges };
|
|
12959
13442
|
}
|
|
12960
|
-
var SOURCE_EXTS, SKIP_DIRS, IMPORT_RE, toPosix;
|
|
13443
|
+
var JS_EXTS, C_EXTS, SOURCE_EXTS, SKIP_DIRS, IMPORT_RE, toPosix, ext, stem, nearness, resolveLongest, PY_FROM, PY_IMPORT, RS_MOD, RS_USE, expandUse, GO_BLOCK, GO_ONE, RS_EXPORT;
|
|
12961
13444
|
var init_structure = __esm({
|
|
12962
13445
|
"packages/ingest/src/structure.ts"() {
|
|
12963
13446
|
"use strict";
|
|
12964
13447
|
init_src2();
|
|
12965
13448
|
init_entities();
|
|
12966
|
-
|
|
13449
|
+
JS_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
13450
|
+
C_EXTS = [".c", ".h", ".cc", ".cpp", ".hpp", ".m", ".mm"];
|
|
13451
|
+
SOURCE_EXTS = [
|
|
13452
|
+
...JS_EXTS,
|
|
13453
|
+
".py",
|
|
13454
|
+
".rs",
|
|
13455
|
+
".go",
|
|
13456
|
+
".swift",
|
|
13457
|
+
".java",
|
|
13458
|
+
".kt",
|
|
13459
|
+
...C_EXTS
|
|
13460
|
+
];
|
|
12967
13461
|
SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
12968
13462
|
"node_modules",
|
|
12969
13463
|
".git",
|
|
@@ -12973,10 +13467,51 @@ var init_structure = __esm({
|
|
|
12973
13467
|
".next",
|
|
12974
13468
|
"dist",
|
|
12975
13469
|
"build",
|
|
12976
|
-
"coverage"
|
|
13470
|
+
"coverage",
|
|
13471
|
+
// Build output and dependency caches of the other languages.
|
|
13472
|
+
"target",
|
|
13473
|
+
".build",
|
|
13474
|
+
"__pycache__",
|
|
13475
|
+
".venv",
|
|
13476
|
+
"venv",
|
|
13477
|
+
"vendor",
|
|
13478
|
+
"Pods",
|
|
13479
|
+
"DerivedData",
|
|
13480
|
+
".gradle"
|
|
12977
13481
|
]);
|
|
12978
13482
|
IMPORT_RE = /(?:import|export)\s+[^'"]*?from\s+['"]([^'"]+)['"]|import\s*\(\s*['"]([^'"]+)['"]\s*\)|require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
12979
13483
|
toPosix = (p) => p.split(path4.sep).join("/");
|
|
13484
|
+
ext = (f) => path4.posix.extname(f);
|
|
13485
|
+
stem = (f) => f.slice(0, f.length - ext(f).length);
|
|
13486
|
+
nearness = (a, b) => {
|
|
13487
|
+
const x = a.split("/");
|
|
13488
|
+
const y = b.split("/");
|
|
13489
|
+
let i = 0;
|
|
13490
|
+
while (i < x.length && i < y.length && x[i] === y[i]) i++;
|
|
13491
|
+
return i;
|
|
13492
|
+
};
|
|
13493
|
+
resolveLongest = (segments, resolve) => {
|
|
13494
|
+
for (let n2 = segments.length; n2 > 0; n2--) {
|
|
13495
|
+
const hit = resolve(segments.slice(0, n2));
|
|
13496
|
+
if (hit) return hit;
|
|
13497
|
+
}
|
|
13498
|
+
return null;
|
|
13499
|
+
};
|
|
13500
|
+
PY_FROM = /^[ \t]*from[ \t]+(\.*)([\w.]*)[ \t]+import[ \t]+\(?([^\n#)]+)/gm;
|
|
13501
|
+
PY_IMPORT = /^[ \t]*import[ \t]+([\w.]+(?:[ \t]*,[ \t]*[\w.]+)*)/gm;
|
|
13502
|
+
RS_MOD = /^[ \t]*(?:pub(?:\([^)]*\))?[ \t]+)?mod[ \t]+(\w+)[ \t]*;/gm;
|
|
13503
|
+
RS_USE = /\buse[ \t]+([^;]+);/g;
|
|
13504
|
+
expandUse = (use) => {
|
|
13505
|
+
const clean = use.replace(/\s+/g, "");
|
|
13506
|
+
const brace = clean.match(/^(.*?)::\{(.*)\}$/);
|
|
13507
|
+
if (!brace) return [clean.replace(/::\*$/, "")];
|
|
13508
|
+
return brace[2].split(",").filter(Boolean).map(
|
|
13509
|
+
(part) => part === "self" ? brace[1] : `${brace[1]}::${part.replace(/::\*$/, "")}`
|
|
13510
|
+
);
|
|
13511
|
+
};
|
|
13512
|
+
GO_BLOCK = /\bimport[ \t]*\(([\s\S]*?)\)/g;
|
|
13513
|
+
GO_ONE = /\bimport[ \t]+(?:[\w.]+[ \t]+)?"([^"]+)"/g;
|
|
13514
|
+
RS_EXPORT = /extern[ \t]+"C"[ \t]+fn[ \t]+([A-Za-z_]\w{2,})/g;
|
|
12980
13515
|
}
|
|
12981
13516
|
});
|
|
12982
13517
|
|
|
@@ -14394,6 +14929,8 @@ var init_summaries = __esm({
|
|
|
14394
14929
|
"use strict";
|
|
14395
14930
|
init_src2();
|
|
14396
14931
|
init_src();
|
|
14932
|
+
init_routing();
|
|
14933
|
+
init_allowance();
|
|
14397
14934
|
REPORT_SYSTEM = [
|
|
14398
14935
|
"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
14936
|
"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 +15631,7 @@ var init_doku_model = __esm({
|
|
|
15094
15631
|
});
|
|
15095
15632
|
|
|
15096
15633
|
// packages/ingest/src/doku.ts
|
|
15097
|
-
import { createHash as
|
|
15634
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
15098
15635
|
import { mkdir as mkdir10, readFile as readFile10, rm as rm6, stat, writeFile as writeFile10 } from "node:fs/promises";
|
|
15099
15636
|
import path15 from "node:path";
|
|
15100
15637
|
function bodyLocations(o) {
|
|
@@ -15200,6 +15737,45 @@ function aiState(m) {
|
|
|
15200
15737
|
aiAttempts: typeof m?.aiAttempts === "number" ? m.aiAttempts : 0
|
|
15201
15738
|
};
|
|
15202
15739
|
}
|
|
15740
|
+
function aiFacts(kind, m) {
|
|
15741
|
+
if (!m) return null;
|
|
15742
|
+
if (kind === "feature") {
|
|
15743
|
+
const f = m;
|
|
15744
|
+
return JSON.stringify([
|
|
15745
|
+
f.title,
|
|
15746
|
+
f.what,
|
|
15747
|
+
f.behavior ?? null,
|
|
15748
|
+
f.config ?? [],
|
|
15749
|
+
f.locations ?? [],
|
|
15750
|
+
f.why ?? null
|
|
15751
|
+
]);
|
|
15752
|
+
}
|
|
15753
|
+
if (kind === "entry") {
|
|
15754
|
+
const e = m;
|
|
15755
|
+
return JSON.stringify([
|
|
15756
|
+
e.brainName,
|
|
15757
|
+
e.description ?? null,
|
|
15758
|
+
(e.architecture?.groups ?? []).map((g) => g.name),
|
|
15759
|
+
// Tuples, never objects: a stored model comes back from jsonb with its keys reordered.
|
|
15760
|
+
(e.architecture?.edges ?? []).map((x) => [x.from, x.to]),
|
|
15761
|
+
(e.features ?? []).map((f) => [f.slug, f.title, f.area, f.what])
|
|
15762
|
+
]);
|
|
15763
|
+
}
|
|
15764
|
+
return null;
|
|
15765
|
+
}
|
|
15766
|
+
function withStoredAi(model, stored) {
|
|
15767
|
+
const s = stored;
|
|
15768
|
+
const fresh = model;
|
|
15769
|
+
return {
|
|
15770
|
+
...model,
|
|
15771
|
+
...s.presentation ? { presentation: s.presentation } : {},
|
|
15772
|
+
body: s.body ?? null,
|
|
15773
|
+
docTitle: s.docTitle ?? null,
|
|
15774
|
+
category: s.category ?? null,
|
|
15775
|
+
...!fresh.flow && s.flow ? { flow: s.flow } : {},
|
|
15776
|
+
...!fresh.flowDiagram && s.flowDiagram ? { flowDiagram: s.flowDiagram } : {}
|
|
15777
|
+
};
|
|
15778
|
+
}
|
|
15203
15779
|
function aiComplete(kind, m) {
|
|
15204
15780
|
if (!m) return false;
|
|
15205
15781
|
const text = (v) => typeof v === "string" && v.trim().length > 0;
|
|
@@ -15215,6 +15791,28 @@ function aiComplete(kind, m) {
|
|
|
15215
15791
|
return true;
|
|
15216
15792
|
}
|
|
15217
15793
|
}
|
|
15794
|
+
function pagePrompt(input) {
|
|
15795
|
+
const { categories, flowDiagramTitle, hasFlow, sharedLines = [] } = input;
|
|
15796
|
+
return [
|
|
15797
|
+
"Categories (pick exactly one key):",
|
|
15798
|
+
...categories.length ? categories.map((c) => `- ${c.key}: ${c.name} (${c.description})`) : ["- general: General (everything)"],
|
|
15799
|
+
"",
|
|
15800
|
+
"Available diagrams (the only allowed flow block sources):",
|
|
15801
|
+
...flowDiagramTitle ? [
|
|
15802
|
+
`- flowDiagram: "${flowDiagramTitle}" (the feature's runtime flow, lanes and stations)`
|
|
15803
|
+
] : [],
|
|
15804
|
+
...hasFlow && !flowDiagramTitle ? ["- flow: the feature's runtime steps as a simple chain"] : [],
|
|
15805
|
+
...!flowDiagramTitle && !hasFlow ? ["- (none)"] : [],
|
|
15806
|
+
"",
|
|
15807
|
+
...sharedLines.length ? [
|
|
15808
|
+
"Documented elsewhere for the same endpoint (proven facts; reuse them, link the page):",
|
|
15809
|
+
...sharedLines,
|
|
15810
|
+
""
|
|
15811
|
+
] : [],
|
|
15812
|
+
"Fact sheet:",
|
|
15813
|
+
input.factSheet
|
|
15814
|
+
].join("\n");
|
|
15815
|
+
}
|
|
15218
15816
|
function parseDocPage(raw, categoryKeys, ctx) {
|
|
15219
15817
|
const bad = (error2) => ({ page: null, error: error2, dropped: [] });
|
|
15220
15818
|
const match = raw.match(/\{[\s\S]*\}/);
|
|
@@ -15358,16 +15956,32 @@ function parseArchNotes(raw, groups) {
|
|
|
15358
15956
|
};
|
|
15359
15957
|
}
|
|
15360
15958
|
function archFlowsInput(model) {
|
|
15959
|
+
const modules = (model.raw?.modules ?? []).filter(
|
|
15960
|
+
(m) => !/\.test\.[a-z]+$/.test(m)
|
|
15961
|
+
);
|
|
15962
|
+
const cited = new Set(model.features.flatMap((f) => f.locations));
|
|
15361
15963
|
const files = /* @__PURE__ */ new Map();
|
|
15362
|
-
for (const m of
|
|
15363
|
-
|
|
15964
|
+
for (const m of [
|
|
15965
|
+
...modules.filter((m2) => cited.has(m2)),
|
|
15966
|
+
...modules.filter((m2) => !cited.has(m2))
|
|
15967
|
+
]) {
|
|
15364
15968
|
const g = moduleGroup(m);
|
|
15365
15969
|
const list = files.get(g) ?? [];
|
|
15366
|
-
if (list.length <
|
|
15970
|
+
if (list.length < 12) list.push(m);
|
|
15367
15971
|
files.set(g, list);
|
|
15368
15972
|
}
|
|
15973
|
+
const spanning = model.features.flatMap((f) => {
|
|
15974
|
+
const groups = [...new Set(f.locations.map(moduleGroup))];
|
|
15975
|
+
return groups.length >= 2 ? [`- ${f.title}: ${groups.join(", ")}`] : [];
|
|
15976
|
+
});
|
|
15369
15977
|
return [
|
|
15370
15978
|
archNotesInput(model),
|
|
15979
|
+
...spanning.length ? [
|
|
15980
|
+
"",
|
|
15981
|
+
"## Features spanning groups (each feature's code lives in all of these groups)",
|
|
15982
|
+
"",
|
|
15983
|
+
...spanning
|
|
15984
|
+
] : [],
|
|
15371
15985
|
"",
|
|
15372
15986
|
"## Files (the ONLY paths you may cite as evidence, copied exactly)",
|
|
15373
15987
|
...[...files.entries()].flatMap(([g, list]) => [
|
|
@@ -15632,45 +16246,69 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
15632
16246
|
}
|
|
15633
16247
|
continue;
|
|
15634
16248
|
}
|
|
15635
|
-
const
|
|
16249
|
+
const facts = aiFacts(page.kind, page.model);
|
|
16250
|
+
const sameFacts = stored !== void 0 && (facts !== null ? aiFacts(page.kind, stored) === facts : state.factHash === factHash);
|
|
15636
16251
|
let markdown = page.markdown;
|
|
15637
16252
|
let model = page.model;
|
|
15638
16253
|
let icon = null;
|
|
15639
16254
|
let ok2 = !required;
|
|
15640
|
-
if (
|
|
15641
|
-
|
|
15642
|
-
|
|
15643
|
-
|
|
15644
|
-
|
|
15645
|
-
|
|
15646
|
-
|
|
15647
|
-
|
|
15648
|
-
|
|
15649
|
-
|
|
15650
|
-
|
|
15651
|
-
|
|
15652
|
-
|
|
15653
|
-
|
|
16255
|
+
if (page.polishable && page.kind === "architecture") {
|
|
16256
|
+
const inputs = {
|
|
16257
|
+
notes: sha1(archNotesInput(doku)),
|
|
16258
|
+
flows: sha1(archFlowsInput(doku))
|
|
16259
|
+
};
|
|
16260
|
+
const s = stored ?? {};
|
|
16261
|
+
let notesFor;
|
|
16262
|
+
let flowsFor;
|
|
16263
|
+
if (typeof s.narrative === "string" && s.aiInputs?.notes === inputs.notes) {
|
|
16264
|
+
model = { ...model, narrative: s.narrative, roles: s.roles };
|
|
16265
|
+
notesFor = inputs.notes;
|
|
16266
|
+
ok2 = true;
|
|
16267
|
+
} else if (opts.llm) {
|
|
16268
|
+
try {
|
|
16269
|
+
const { notes } = parseArchNotes(
|
|
16270
|
+
await opts.llm.complete({
|
|
16271
|
+
system: ARCH_SYSTEM,
|
|
16272
|
+
prompt: archNotesInput(doku),
|
|
16273
|
+
maxTokens: 800
|
|
16274
|
+
}),
|
|
16275
|
+
doku.architecture.groups.map((g) => g.name)
|
|
16276
|
+
);
|
|
16277
|
+
if (notes) {
|
|
16278
|
+
model = {
|
|
16279
|
+
...model,
|
|
16280
|
+
narrative: notes.narrative,
|
|
16281
|
+
roles: notes.roles
|
|
16282
|
+
};
|
|
16283
|
+
notesFor = inputs.notes;
|
|
16284
|
+
polished = true;
|
|
16285
|
+
ok2 = true;
|
|
16286
|
+
}
|
|
16287
|
+
} catch {
|
|
15654
16288
|
}
|
|
15655
|
-
} catch {
|
|
15656
16289
|
}
|
|
15657
|
-
|
|
15658
|
-
|
|
15659
|
-
|
|
15660
|
-
|
|
15661
|
-
|
|
15662
|
-
|
|
15663
|
-
|
|
15664
|
-
|
|
15665
|
-
|
|
15666
|
-
|
|
15667
|
-
|
|
15668
|
-
|
|
16290
|
+
if (Array.isArray(s.flows) && s.flows.length > 0 && s.aiInputs?.flows === inputs.flows) {
|
|
16291
|
+
model = { ...model, flows: s.flows };
|
|
16292
|
+
flowsFor = inputs.flows;
|
|
16293
|
+
} else if (opts.llm) {
|
|
16294
|
+
try {
|
|
16295
|
+
const { flows } = parseArchFlows(
|
|
16296
|
+
await opts.llm.complete({
|
|
16297
|
+
system: ARCH_FLOWS_SYSTEM,
|
|
16298
|
+
prompt: archFlowsInput(doku),
|
|
16299
|
+
maxTokens: 4500
|
|
16300
|
+
}),
|
|
16301
|
+
new Set(doku.raw?.modules ?? [])
|
|
16302
|
+
);
|
|
16303
|
+
if (flows.length > 0) {
|
|
16304
|
+
model = { ...model, flows };
|
|
16305
|
+
flowsFor = inputs.flows;
|
|
16306
|
+
polished = true;
|
|
16307
|
+
}
|
|
16308
|
+
} catch {
|
|
15669
16309
|
}
|
|
15670
|
-
} catch {
|
|
15671
16310
|
}
|
|
15672
16311
|
if (!ok2 && sameFacts) {
|
|
15673
|
-
const s = stored;
|
|
15674
16312
|
const kept = model;
|
|
15675
16313
|
model = {
|
|
15676
16314
|
...model,
|
|
@@ -15678,6 +16316,14 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
15678
16316
|
...!kept.flows && Array.isArray(s.flows) ? { flows: s.flows } : {}
|
|
15679
16317
|
};
|
|
15680
16318
|
}
|
|
16319
|
+
const present = model;
|
|
16320
|
+
model = {
|
|
16321
|
+
...model,
|
|
16322
|
+
aiInputs: {
|
|
16323
|
+
...typeof present.narrative === "string" ? { notes: notesFor ?? s.aiInputs?.notes } : {},
|
|
16324
|
+
...Array.isArray(present.flows) ? { flows: flowsFor ?? s.aiInputs?.flows } : {}
|
|
16325
|
+
}
|
|
16326
|
+
};
|
|
15681
16327
|
const arch = model;
|
|
15682
16328
|
if (arch.narrative || arch.flows?.length)
|
|
15683
16329
|
markdown = renderArchitecturePage(doku, {
|
|
@@ -15686,7 +16332,11 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
15686
16332
|
});
|
|
15687
16333
|
} else if (page.kind === "entry") {
|
|
15688
16334
|
model = { ...page.model, categories };
|
|
15689
|
-
|
|
16335
|
+
const s = stored;
|
|
16336
|
+
if (sameFacts && typeof s?.narrative === "string" && s.narrative.trim()) {
|
|
16337
|
+
model = { ...model, narrative: s.narrative };
|
|
16338
|
+
ok2 = true;
|
|
16339
|
+
} else if (opts.llm && page.polishable) {
|
|
15690
16340
|
try {
|
|
15691
16341
|
const narrative2 = (await opts.llm.complete({
|
|
15692
16342
|
system: ENTRY_NARRATIVE_SYSTEM,
|
|
@@ -15700,15 +16350,16 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
15700
16350
|
}
|
|
15701
16351
|
} catch {
|
|
15702
16352
|
}
|
|
15703
|
-
if (!ok2 && sameFacts) {
|
|
15704
|
-
const s = stored;
|
|
15705
|
-
if (typeof s.narrative === "string" && s.narrative.trim())
|
|
15706
|
-
model = { ...model, narrative: s.narrative };
|
|
15707
|
-
}
|
|
15708
16353
|
}
|
|
15709
16354
|
const narrative = model.narrative;
|
|
15710
16355
|
if (narrative)
|
|
15711
16356
|
markdown = renderEntryPage(doku, "brainjar-doku/", narrative);
|
|
16357
|
+
} else if (page.kind === "feature" && sameFacts && !storedCollisions.has(page.slug) && aiComplete("feature", stored)) {
|
|
16358
|
+
const f = withStoredAi(page.model, stored);
|
|
16359
|
+
model = f;
|
|
16360
|
+
markdown = renderFeaturePage(f);
|
|
16361
|
+
if (f.docTitle) page.title = f.docTitle;
|
|
16362
|
+
ok2 = true;
|
|
15712
16363
|
} else if (opts.llm && page.polishable) {
|
|
15713
16364
|
try {
|
|
15714
16365
|
const { flow } = parseFlow(
|
|
@@ -15762,25 +16413,13 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
15762
16413
|
hasFlowDiagram: Boolean(diagrams.flowDiagram),
|
|
15763
16414
|
hasFlow: Boolean(diagrams.flow)
|
|
15764
16415
|
};
|
|
15765
|
-
const prompt =
|
|
15766
|
-
|
|
15767
|
-
|
|
15768
|
-
|
|
15769
|
-
|
|
15770
|
-
|
|
15771
|
-
|
|
15772
|
-
] : [],
|
|
15773
|
-
...ctx.hasFlow && !ctx.hasFlowDiagram ? ["- flow: the feature's runtime steps as a simple chain"] : [],
|
|
15774
|
-
...!ctx.hasFlowDiagram && !ctx.hasFlow ? ["- (none)"] : [],
|
|
15775
|
-
"",
|
|
15776
|
-
...sharedLines.length ? [
|
|
15777
|
-
"Documented elsewhere for the same endpoint (proven facts; reuse them, link the page):",
|
|
15778
|
-
...sharedLines,
|
|
15779
|
-
""
|
|
15780
|
-
] : [],
|
|
15781
|
-
"Fact sheet:",
|
|
15782
|
-
page.markdown
|
|
15783
|
-
].join("\n");
|
|
16416
|
+
const prompt = pagePrompt({
|
|
16417
|
+
categories,
|
|
16418
|
+
factSheet: page.markdown,
|
|
16419
|
+
flowDiagramTitle: diagrams.flowDiagram?.title ?? null,
|
|
16420
|
+
hasFlow: ctx.hasFlow,
|
|
16421
|
+
sharedLines
|
|
16422
|
+
});
|
|
15784
16423
|
const keys = categoryKeys.length ? categoryKeys : ["general"];
|
|
15785
16424
|
let synth = parseDocPage(
|
|
15786
16425
|
await opts.llm.complete({
|
|
@@ -15810,17 +16449,20 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
15810
16449
|
`doku: blocks dropped (${page.slug}): ${synth.dropped.join("; ")}`
|
|
15811
16450
|
);
|
|
15812
16451
|
if (synth.page) {
|
|
15813
|
-
const
|
|
16452
|
+
const s = stored;
|
|
16453
|
+
const same = s?.title === page.model.title && !storedCollisions.has(page.slug);
|
|
16454
|
+
const title = same && s?.docTitle ? s.docTitle : pickDocTitle(
|
|
15814
16455
|
synth.page.title,
|
|
15815
16456
|
page.title,
|
|
15816
16457
|
page.slug,
|
|
15817
16458
|
takenTitle
|
|
15818
16459
|
);
|
|
16460
|
+
const category = same && s?.category && categoryKeys.includes(s.category) ? s.category : synth.page.category;
|
|
15819
16461
|
const f = {
|
|
15820
16462
|
...model,
|
|
15821
16463
|
presentation: synth.page.presentation,
|
|
15822
16464
|
docTitle: title,
|
|
15823
|
-
category
|
|
16465
|
+
category
|
|
15824
16466
|
};
|
|
15825
16467
|
f.body = pageBody(f);
|
|
15826
16468
|
model = f;
|
|
@@ -15838,17 +16480,7 @@ async function generateDokuInner(brainId, opts = {}) {
|
|
|
15838
16480
|
console.warn(`doku: page synthesis failed (${page.slug}):`, e);
|
|
15839
16481
|
}
|
|
15840
16482
|
if (!ok2 && sameFacts && aiComplete("feature", stored)) {
|
|
15841
|
-
const
|
|
15842
|
-
const fresh = model;
|
|
15843
|
-
const f = {
|
|
15844
|
-
...model,
|
|
15845
|
-
...s.presentation ? { presentation: s.presentation } : {},
|
|
15846
|
-
body: s.body ?? null,
|
|
15847
|
-
docTitle: s.docTitle ?? null,
|
|
15848
|
-
category: s.category ?? null,
|
|
15849
|
-
...!fresh.flow && s.flow ? { flow: s.flow } : {},
|
|
15850
|
-
...!fresh.flowDiagram && s.flowDiagram ? { flowDiagram: s.flowDiagram } : {}
|
|
15851
|
-
};
|
|
16483
|
+
const f = withStoredAi(model, stored);
|
|
15852
16484
|
model = f;
|
|
15853
16485
|
markdown = renderFeaturePage(f);
|
|
15854
16486
|
if (f.docTitle) page.title = f.docTitle;
|
|
@@ -16094,7 +16726,7 @@ var init_doku = __esm({
|
|
|
16094
16726
|
DOKU_DIR = "brainjar-doku";
|
|
16095
16727
|
MANIFEST = ".manifest.json";
|
|
16096
16728
|
CHANGELOG_DAYS = 60;
|
|
16097
|
-
sha1 = (s) =>
|
|
16729
|
+
sha1 = (s) => createHash3("sha1").update(s).digest("hex");
|
|
16098
16730
|
exists = (p) => stat(p).then(
|
|
16099
16731
|
() => true,
|
|
16100
16732
|
() => false
|
|
@@ -16121,7 +16753,7 @@ var init_doku = __esm({
|
|
|
16121
16753
|
`{"kind":"flow","title":"...","source":"flowDiagram"}: places the feature's existing runtime diagram where it helps the explanation; allowed only when the input lists that diagram as available, at most once.`,
|
|
16122
16754
|
"Composition: explanation and outcome before technical detail. A feature exposing an API documents what it is, why you need it, and each endpoint with its parameters, request and response shape, and error cases, all from the facts only. A user-facing feature explains what is shown and how to use it, with steps only for a real action sequence. A pipeline explains what goes in, what comes out, and when it runs. A configuration-heavy feature tabulates its settings and explains precedence and effects. A feature containing several of these covers each. Mix kinds freely; there is no fixed template and no fixed number of blocks.",
|
|
16123
16755
|
"Length follows the subject: an extensive feature gets several text sections with subheadings, the relationships, prerequisites, special cases, and reasons explained, and a concrete example; a small feature stays short. Never break a coherent explanation into bullet points for layout reasons and never cut it short. Explain a term at its first use; state the concrete consequence for the reader.",
|
|
16124
|
-
"Keep every fact; invent nothing. Missing facts stay missing: leave optional blocks out, and when an indispensable piece of information is not available, say so in one plain sentence addressed to the reader (for example: The exact request path is not documented yet.), never mentioning a fact sheet, an input, or a model.
|
|
16756
|
+
"Keep every fact; invent nothing. Missing facts stay missing: leave optional blocks out, and when an indispensable piece of information is not available, say so in one plain sentence addressed to the reader (for example: The exact request path is not documented yet.), never mentioning a fact sheet, an input, or a model. The change history and the decision list are shown separately below the page: never reproduce them as a list or table. Their content still counts as facts. The page describes the CURRENT state: where a newer history entry or decision added, changed, replaced, or removed something, the text states the current state (for example that a capability no longer exists, or which mechanism replaced another), and the reasons that explain the current design belong in the explanation. When facts conflict, the newer one wins. Never replace a stated fact with what is usual elsewhere. No H1 heading (the title renders above).",
|
|
16125
16757
|
"When the input lists facts documented elsewhere for the SAME endpoint or interface, use them: they are proven, so the endpoint may be shown here as well, and the other page may be linked with a short readable link text in the form [text](page:slug), copying the slug exactly. Never merge pages because their titles look alike.",
|
|
16126
16758
|
PROSE_STYLE
|
|
16127
16759
|
].join(" ");
|
|
@@ -16177,11 +16809,12 @@ var init_doku = __esm({
|
|
|
16177
16809
|
"You read one architecture overview of a software system (module groups with their features and dependencies, plus real file paths) and produce the system's main RUNTIME flows: how a request, session, or piece of data actually moves through the system.",
|
|
16178
16810
|
'Respond ONLY with JSON of exactly this shape: {"flows": [{"slug": "...", "title": "...", "summary": "...", "explanation": "...", "lanes": [{"key": "...", "title": "..."}], "nodes": [{"id": "...", "lane": "...", "title": "...", "subtitle": "...", "kind": "...", "evidence": ["path"]}], "edges": [{"from": "...", "to": "...", "label": "...", "kind": "..."}]}]}. Nothing outside the JSON.',
|
|
16179
16811
|
'"explanation" walks a reader through THIS flow in markdown (2-5 short paragraphs, ### subheadings where they help, at most 3500 characters): what starts it, what each phase does, what comes out. Describe an order only where the edges show it; where paths run in parallel or depend on a gate, say so instead of inventing a sequence. Name the same stations as the nodes.',
|
|
16180
|
-
|
|
16812
|
+
'Produce up to 4 flows, one per MAIN runtime path a newcomer must understand (for example: how input becomes stored data, how a query becomes an answer, how an external agent connects, how background verification runs). Cover every major path the listed features describe and no path they do not: when they describe fewer paths, produce fewer flows, and when no feature is documented at all, respond {"flows": []}.',
|
|
16181
16813
|
"lanes are 3-5 PHASES of the flow in order (intake, processing, storage, ...), titled in plain words. Never folder names.",
|
|
16182
16814
|
"nodes are the runtime components in those phases: 4-10 per flow. kind is exactly one of: actor (human or external agent), surface (UI or endpoint), logic (application work), policy (a gate: auth, quota, validation), action (an outward call such as an LLM or external API), data (a store). title at most 3 words; subtitle at most 3 words (under 24 characters).",
|
|
16183
16815
|
"evidence lists 1-3 file paths copied EXACTLY from the Files section that implement the node. Never invent or modify a path; omit evidence rather than guess.",
|
|
16184
16816
|
'edges carry meaning: label with WHAT flows (at most 3 words, under 20 characters, never counts). kind is "primary" for the main path, "return" for a reply going back, "policy" for a gate outcome, "async" for background work.',
|
|
16817
|
+
"Draw an edge only where the input shows the connection: a listed dependency between the two groups, a feature whose code spans both groups (Features spanning groups), or one feature describing both steps. Never connect components because such systems usually are connected; a path the input does not show stays out of the flow.",
|
|
16185
16818
|
"Ground everything in the listed features and dependencies. Invent nothing."
|
|
16186
16819
|
].join(" ");
|
|
16187
16820
|
FEATURE_FLOW_SYSTEM = [
|
|
@@ -16311,10 +16944,12 @@ var init_verify_agent = __esm({
|
|
|
16311
16944
|
init_sdk();
|
|
16312
16945
|
init_src2();
|
|
16313
16946
|
init_src();
|
|
16947
|
+
init_allowance();
|
|
16314
16948
|
init_product_facts();
|
|
16315
16949
|
init_doku();
|
|
16316
16950
|
init_llm();
|
|
16317
16951
|
init_openai();
|
|
16952
|
+
init_provider_rate2();
|
|
16318
16953
|
init_routing();
|
|
16319
16954
|
VERIFY_SYSTEM = [
|
|
16320
16955
|
"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 +16998,40 @@ var init_verify_poll = __esm({
|
|
|
16363
16998
|
}
|
|
16364
16999
|
});
|
|
16365
17000
|
|
|
17001
|
+
// packages/ingest/src/scheduler.ts
|
|
17002
|
+
var init_scheduler = __esm({
|
|
17003
|
+
"packages/ingest/src/scheduler.ts"() {
|
|
17004
|
+
"use strict";
|
|
17005
|
+
init_src2();
|
|
17006
|
+
init_daily_log();
|
|
17007
|
+
init_summaries();
|
|
17008
|
+
}
|
|
17009
|
+
});
|
|
17010
|
+
|
|
16366
17011
|
// packages/ingest/src/job-worker.ts
|
|
16367
17012
|
var init_job_worker = __esm({
|
|
16368
17013
|
"packages/ingest/src/job-worker.ts"() {
|
|
16369
17014
|
"use strict";
|
|
16370
17015
|
init_src2();
|
|
16371
17016
|
init_deepscan();
|
|
17017
|
+
init_doku();
|
|
17018
|
+
init_scheduler();
|
|
17019
|
+
init_summaries();
|
|
17020
|
+
init_verify_poll();
|
|
16372
17021
|
init_routing();
|
|
17022
|
+
init_allowance();
|
|
16373
17023
|
init_verify_start();
|
|
16374
17024
|
}
|
|
16375
17025
|
});
|
|
16376
17026
|
|
|
17027
|
+
// packages/ingest/src/job-clock.ts
|
|
17028
|
+
var init_job_clock = __esm({
|
|
17029
|
+
"packages/ingest/src/job-clock.ts"() {
|
|
17030
|
+
"use strict";
|
|
17031
|
+
init_src2();
|
|
17032
|
+
}
|
|
17033
|
+
});
|
|
17034
|
+
|
|
16377
17035
|
// packages/ingest/src/ask-agent-model.ts
|
|
16378
17036
|
var init_ask_agent_model = __esm({
|
|
16379
17037
|
"packages/ingest/src/ask-agent-model.ts"() {
|
|
@@ -16381,18 +17039,10 @@ var init_ask_agent_model = __esm({
|
|
|
16381
17039
|
init_sdk();
|
|
16382
17040
|
init_src2();
|
|
16383
17041
|
init_src();
|
|
17042
|
+
init_allowance();
|
|
16384
17043
|
init_llm();
|
|
16385
17044
|
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();
|
|
17045
|
+
init_provider_rate2();
|
|
16396
17046
|
}
|
|
16397
17047
|
});
|
|
16398
17048
|
|
|
@@ -16415,6 +17065,7 @@ var init_src3 = __esm({
|
|
|
16415
17065
|
init_llm();
|
|
16416
17066
|
init_openai();
|
|
16417
17067
|
init_routing();
|
|
17068
|
+
init_allowance();
|
|
16418
17069
|
init_rationale_extractor();
|
|
16419
17070
|
init_extraction();
|
|
16420
17071
|
init_entities();
|
|
@@ -16453,6 +17104,7 @@ var init_src3 = __esm({
|
|
|
16453
17104
|
init_verify_poll();
|
|
16454
17105
|
init_verify_start();
|
|
16455
17106
|
init_job_worker();
|
|
17107
|
+
init_job_clock();
|
|
16456
17108
|
init_ask_agent_model();
|
|
16457
17109
|
init_scheduler();
|
|
16458
17110
|
registerConnector(
|
|
@@ -16505,7 +17157,24 @@ async function captureApi(pathAndQuery, init2 = {}) {
|
|
|
16505
17157
|
}
|
|
16506
17158
|
return body;
|
|
16507
17159
|
}
|
|
16508
|
-
|
|
17160
|
+
async function uploadArtifacts(brain, sourceId, artifacts, cursor3) {
|
|
17161
|
+
let inserted = 0;
|
|
17162
|
+
for (let i = 0; i < artifacts.length; i += UPLOAD_PORTION) {
|
|
17163
|
+
const last = i + UPLOAD_PORTION >= artifacts.length;
|
|
17164
|
+
const r = await captureApi("/capture/artifacts", {
|
|
17165
|
+
method: "POST",
|
|
17166
|
+
body: {
|
|
17167
|
+
brain,
|
|
17168
|
+
sourceId,
|
|
17169
|
+
artifacts: artifacts.slice(i, i + UPLOAD_PORTION),
|
|
17170
|
+
cursor: last ? cursor3 : null
|
|
17171
|
+
}
|
|
17172
|
+
});
|
|
17173
|
+
inserted += r.inserted;
|
|
17174
|
+
}
|
|
17175
|
+
return inserted;
|
|
17176
|
+
}
|
|
17177
|
+
var TokenError, TOKEN_HELP, UPLOAD_PORTION;
|
|
16509
17178
|
var init_api_client = __esm({
|
|
16510
17179
|
"packages/cli/src/api-client.ts"() {
|
|
16511
17180
|
"use strict";
|
|
@@ -16513,6 +17182,7 @@ var init_api_client = __esm({
|
|
|
16513
17182
|
TokenError = class extends Error {
|
|
16514
17183
|
};
|
|
16515
17184
|
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.";
|
|
17185
|
+
UPLOAD_PORTION = 2e3;
|
|
16516
17186
|
}
|
|
16517
17187
|
});
|
|
16518
17188
|
|
|
@@ -16745,7 +17415,7 @@ var init_debt = __esm({
|
|
|
16745
17415
|
});
|
|
16746
17416
|
|
|
16747
17417
|
// packages/cli/src/adapters/claude-code.ts
|
|
16748
|
-
import { createHash as
|
|
17418
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
16749
17419
|
function targetOf(input) {
|
|
16750
17420
|
const i = input ?? {};
|
|
16751
17421
|
for (const k of ["command", "file_path", "notebook_path"]) {
|
|
@@ -16790,7 +17460,7 @@ var init_claude_code = __esm({
|
|
|
16790
17460
|
"use strict";
|
|
16791
17461
|
init_src();
|
|
16792
17462
|
EDIT_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
|
|
16793
|
-
short2 = (s) =>
|
|
17463
|
+
short2 = (s) => createHash4("sha256").update(s).digest("hex").slice(0, 16);
|
|
16794
17464
|
claudeCode2 = {
|
|
16795
17465
|
id: "claude-code",
|
|
16796
17466
|
map(payload, now) {
|
|
@@ -16889,7 +17559,7 @@ var init_claude_code = __esm({
|
|
|
16889
17559
|
});
|
|
16890
17560
|
|
|
16891
17561
|
// packages/cli/src/adapters/cursor.ts
|
|
16892
|
-
import { createHash as
|
|
17562
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
16893
17563
|
function targetOf2(input) {
|
|
16894
17564
|
const i = input ?? {};
|
|
16895
17565
|
for (const k of ["command", "file_path", "path", "target_file"]) {
|
|
@@ -16910,7 +17580,7 @@ var init_cursor = __esm({
|
|
|
16910
17580
|
"packages/cli/src/adapters/cursor.ts"() {
|
|
16911
17581
|
"use strict";
|
|
16912
17582
|
init_src();
|
|
16913
|
-
short3 = (s) =>
|
|
17583
|
+
short3 = (s) => createHash5("sha256").update(s).digest("hex").slice(0, 16);
|
|
16914
17584
|
cursor2 = {
|
|
16915
17585
|
id: "cursor",
|
|
16916
17586
|
map(payload, now) {
|
|
@@ -17028,7 +17698,7 @@ var init_cursor = __esm({
|
|
|
17028
17698
|
});
|
|
17029
17699
|
|
|
17030
17700
|
// packages/cli/src/adapters/codex.ts
|
|
17031
|
-
import { createHash as
|
|
17701
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
17032
17702
|
function patchFiles(patch) {
|
|
17033
17703
|
const out = /* @__PURE__ */ new Set();
|
|
17034
17704
|
for (const m of patch.matchAll(
|
|
@@ -17064,7 +17734,7 @@ var init_codex = __esm({
|
|
|
17064
17734
|
"use strict";
|
|
17065
17735
|
init_src();
|
|
17066
17736
|
PATCH_TOOLS = /* @__PURE__ */ new Set(["apply_patch", "Edit", "Write"]);
|
|
17067
|
-
short4 = (s) =>
|
|
17737
|
+
short4 = (s) => createHash6("sha256").update(s).digest("hex").slice(0, 16);
|
|
17068
17738
|
codex2 = {
|
|
17069
17739
|
id: "codex",
|
|
17070
17740
|
map(payload, now) {
|
|
@@ -17161,7 +17831,7 @@ var init_codex = __esm({
|
|
|
17161
17831
|
});
|
|
17162
17832
|
|
|
17163
17833
|
// packages/cli/src/adapters/copilot.ts
|
|
17164
|
-
import { createHash as
|
|
17834
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
17165
17835
|
function targetOf3(args) {
|
|
17166
17836
|
if (!args || typeof args !== "object") return "";
|
|
17167
17837
|
const a = args;
|
|
@@ -17194,7 +17864,7 @@ var init_copilot = __esm({
|
|
|
17194
17864
|
"use strict";
|
|
17195
17865
|
init_src();
|
|
17196
17866
|
EDIT_TOOLS2 = /* @__PURE__ */ new Set(["edit", "create"]);
|
|
17197
|
-
short5 = (s) =>
|
|
17867
|
+
short5 = (s) => createHash7("sha256").update(s).digest("hex").slice(0, 16);
|
|
17198
17868
|
EVENT_ALIASES = {
|
|
17199
17869
|
SessionStart: "sessionStart",
|
|
17200
17870
|
SessionEnd: "sessionEnd",
|
|
@@ -17303,7 +17973,7 @@ var init_copilot = __esm({
|
|
|
17303
17973
|
});
|
|
17304
17974
|
|
|
17305
17975
|
// packages/cli/src/adapters/gemini.ts
|
|
17306
|
-
import { createHash as
|
|
17976
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
17307
17977
|
function targetOf4(input) {
|
|
17308
17978
|
const i = input ?? {};
|
|
17309
17979
|
for (const k of ["command", "file_path"]) {
|
|
@@ -17325,7 +17995,7 @@ var init_gemini = __esm({
|
|
|
17325
17995
|
"use strict";
|
|
17326
17996
|
init_src();
|
|
17327
17997
|
EDIT_TOOLS3 = /* @__PURE__ */ new Set(["write_file", "replace"]);
|
|
17328
|
-
short6 = (s) =>
|
|
17998
|
+
short6 = (s) => createHash8("sha256").update(s).digest("hex").slice(0, 16);
|
|
17329
17999
|
gemini2 = {
|
|
17330
18000
|
id: "gemini",
|
|
17331
18001
|
map(payload, now) {
|
|
@@ -17421,7 +18091,7 @@ var init_gemini = __esm({
|
|
|
17421
18091
|
});
|
|
17422
18092
|
|
|
17423
18093
|
// packages/cli/src/adapters/kiro.ts
|
|
17424
|
-
import { createHash as
|
|
18094
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
17425
18095
|
function targetOf5(input) {
|
|
17426
18096
|
const i = input ?? {};
|
|
17427
18097
|
for (const k of ["command", "cmd", "path", "file_path", "filePath"]) {
|
|
@@ -17452,7 +18122,7 @@ var init_kiro = __esm({
|
|
|
17452
18122
|
"use strict";
|
|
17453
18123
|
init_src();
|
|
17454
18124
|
EDIT_TOOLS4 = /* @__PURE__ */ new Set(["fs_write", "write", "str_replace", "fs_append"]);
|
|
17455
|
-
short7 = (s) =>
|
|
18125
|
+
short7 = (s) => createHash9("sha256").update(s).digest("hex").slice(0, 16);
|
|
17456
18126
|
EVENTS5 = {
|
|
17457
18127
|
agentSpawn: "start",
|
|
17458
18128
|
sessionStart: "start",
|
|
@@ -17568,7 +18238,7 @@ var init_kiro = __esm({
|
|
|
17568
18238
|
});
|
|
17569
18239
|
|
|
17570
18240
|
// packages/cli/src/adapters/opencode.ts
|
|
17571
|
-
import { createHash as
|
|
18241
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
17572
18242
|
function commitShas7(text) {
|
|
17573
18243
|
const out = /* @__PURE__ */ new Set();
|
|
17574
18244
|
for (const m of (text ?? "").matchAll(/\[[^\]\n]* ([0-9a-f]{7,40})\]/g)) {
|
|
@@ -17582,7 +18252,7 @@ var init_opencode = __esm({
|
|
|
17582
18252
|
"use strict";
|
|
17583
18253
|
init_src();
|
|
17584
18254
|
EDIT_TOOLS5 = /* @__PURE__ */ new Set(["edit", "write"]);
|
|
17585
|
-
short8 = (s) =>
|
|
18255
|
+
short8 = (s) => createHash10("sha256").update(s).digest("hex").slice(0, 16);
|
|
17586
18256
|
opencode2 = {
|
|
17587
18257
|
id: "opencode",
|
|
17588
18258
|
map(payload, now) {
|
|
@@ -17682,7 +18352,7 @@ var init_opencode = __esm({
|
|
|
17682
18352
|
});
|
|
17683
18353
|
|
|
17684
18354
|
// packages/cli/src/adapters/cline.ts
|
|
17685
|
-
import { createHash as
|
|
18355
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
17686
18356
|
function targetOf6(params) {
|
|
17687
18357
|
const i = params ?? {};
|
|
17688
18358
|
for (const k of ["command", "path"]) {
|
|
@@ -17714,7 +18384,7 @@ var init_cline = __esm({
|
|
|
17714
18384
|
"use strict";
|
|
17715
18385
|
init_src();
|
|
17716
18386
|
EDIT_TOOLS6 = /* @__PURE__ */ new Set(["write_to_file", "replace_in_file", "apply_patch"]);
|
|
17717
|
-
short9 = (s) =>
|
|
18387
|
+
short9 = (s) => createHash11("sha256").update(s).digest("hex").slice(0, 16);
|
|
17718
18388
|
CLINE_REPLY = JSON.stringify({ cancel: false });
|
|
17719
18389
|
cline2 = {
|
|
17720
18390
|
id: "cline",
|
|
@@ -18016,18 +18686,15 @@ async function syncRepo(root) {
|
|
|
18016
18686
|
const connector = createConnector("git_local", { cwd: root }, {});
|
|
18017
18687
|
const artifacts = await connector.sync(project.cursor ?? void 0);
|
|
18018
18688
|
if (artifacts.length > 0) {
|
|
18019
|
-
const
|
|
18020
|
-
|
|
18021
|
-
|
|
18022
|
-
|
|
18023
|
-
|
|
18024
|
-
|
|
18025
|
-
|
|
18026
|
-
}
|
|
18027
|
-
});
|
|
18028
|
-
if (r.inserted > 0) {
|
|
18689
|
+
const inserted = await uploadArtifacts(
|
|
18690
|
+
link.brainId,
|
|
18691
|
+
project.sourceId,
|
|
18692
|
+
artifacts,
|
|
18693
|
+
maxUpdatedAt(artifacts) ?? project.cursor
|
|
18694
|
+
);
|
|
18695
|
+
if (inserted > 0) {
|
|
18029
18696
|
console.error(
|
|
18030
|
-
`brainjar: ${
|
|
18697
|
+
`brainjar: ${inserted} new commits sent, the server reads them in the background`
|
|
18031
18698
|
);
|
|
18032
18699
|
const graph = await scanStructure(root);
|
|
18033
18700
|
await captureApi("/capture/structure", {
|
|
@@ -18095,7 +18762,7 @@ async function waitForServer(brainId, since, opts = {}) {
|
|
|
18095
18762
|
const poll = opts.poll ?? (() => captureApi(
|
|
18096
18763
|
`/capture/progress?brain=${brainId}&since=${encodeURIComponent(since.toISOString())}`
|
|
18097
18764
|
));
|
|
18098
|
-
const
|
|
18765
|
+
const sleep3 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
18099
18766
|
const write = opts.write ?? ((s) => void process.stdout.write(s));
|
|
18100
18767
|
const tty = opts.tty ?? Boolean(process.stdout.isTTY);
|
|
18101
18768
|
const intervalMs = opts.intervalMs ?? 2e3;
|
|
@@ -18112,7 +18779,7 @@ async function waitForServer(brainId, since, opts = {}) {
|
|
|
18112
18779
|
if (tty && shown) write("\n");
|
|
18113
18780
|
return null;
|
|
18114
18781
|
}
|
|
18115
|
-
await
|
|
18782
|
+
await sleep3(intervalMs);
|
|
18116
18783
|
continue;
|
|
18117
18784
|
}
|
|
18118
18785
|
if (p.queued + p.running === 0) {
|
|
@@ -18127,7 +18794,7 @@ async function waitForServer(brainId, since, opts = {}) {
|
|
|
18127
18794
|
shownAt = Date.now();
|
|
18128
18795
|
}
|
|
18129
18796
|
shown = line;
|
|
18130
|
-
await
|
|
18797
|
+
await sleep3(intervalMs);
|
|
18131
18798
|
}
|
|
18132
18799
|
}
|
|
18133
18800
|
var n;
|
|
@@ -18213,16 +18880,12 @@ async function init(argv) {
|
|
|
18213
18880
|
const artifacts = await connector.sync(conn.cursor ?? void 0);
|
|
18214
18881
|
let commits = 0;
|
|
18215
18882
|
if (artifacts.length > 0) {
|
|
18216
|
-
|
|
18217
|
-
|
|
18218
|
-
|
|
18219
|
-
|
|
18220
|
-
|
|
18221
|
-
|
|
18222
|
-
cursor: maxUpdatedAt(artifacts) ?? conn.cursor
|
|
18223
|
-
}
|
|
18224
|
-
});
|
|
18225
|
-
commits = r.inserted;
|
|
18883
|
+
commits = await uploadArtifacts(
|
|
18884
|
+
conn.brainId,
|
|
18885
|
+
conn.sourceId,
|
|
18886
|
+
artifacts,
|
|
18887
|
+
maxUpdatedAt(artifacts) ?? conn.cursor
|
|
18888
|
+
);
|
|
18226
18889
|
}
|
|
18227
18890
|
ok(
|
|
18228
18891
|
`${graph.modules.length} modules, ${commits} new commits sent to the server`
|