modelstat 0.0.52 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +801 -174
- package/dist/cli.mjs.map +1 -1
- package/package.json +2 -2
package/dist/cli.mjs
CHANGED
|
@@ -63,17 +63,77 @@ import { execFile } from "child_process";
|
|
|
63
63
|
import { promisify } from "util";
|
|
64
64
|
import { dirname, resolve } from "path";
|
|
65
65
|
import { existsSync } from "fs";
|
|
66
|
+
function findRepoRoot(startCwd) {
|
|
67
|
+
let cwd = resolve(startCwd);
|
|
68
|
+
for (let i = 0; i < 10; i++) {
|
|
69
|
+
if (existsSync(`${cwd}/.git`)) return cwd;
|
|
70
|
+
const parent = dirname(cwd);
|
|
71
|
+
if (parent === cwd) return null;
|
|
72
|
+
cwd = parent;
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
function parseRemote(url) {
|
|
77
|
+
const ssh = /^(?:git@)?([^:]+):([^/]+)\/([^.]+?)(?:\.git)?$/.exec(url);
|
|
78
|
+
if (ssh) return { host: ssh[1] ?? null, slug: `${ssh[2]}/${ssh[3]}` };
|
|
79
|
+
try {
|
|
80
|
+
const u = new URL(url);
|
|
81
|
+
const m = /^\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/.exec(u.pathname);
|
|
82
|
+
if (!m) return { host: u.hostname, slug: null };
|
|
83
|
+
return { host: u.hostname, slug: `${m[1]}/${m[2]}` };
|
|
84
|
+
} catch {
|
|
85
|
+
return { host: null, slug: null };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async function resolveGitContext(cwd) {
|
|
89
|
+
if (!cwd) return null;
|
|
90
|
+
if (cache.has(cwd)) return cache.get(cwd) ?? null;
|
|
91
|
+
const root = findRepoRoot(cwd);
|
|
92
|
+
if (!root) {
|
|
93
|
+
const empty = {
|
|
94
|
+
remote_url: null,
|
|
95
|
+
remote_host: null,
|
|
96
|
+
remote_slug: null,
|
|
97
|
+
branch: null,
|
|
98
|
+
commit_sha: null
|
|
99
|
+
};
|
|
100
|
+
cache.set(cwd, empty);
|
|
101
|
+
return empty;
|
|
102
|
+
}
|
|
103
|
+
const ran = async (args) => {
|
|
104
|
+
try {
|
|
105
|
+
const { stdout } = await pexec("git", args, { cwd: root, timeout: 2e3 });
|
|
106
|
+
return stdout.trim() || null;
|
|
107
|
+
} catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
const remoteUrl = await ran(["config", "--get", "remote.origin.url"]);
|
|
112
|
+
const branch = await ran(["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
113
|
+
const sha = await ran(["rev-parse", "HEAD"]);
|
|
114
|
+
const parsed = remoteUrl ? parseRemote(remoteUrl) : { host: null, slug: null };
|
|
115
|
+
const ctx = {
|
|
116
|
+
remote_url: remoteUrl,
|
|
117
|
+
remote_host: parsed.host,
|
|
118
|
+
remote_slug: parsed.slug,
|
|
119
|
+
branch,
|
|
120
|
+
commit_sha: sha
|
|
121
|
+
};
|
|
122
|
+
cache.set(cwd, ctx);
|
|
123
|
+
return ctx;
|
|
124
|
+
}
|
|
66
125
|
function guessRepoSlugFromPath(cwd) {
|
|
67
126
|
if (!cwd) return null;
|
|
68
127
|
const m = /\/(?:www|src|code|repos|projects)\/([^/]+)\/([^/]+)/i.exec(cwd);
|
|
69
128
|
if (m) return `${m[1]}/${m[2]}`;
|
|
70
129
|
return null;
|
|
71
130
|
}
|
|
72
|
-
var pexec;
|
|
131
|
+
var pexec, cache;
|
|
73
132
|
var init_git = __esm({
|
|
74
133
|
"../../packages/parsers/src/git.ts"() {
|
|
75
134
|
"use strict";
|
|
76
135
|
pexec = promisify(execFile);
|
|
136
|
+
cache = /* @__PURE__ */ new Map();
|
|
77
137
|
}
|
|
78
138
|
});
|
|
79
139
|
|
|
@@ -4603,6 +4663,327 @@ var init_redact = __esm({
|
|
|
4603
4663
|
}
|
|
4604
4664
|
});
|
|
4605
4665
|
|
|
4666
|
+
// ../../packages/core/src/session-metadata.ts
|
|
4667
|
+
function emptyDetectedRefs() {
|
|
4668
|
+
return { repos: [], pull_requests: [], commits: [], issues: [] };
|
|
4669
|
+
}
|
|
4670
|
+
function repoFrom(host, slug, source) {
|
|
4671
|
+
return { host, slug, branches: [], source };
|
|
4672
|
+
}
|
|
4673
|
+
function detectReferences(text, source = "content") {
|
|
4674
|
+
const out = emptyDetectedRefs();
|
|
4675
|
+
if (!text) return out;
|
|
4676
|
+
for (const m of text.matchAll(GITHUB_PR)) {
|
|
4677
|
+
const slug = `${m[1]}/${m[2]}`;
|
|
4678
|
+
out.pull_requests.push({
|
|
4679
|
+
host: "github.com",
|
|
4680
|
+
slug,
|
|
4681
|
+
number: Number(m[3]),
|
|
4682
|
+
url: m[0],
|
|
4683
|
+
source,
|
|
4684
|
+
confidence: 0.95
|
|
4685
|
+
});
|
|
4686
|
+
out.repos.push(repoFrom("github.com", slug, source));
|
|
4687
|
+
}
|
|
4688
|
+
for (const m of text.matchAll(GITLAB_MR)) {
|
|
4689
|
+
out.pull_requests.push({
|
|
4690
|
+
host: "gitlab.com",
|
|
4691
|
+
slug: m[1] ?? null,
|
|
4692
|
+
number: Number(m[2]),
|
|
4693
|
+
url: m[0],
|
|
4694
|
+
source,
|
|
4695
|
+
confidence: 0.95
|
|
4696
|
+
});
|
|
4697
|
+
if (m[1]) out.repos.push(repoFrom("gitlab.com", m[1], source));
|
|
4698
|
+
}
|
|
4699
|
+
for (const m of text.matchAll(BITBUCKET_PR)) {
|
|
4700
|
+
const slug = `${m[1]}/${m[2]}`;
|
|
4701
|
+
out.pull_requests.push({
|
|
4702
|
+
host: "bitbucket.org",
|
|
4703
|
+
slug,
|
|
4704
|
+
number: Number(m[3]),
|
|
4705
|
+
url: m[0],
|
|
4706
|
+
source,
|
|
4707
|
+
confidence: 0.95
|
|
4708
|
+
});
|
|
4709
|
+
out.repos.push(repoFrom("bitbucket.org", slug, source));
|
|
4710
|
+
}
|
|
4711
|
+
for (const m of text.matchAll(GITHUB_ISSUE)) {
|
|
4712
|
+
const slug = `${m[1]}/${m[2]}`;
|
|
4713
|
+
out.issues.push({
|
|
4714
|
+
provider: "github",
|
|
4715
|
+
key: m[3] ?? "",
|
|
4716
|
+
slug,
|
|
4717
|
+
url: m[0],
|
|
4718
|
+
source,
|
|
4719
|
+
confidence: 0.95
|
|
4720
|
+
});
|
|
4721
|
+
out.repos.push(repoFrom("github.com", slug, source));
|
|
4722
|
+
}
|
|
4723
|
+
for (const m of text.matchAll(GITLAB_ISSUE)) {
|
|
4724
|
+
out.issues.push({
|
|
4725
|
+
provider: "gitlab",
|
|
4726
|
+
key: m[2] ?? "",
|
|
4727
|
+
slug: m[1] ?? null,
|
|
4728
|
+
url: m[0],
|
|
4729
|
+
source,
|
|
4730
|
+
confidence: 0.95
|
|
4731
|
+
});
|
|
4732
|
+
if (m[1]) out.repos.push(repoFrom("gitlab.com", m[1], source));
|
|
4733
|
+
}
|
|
4734
|
+
for (const m of text.matchAll(LINEAR_ISSUE)) {
|
|
4735
|
+
out.issues.push({
|
|
4736
|
+
provider: "linear",
|
|
4737
|
+
key: m[1] ?? "",
|
|
4738
|
+
slug: null,
|
|
4739
|
+
url: m[0],
|
|
4740
|
+
source,
|
|
4741
|
+
confidence: 0.9
|
|
4742
|
+
});
|
|
4743
|
+
}
|
|
4744
|
+
for (const m of text.matchAll(JIRA_ISSUE)) {
|
|
4745
|
+
out.issues.push({
|
|
4746
|
+
provider: "jira",
|
|
4747
|
+
key: m[1] ?? "",
|
|
4748
|
+
slug: null,
|
|
4749
|
+
url: m[0],
|
|
4750
|
+
source,
|
|
4751
|
+
confidence: 0.9
|
|
4752
|
+
});
|
|
4753
|
+
}
|
|
4754
|
+
for (const m of text.matchAll(GITHUB_COMMIT)) {
|
|
4755
|
+
const slug = `${m[1]}/${m[2]}`;
|
|
4756
|
+
out.commits.push({ sha: m[3] ?? "", slug, url: m[0], source, confidence: 0.95 });
|
|
4757
|
+
out.repos.push(repoFrom("github.com", slug, source));
|
|
4758
|
+
}
|
|
4759
|
+
for (const m of text.matchAll(GITLAB_COMMIT)) {
|
|
4760
|
+
out.commits.push({
|
|
4761
|
+
sha: m[2] ?? "",
|
|
4762
|
+
slug: m[1] ?? null,
|
|
4763
|
+
url: m[0],
|
|
4764
|
+
source,
|
|
4765
|
+
confidence: 0.95
|
|
4766
|
+
});
|
|
4767
|
+
if (m[1]) out.repos.push(repoFrom("gitlab.com", m[1], source));
|
|
4768
|
+
}
|
|
4769
|
+
for (const m of text.matchAll(SLUG_HASH)) {
|
|
4770
|
+
const slug = m[1] ?? "";
|
|
4771
|
+
out.issues.push({
|
|
4772
|
+
provider: "github",
|
|
4773
|
+
key: m[2] ?? "",
|
|
4774
|
+
slug,
|
|
4775
|
+
url: null,
|
|
4776
|
+
source,
|
|
4777
|
+
confidence: 0.55
|
|
4778
|
+
});
|
|
4779
|
+
}
|
|
4780
|
+
if (source === "model") {
|
|
4781
|
+
for (const m of text.matchAll(BARE_TICKET)) {
|
|
4782
|
+
out.issues.push({
|
|
4783
|
+
provider: "other",
|
|
4784
|
+
key: m[1] ?? "",
|
|
4785
|
+
slug: null,
|
|
4786
|
+
url: null,
|
|
4787
|
+
source,
|
|
4788
|
+
confidence: 0.4
|
|
4789
|
+
});
|
|
4790
|
+
}
|
|
4791
|
+
}
|
|
4792
|
+
return out;
|
|
4793
|
+
}
|
|
4794
|
+
function detectBranchTickets(branch) {
|
|
4795
|
+
if (!branch) return [];
|
|
4796
|
+
const out = [];
|
|
4797
|
+
for (const m of branch.matchAll(BARE_TICKET)) {
|
|
4798
|
+
out.push({
|
|
4799
|
+
provider: "other",
|
|
4800
|
+
key: m[1] ?? "",
|
|
4801
|
+
slug: null,
|
|
4802
|
+
url: null,
|
|
4803
|
+
source: "git",
|
|
4804
|
+
confidence: 0.7
|
|
4805
|
+
});
|
|
4806
|
+
}
|
|
4807
|
+
return out;
|
|
4808
|
+
}
|
|
4809
|
+
function stronger(a, b) {
|
|
4810
|
+
return SOURCE_RANK[a] >= SOURCE_RANK[b] ? a : b;
|
|
4811
|
+
}
|
|
4812
|
+
function dedupe(items, keyOf, merge) {
|
|
4813
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
4814
|
+
for (const item of items) {
|
|
4815
|
+
const key = keyOf(item);
|
|
4816
|
+
const existing = byKey.get(key);
|
|
4817
|
+
byKey.set(key, existing ? merge(existing, item) : item);
|
|
4818
|
+
}
|
|
4819
|
+
return [...byKey.values()];
|
|
4820
|
+
}
|
|
4821
|
+
function dedupeSessionMetadata(parts) {
|
|
4822
|
+
const all = emptyDetectedRefs();
|
|
4823
|
+
for (const p of parts) {
|
|
4824
|
+
all.repos.push(...p.repos);
|
|
4825
|
+
all.pull_requests.push(...p.pull_requests);
|
|
4826
|
+
all.commits.push(...p.commits);
|
|
4827
|
+
all.issues.push(...p.issues);
|
|
4828
|
+
}
|
|
4829
|
+
const repos = dedupe(
|
|
4830
|
+
all.repos,
|
|
4831
|
+
(r) => r.slug.toLowerCase(),
|
|
4832
|
+
(a, b) => ({
|
|
4833
|
+
host: a.host ?? b.host,
|
|
4834
|
+
slug: a.slug,
|
|
4835
|
+
branches: [.../* @__PURE__ */ new Set([...a.branches, ...b.branches])].slice(0, 50),
|
|
4836
|
+
source: stronger(a.source, b.source)
|
|
4837
|
+
})
|
|
4838
|
+
);
|
|
4839
|
+
const score = (r) => SOURCE_RANK[r.source] * 2 + (r.confidence ?? 0);
|
|
4840
|
+
const pull_requests = dedupe(
|
|
4841
|
+
all.pull_requests,
|
|
4842
|
+
(p) => `${(p.slug ?? "").toLowerCase()}#${p.number}`,
|
|
4843
|
+
(a, b) => {
|
|
4844
|
+
const [win, lose] = score(a) >= score(b) ? [a, b] : [b, a];
|
|
4845
|
+
return {
|
|
4846
|
+
host: win.host ?? lose.host,
|
|
4847
|
+
slug: win.slug ?? lose.slug,
|
|
4848
|
+
number: win.number,
|
|
4849
|
+
url: win.url ?? lose.url,
|
|
4850
|
+
source: win.source,
|
|
4851
|
+
confidence: Math.max(win.confidence, lose.confidence)
|
|
4852
|
+
};
|
|
4853
|
+
}
|
|
4854
|
+
);
|
|
4855
|
+
const commits = dedupe(
|
|
4856
|
+
all.commits,
|
|
4857
|
+
// Key on slug + sha: two different commits in two repos can share a short
|
|
4858
|
+
// 7-hex prefix, and must not collapse into one.
|
|
4859
|
+
(c) => `${(c.slug ?? "").toLowerCase()}@${c.sha.toLowerCase()}`,
|
|
4860
|
+
(a, b) => {
|
|
4861
|
+
const [win, lose] = score(a) >= score(b) ? [a, b] : [b, a];
|
|
4862
|
+
return {
|
|
4863
|
+
sha: win.sha,
|
|
4864
|
+
slug: win.slug ?? lose.slug,
|
|
4865
|
+
url: win.url ?? lose.url,
|
|
4866
|
+
source: win.source,
|
|
4867
|
+
confidence: Math.max(win.confidence, lose.confidence)
|
|
4868
|
+
};
|
|
4869
|
+
}
|
|
4870
|
+
);
|
|
4871
|
+
const issues = dedupe(
|
|
4872
|
+
all.issues,
|
|
4873
|
+
(i) => `${i.provider}:${(i.slug ?? "").toLowerCase()}#${i.key.toLowerCase()}`,
|
|
4874
|
+
(a, b) => {
|
|
4875
|
+
const [win, lose] = score(a) >= score(b) ? [a, b] : [b, a];
|
|
4876
|
+
return {
|
|
4877
|
+
provider: win.provider,
|
|
4878
|
+
key: win.key,
|
|
4879
|
+
slug: win.slug ?? lose.slug,
|
|
4880
|
+
url: win.url ?? lose.url,
|
|
4881
|
+
source: win.source,
|
|
4882
|
+
confidence: Math.max(win.confidence, lose.confidence)
|
|
4883
|
+
};
|
|
4884
|
+
}
|
|
4885
|
+
);
|
|
4886
|
+
const prKeys = new Set(pull_requests.map((p) => `${(p.slug ?? "").toLowerCase()}#${p.number}`));
|
|
4887
|
+
const reconciledIssues = issues.filter(
|
|
4888
|
+
(i) => !(i.provider === "github" && prKeys.has(`${(i.slug ?? "").toLowerCase()}#${i.key}`))
|
|
4889
|
+
);
|
|
4890
|
+
const keepValid = (schema, items) => items.filter((item) => schema.safeParse(item).success);
|
|
4891
|
+
return {
|
|
4892
|
+
repos: keepValid(RepoRef, repos).slice(0, 50),
|
|
4893
|
+
pull_requests: keepValid(PullRequestRef, pull_requests).slice(0, 100),
|
|
4894
|
+
commits: keepValid(CommitRef, commits).slice(0, 200),
|
|
4895
|
+
issues: keepValid(IssueRef, reconciledIssues).slice(0, 100)
|
|
4896
|
+
};
|
|
4897
|
+
}
|
|
4898
|
+
function isEmptySessionMetadata(m) {
|
|
4899
|
+
return m.repos.length === 0 && m.pull_requests.length === 0 && m.commits.length === 0 && m.issues.length === 0;
|
|
4900
|
+
}
|
|
4901
|
+
function detectEventReferences(text) {
|
|
4902
|
+
if (!text) return null;
|
|
4903
|
+
const m = dedupeSessionMetadata([detectReferences(text, "content")]);
|
|
4904
|
+
if (isEmptySessionMetadata(m)) return null;
|
|
4905
|
+
return {
|
|
4906
|
+
repos: m.repos.slice(0, 24),
|
|
4907
|
+
pull_requests: m.pull_requests.slice(0, 24),
|
|
4908
|
+
commits: m.commits.slice(0, 24),
|
|
4909
|
+
issues: m.issues.slice(0, 24)
|
|
4910
|
+
};
|
|
4911
|
+
}
|
|
4912
|
+
var REF_SOURCES, RefSource, SOURCE_RANK, RepoRef, PullRequestRef, CommitRef, ISSUE_PROVIDERS, IssueRef, SessionMetadata, EventReferences, GITHUB_PR, GITLAB_MR, BITBUCKET_PR, GITHUB_ISSUE, GITLAB_ISSUE, GITHUB_COMMIT, GITLAB_COMMIT, LINEAR_ISSUE, JIRA_ISSUE, SLUG_HASH, BARE_TICKET;
|
|
4913
|
+
var init_session_metadata = __esm({
|
|
4914
|
+
"../../packages/core/src/session-metadata.ts"() {
|
|
4915
|
+
"use strict";
|
|
4916
|
+
init_zod();
|
|
4917
|
+
REF_SOURCES = ["git", "tool", "content", "model"];
|
|
4918
|
+
RefSource = external_exports.enum(REF_SOURCES);
|
|
4919
|
+
SOURCE_RANK = { git: 3, tool: 2, content: 1, model: 0 };
|
|
4920
|
+
RepoRef = external_exports.object({
|
|
4921
|
+
/** `github.com`, `gitlab.com`, … — null when only the slug is known. */
|
|
4922
|
+
host: external_exports.string().max(80).nullable().default(null),
|
|
4923
|
+
/** `org/repo`. The stable identity of the repo. */
|
|
4924
|
+
slug: external_exports.string().max(200),
|
|
4925
|
+
/** Every branch observed for this repo this session. */
|
|
4926
|
+
branches: external_exports.array(external_exports.string().max(200)).max(50).default([]),
|
|
4927
|
+
source: RefSource.default("content")
|
|
4928
|
+
});
|
|
4929
|
+
PullRequestRef = external_exports.object({
|
|
4930
|
+
host: external_exports.string().max(80).nullable().default(null),
|
|
4931
|
+
slug: external_exports.string().max(200).nullable().default(null),
|
|
4932
|
+
number: external_exports.number().int().positive(),
|
|
4933
|
+
url: external_exports.string().max(400).nullable().default(null),
|
|
4934
|
+
source: RefSource.default("content"),
|
|
4935
|
+
confidence: external_exports.number().min(0).max(1).default(0.9)
|
|
4936
|
+
});
|
|
4937
|
+
CommitRef = external_exports.object({
|
|
4938
|
+
/** 7–40 char hex SHA. */
|
|
4939
|
+
sha: external_exports.string().max(64),
|
|
4940
|
+
slug: external_exports.string().max(200).nullable().default(null),
|
|
4941
|
+
url: external_exports.string().max(400).nullable().default(null),
|
|
4942
|
+
source: RefSource.default("content"),
|
|
4943
|
+
confidence: external_exports.number().min(0).max(1).default(0.8)
|
|
4944
|
+
});
|
|
4945
|
+
ISSUE_PROVIDERS = [
|
|
4946
|
+
"github",
|
|
4947
|
+
"gitlab",
|
|
4948
|
+
"bitbucket",
|
|
4949
|
+
"linear",
|
|
4950
|
+
"jira",
|
|
4951
|
+
"other"
|
|
4952
|
+
];
|
|
4953
|
+
IssueRef = external_exports.object({
|
|
4954
|
+
provider: external_exports.enum(ISSUE_PROVIDERS).default("other"),
|
|
4955
|
+
key: external_exports.string().max(80),
|
|
4956
|
+
slug: external_exports.string().max(200).nullable().default(null),
|
|
4957
|
+
url: external_exports.string().max(400).nullable().default(null),
|
|
4958
|
+
source: RefSource.default("content"),
|
|
4959
|
+
confidence: external_exports.number().min(0).max(1).default(0.8)
|
|
4960
|
+
});
|
|
4961
|
+
SessionMetadata = external_exports.object({
|
|
4962
|
+
repos: external_exports.array(RepoRef).max(50).default([]),
|
|
4963
|
+
pull_requests: external_exports.array(PullRequestRef).max(100).default([]),
|
|
4964
|
+
commits: external_exports.array(CommitRef).max(200).default([]),
|
|
4965
|
+
issues: external_exports.array(IssueRef).max(100).default([])
|
|
4966
|
+
});
|
|
4967
|
+
EventReferences = external_exports.object({
|
|
4968
|
+
repos: external_exports.array(RepoRef).max(24).default([]),
|
|
4969
|
+
pull_requests: external_exports.array(PullRequestRef).max(24).default([]),
|
|
4970
|
+
commits: external_exports.array(CommitRef).max(24).default([]),
|
|
4971
|
+
issues: external_exports.array(IssueRef).max(24).default([])
|
|
4972
|
+
});
|
|
4973
|
+
GITHUB_PR = /https?:\/\/github\.com\/([\w.-]+)\/([\w.-]+)\/pull\/(\d+)/gi;
|
|
4974
|
+
GITLAB_MR = /https?:\/\/gitlab\.com\/([\w./-]+?)\/-\/merge_requests\/(\d+)/gi;
|
|
4975
|
+
BITBUCKET_PR = /https?:\/\/bitbucket\.org\/([\w.-]+)\/([\w.-]+)\/pull-requests\/(\d+)/gi;
|
|
4976
|
+
GITHUB_ISSUE = /https?:\/\/github\.com\/([\w.-]+)\/([\w.-]+)\/issues\/(\d+)/gi;
|
|
4977
|
+
GITLAB_ISSUE = /https?:\/\/gitlab\.com\/([\w./-]+?)\/-\/issues\/(\d+)/gi;
|
|
4978
|
+
GITHUB_COMMIT = /https?:\/\/github\.com\/([\w.-]+)\/([\w.-]+)\/commit\/([0-9a-f]{7,40})/gi;
|
|
4979
|
+
GITLAB_COMMIT = /https?:\/\/gitlab\.com\/([\w./-]+?)\/-\/commit\/([0-9a-f]{7,40})/gi;
|
|
4980
|
+
LINEAR_ISSUE = /https?:\/\/linear\.app\/[\w.-]+\/issue\/([A-Z][A-Z0-9]*-\d+)/gi;
|
|
4981
|
+
JIRA_ISSUE = /https?:\/\/[\w.-]+\/browse\/([A-Z][A-Z0-9]+-\d+)/gi;
|
|
4982
|
+
SLUG_HASH = /(?:^|[\s([{<])([\w.-]+\/[\w.-]+)#(\d+)\b/g;
|
|
4983
|
+
BARE_TICKET = /\b([A-Z][A-Z0-9]{1,9}-\d{1,6})\b/g;
|
|
4984
|
+
}
|
|
4985
|
+
});
|
|
4986
|
+
|
|
4606
4987
|
// ../../packages/core/src/schemas.ts
|
|
4607
4988
|
var TokenUsage, GitContext, RawEvent, RedactionReport, TaxonomyHintRooted, Segment, ToolAction, ToolCallWire, IngestBatch, HeartbeatPayload, DeviceEnrollment, DeviceSelfRegister, DeviceClaimRequest, ProcessingMetadata, RedactionPolicy, DetectedInstallation, DetectedIdentity, DiscoveryReport, ClassificationConfidenceEnum;
|
|
4608
4989
|
var init_schemas = __esm({
|
|
@@ -4610,6 +4991,7 @@ var init_schemas = __esm({
|
|
|
4610
4991
|
"use strict";
|
|
4611
4992
|
init_zod();
|
|
4612
4993
|
init_enums();
|
|
4994
|
+
init_session_metadata();
|
|
4613
4995
|
TokenUsage = external_exports.object({
|
|
4614
4996
|
input: external_exports.number().int().nonnegative().default(0),
|
|
4615
4997
|
output: external_exports.number().int().nonnegative().default(0),
|
|
@@ -4662,6 +5044,12 @@ var init_schemas = __esm({
|
|
|
4662
5044
|
// summarize prompt; it never gets stored long-term server-side, only
|
|
4663
5045
|
// used to construct the summarize input.
|
|
4664
5046
|
content_excerpt: external_exports.string().max(320).optional(),
|
|
5047
|
+
// Public code references (PRs, issues, commits, repos) detected on-device
|
|
5048
|
+
// from this turn's FULL text — the high-recall feed the server rolls up into
|
|
5049
|
+
// SessionMetadata. Only public reference shapes (forge URLs, slugs, numbers,
|
|
5050
|
+
// ticket keys) ride here, never raw text — so it is derived pre-redaction
|
|
5051
|
+
// safely (same class as git.remote_slug). Optional + additive.
|
|
5052
|
+
references: EventReferences.optional(),
|
|
4665
5053
|
// Reference to originating file for reparsing
|
|
4666
5054
|
source_file: external_exports.string().max(1024).nullable(),
|
|
4667
5055
|
source_byte_offset: external_exports.number().int().nonnegative().nullable(),
|
|
@@ -4810,7 +5198,14 @@ var init_schemas = __esm({
|
|
|
4810
5198
|
* session view on every upload, so the latest batch always carries the
|
|
4811
5199
|
* freshest title. Absent for runtimes without a titler (older agents,
|
|
4812
5200
|
* no-op browser summariser). */
|
|
4813
|
-
session_titles: external_exports.record(external_exports.string(), external_exports.string().max(120)).optional()
|
|
5201
|
+
session_titles: external_exports.record(external_exports.string(), external_exports.string().max(120)).optional(),
|
|
5202
|
+
/** Optional per-session deterministic metadata — session_id →
|
|
5203
|
+
* {@link SessionMetadata}: the repos, pull requests, commits, and issues the
|
|
5204
|
+
* session touched, detected on-device across git context, tool calls,
|
|
5205
|
+
* redacted content, and the local model (so it works for any provider).
|
|
5206
|
+
* Additive — old companions omit it, old servers ignore it (the wire has no
|
|
5207
|
+
* `deny_unknown_fields`). The join layer between AI spend and shipped work. */
|
|
5208
|
+
session_metadata: external_exports.record(external_exports.string(), SessionMetadata).optional()
|
|
4814
5209
|
});
|
|
4815
5210
|
HeartbeatPayload = external_exports.object({
|
|
4816
5211
|
device_id: external_exports.string(),
|
|
@@ -4918,6 +5313,7 @@ var init_src = __esm({
|
|
|
4918
5313
|
init_redact();
|
|
4919
5314
|
init_redact_floor();
|
|
4920
5315
|
init_schemas();
|
|
5316
|
+
init_session_metadata();
|
|
4921
5317
|
}
|
|
4922
5318
|
});
|
|
4923
5319
|
|
|
@@ -5133,6 +5529,22 @@ function extractExcerpt(content) {
|
|
|
5133
5529
|
const truncated = cleaned.slice(0, 320);
|
|
5134
5530
|
return truncated.length > 0 ? truncated : void 0;
|
|
5135
5531
|
}
|
|
5532
|
+
function collectRefText(content) {
|
|
5533
|
+
if (!content) return "";
|
|
5534
|
+
let text = "";
|
|
5535
|
+
if (typeof content === "string") {
|
|
5536
|
+
text = content;
|
|
5537
|
+
} else if (Array.isArray(content)) {
|
|
5538
|
+
const parts = [];
|
|
5539
|
+
for (const block of content) {
|
|
5540
|
+
if (block && block.type === "text" && typeof block.text === "string") {
|
|
5541
|
+
parts.push(block.text);
|
|
5542
|
+
}
|
|
5543
|
+
}
|
|
5544
|
+
text = parts.join(" ");
|
|
5545
|
+
}
|
|
5546
|
+
return text.length > 64e3 ? text.slice(0, 64e3) : text;
|
|
5547
|
+
}
|
|
5136
5548
|
function buildToolCallDraft(opts) {
|
|
5137
5549
|
const { server, name } = splitObservedToolName(opts.observedName);
|
|
5138
5550
|
const hashes = hashArgs(opts.input);
|
|
@@ -5270,6 +5682,7 @@ async function parseClaudeCodeJsonl(ctx) {
|
|
|
5270
5682
|
}
|
|
5271
5683
|
const slug = guessRepoSlugFromPath(cwd);
|
|
5272
5684
|
const excerpt = extractExcerpt(a.message?.content);
|
|
5685
|
+
const refs = detectEventReferences(collectRefText(a.message?.content));
|
|
5273
5686
|
const aggregate = {};
|
|
5274
5687
|
const blocks = Array.isArray(a.message?.content) ? a.message.content : [];
|
|
5275
5688
|
let callIndex = 0;
|
|
@@ -5309,9 +5722,14 @@ async function parseClaudeCodeJsonl(ctx) {
|
|
|
5309
5722
|
turn_index: null,
|
|
5310
5723
|
parent_event_id: a.parentUuid ?? null,
|
|
5311
5724
|
cwd,
|
|
5312
|
-
git
|
|
5725
|
+
// Emit git context whenever there is ANY signal — a repo slug OR the
|
|
5726
|
+
// session's branch. Gating on `slug` alone dropped the branch (and all
|
|
5727
|
+
// git context) for cwds the path heuristic doesn't match (e.g.
|
|
5728
|
+
// ~/Documents/<repo>), starving the session-metadata join. The branch
|
|
5729
|
+
// is the historical one Claude Code recorded for the turn.
|
|
5730
|
+
git: slug || gitBranch ? {
|
|
5313
5731
|
remote_url: null,
|
|
5314
|
-
remote_host: slug
|
|
5732
|
+
remote_host: slug?.includes("/") ? "github.com" : null,
|
|
5315
5733
|
remote_slug: slug,
|
|
5316
5734
|
branch: gitBranch,
|
|
5317
5735
|
commit_sha: null
|
|
@@ -5327,6 +5745,7 @@ async function parseClaudeCodeJsonl(ctx) {
|
|
|
5327
5745
|
tool_calls: aggregate,
|
|
5328
5746
|
files_touched: [],
|
|
5329
5747
|
...excerpt ? { content_excerpt: excerpt } : {},
|
|
5748
|
+
...refs ? { references: refs } : {},
|
|
5330
5749
|
source_file: ctx.sourceFile,
|
|
5331
5750
|
source_byte_offset: offsetAtLineStart,
|
|
5332
5751
|
// Files in ~/.claude/projects/ come from the Claude Code app
|
|
@@ -5361,6 +5780,7 @@ async function parseClaudeCodeJsonl(ctx) {
|
|
|
5361
5780
|
continue;
|
|
5362
5781
|
}
|
|
5363
5782
|
const excerpt = extractExcerpt(u.message?.content);
|
|
5783
|
+
const refs = detectEventReferences(collectRefText(u.message?.content));
|
|
5364
5784
|
await emit({
|
|
5365
5785
|
source_event_id: eventId,
|
|
5366
5786
|
ts: u.timestamp,
|
|
@@ -5378,6 +5798,7 @@ async function parseClaudeCodeJsonl(ctx) {
|
|
|
5378
5798
|
tool_calls: {},
|
|
5379
5799
|
files_touched: [],
|
|
5380
5800
|
...excerpt ? { content_excerpt: excerpt } : {},
|
|
5801
|
+
...refs ? { references: refs } : {},
|
|
5381
5802
|
source_file: ctx.sourceFile,
|
|
5382
5803
|
source_byte_offset: offsetAtLineStart,
|
|
5383
5804
|
billing: "subscription"
|
|
@@ -29466,8 +29887,8 @@ var require_cache3 = __commonJS({
|
|
|
29466
29887
|
* @returns {requestResponseList}
|
|
29467
29888
|
*/
|
|
29468
29889
|
#batchCacheOperations(operations) {
|
|
29469
|
-
const
|
|
29470
|
-
const backupCache = [...
|
|
29890
|
+
const cache2 = this.#relevantRequestResponseList;
|
|
29891
|
+
const backupCache = [...cache2];
|
|
29471
29892
|
const addedItems = [];
|
|
29472
29893
|
const resultList = [];
|
|
29473
29894
|
try {
|
|
@@ -29494,9 +29915,9 @@ var require_cache3 = __commonJS({
|
|
|
29494
29915
|
return [];
|
|
29495
29916
|
}
|
|
29496
29917
|
for (const requestResponse of requestResponses) {
|
|
29497
|
-
const idx =
|
|
29918
|
+
const idx = cache2.indexOf(requestResponse);
|
|
29498
29919
|
assert2(idx !== -1);
|
|
29499
|
-
|
|
29920
|
+
cache2.splice(idx, 1);
|
|
29500
29921
|
}
|
|
29501
29922
|
} else if (operation.type === "put") {
|
|
29502
29923
|
if (operation.response == null) {
|
|
@@ -29526,11 +29947,11 @@ var require_cache3 = __commonJS({
|
|
|
29526
29947
|
}
|
|
29527
29948
|
requestResponses = this.#queryCache(operation.request);
|
|
29528
29949
|
for (const requestResponse of requestResponses) {
|
|
29529
|
-
const idx =
|
|
29950
|
+
const idx = cache2.indexOf(requestResponse);
|
|
29530
29951
|
assert2(idx !== -1);
|
|
29531
|
-
|
|
29952
|
+
cache2.splice(idx, 1);
|
|
29532
29953
|
}
|
|
29533
|
-
|
|
29954
|
+
cache2.push([operation.request, operation.response]);
|
|
29534
29955
|
addedItems.push([operation.request, operation.response]);
|
|
29535
29956
|
}
|
|
29536
29957
|
resultList.push([operation.request, operation.response]);
|
|
@@ -29707,13 +30128,13 @@ var require_cachestorage = __commonJS({
|
|
|
29707
30128
|
if (options.cacheName != null) {
|
|
29708
30129
|
if (this.#caches.has(options.cacheName)) {
|
|
29709
30130
|
const cacheList = this.#caches.get(options.cacheName);
|
|
29710
|
-
const
|
|
29711
|
-
return await
|
|
30131
|
+
const cache2 = new Cache(kConstruct, cacheList);
|
|
30132
|
+
return await cache2.match(request3, options);
|
|
29712
30133
|
}
|
|
29713
30134
|
} else {
|
|
29714
30135
|
for (const cacheList of this.#caches.values()) {
|
|
29715
|
-
const
|
|
29716
|
-
const response = await
|
|
30136
|
+
const cache2 = new Cache(kConstruct, cacheList);
|
|
30137
|
+
const response = await cache2.match(request3, options);
|
|
29717
30138
|
if (response !== void 0) {
|
|
29718
30139
|
return response;
|
|
29719
30140
|
}
|
|
@@ -29743,12 +30164,12 @@ var require_cachestorage = __commonJS({
|
|
|
29743
30164
|
webidl.argumentLengthCheck(arguments, 1, prefix);
|
|
29744
30165
|
cacheName = webidl.converters.DOMString(cacheName, prefix, "cacheName");
|
|
29745
30166
|
if (this.#caches.has(cacheName)) {
|
|
29746
|
-
const
|
|
29747
|
-
return new Cache(kConstruct,
|
|
30167
|
+
const cache3 = this.#caches.get(cacheName);
|
|
30168
|
+
return new Cache(kConstruct, cache3);
|
|
29748
30169
|
}
|
|
29749
|
-
const
|
|
29750
|
-
this.#caches.set(cacheName,
|
|
29751
|
-
return new Cache(kConstruct,
|
|
30170
|
+
const cache2 = [];
|
|
30171
|
+
this.#caches.set(cacheName, cache2);
|
|
30172
|
+
return new Cache(kConstruct, cache2);
|
|
29752
30173
|
}
|
|
29753
30174
|
/**
|
|
29754
30175
|
* @see https://w3c.github.io/ServiceWorker/#cache-storage-delete
|
|
@@ -43156,7 +43577,7 @@ var require_range = __commonJS({
|
|
|
43156
43577
|
parseRange(range) {
|
|
43157
43578
|
const memoOpts = (this.options.includePrerelease && FLAG_INCLUDE_PRERELEASE) | (this.options.loose && FLAG_LOOSE);
|
|
43158
43579
|
const memoKey = memoOpts + ":" + range;
|
|
43159
|
-
const cached2 =
|
|
43580
|
+
const cached2 = cache2.get(memoKey);
|
|
43160
43581
|
if (cached2) {
|
|
43161
43582
|
return cached2;
|
|
43162
43583
|
}
|
|
@@ -43190,7 +43611,7 @@ var require_range = __commonJS({
|
|
|
43190
43611
|
rangeMap.delete("");
|
|
43191
43612
|
}
|
|
43192
43613
|
const result2 = [...rangeMap.values()];
|
|
43193
|
-
|
|
43614
|
+
cache2.set(memoKey, result2);
|
|
43194
43615
|
return result2;
|
|
43195
43616
|
}
|
|
43196
43617
|
intersects(range, options) {
|
|
@@ -43229,7 +43650,7 @@ var require_range = __commonJS({
|
|
|
43229
43650
|
};
|
|
43230
43651
|
module.exports = Range;
|
|
43231
43652
|
var LRU = require_lrucache();
|
|
43232
|
-
var
|
|
43653
|
+
var cache2 = new LRU();
|
|
43233
43654
|
var parseOptions = require_parse_options();
|
|
43234
43655
|
var Comparator = require_comparator();
|
|
43235
43656
|
var debug = require_debug();
|
|
@@ -45301,6 +45722,38 @@ var init_prompts = __esm({
|
|
|
45301
45722
|
}
|
|
45302
45723
|
});
|
|
45303
45724
|
|
|
45725
|
+
// ../../packages/companion-core/src/pipeline/script-summary.ts
|
|
45726
|
+
function buildScriptSummaryUserPrompt(input) {
|
|
45727
|
+
const body = input.content.slice(0, SCRIPT_SUMMARY_INPUT_MAX_CHARS);
|
|
45728
|
+
return [
|
|
45729
|
+
`Script reference: ${input.ref}`,
|
|
45730
|
+
"Contents:",
|
|
45731
|
+
"```",
|
|
45732
|
+
body,
|
|
45733
|
+
"```",
|
|
45734
|
+
"",
|
|
45735
|
+
"One sentence (\u2264200 chars): what does running this script do?"
|
|
45736
|
+
].join("\n");
|
|
45737
|
+
}
|
|
45738
|
+
var SCRIPT_SUMMARY_OUTPUT_MAX_CHARS, SCRIPT_SUMMARY_TEMPERATURE, SCRIPT_SUMMARY_MAX_TOKENS, SCRIPT_SUMMARY_INPUT_MAX_CHARS, SCRIPT_SUMMARY_SYSTEM_PROMPT;
|
|
45739
|
+
var init_script_summary = __esm({
|
|
45740
|
+
"../../packages/companion-core/src/pipeline/script-summary.ts"() {
|
|
45741
|
+
"use strict";
|
|
45742
|
+
SCRIPT_SUMMARY_OUTPUT_MAX_CHARS = 200;
|
|
45743
|
+
SCRIPT_SUMMARY_TEMPERATURE = 0.2;
|
|
45744
|
+
SCRIPT_SUMMARY_MAX_TOKENS = 120;
|
|
45745
|
+
SCRIPT_SUMMARY_INPUT_MAX_CHARS = 6e3;
|
|
45746
|
+
SCRIPT_SUMMARY_SYSTEM_PROMPT = `You summarise what a script file does, for an engineer scanning a dashboard.
|
|
45747
|
+
|
|
45748
|
+
Rules:
|
|
45749
|
+
- Output ONE plain sentence (at most 200 characters) stating what the script DOES when it runs.
|
|
45750
|
+
- Be concrete and factual: the actions it performs and the systems it touches.
|
|
45751
|
+
- No preamble ("This script\u2026"), no markdown, no quotes, no line breaks.
|
|
45752
|
+
- Do not invent behaviour that is not in the file. If the file is trivial or unreadable, say so briefly.
|
|
45753
|
+
- Never include secrets, tokens, passwords, or personal data, even if they appear in the file.`;
|
|
45754
|
+
}
|
|
45755
|
+
});
|
|
45756
|
+
|
|
45304
45757
|
// ../../packages/companion-core/src/pipeline/title.ts
|
|
45305
45758
|
function buildTitleUserPrompt(input) {
|
|
45306
45759
|
const lines = input.abstracts.map(
|
|
@@ -45384,35 +45837,113 @@ var init_title = __esm({
|
|
|
45384
45837
|
}
|
|
45385
45838
|
});
|
|
45386
45839
|
|
|
45387
|
-
// ../../packages/companion-core/src/pipeline/
|
|
45388
|
-
function
|
|
45389
|
-
const
|
|
45390
|
-
return
|
|
45391
|
-
|
|
45392
|
-
|
|
45393
|
-
|
|
45394
|
-
body,
|
|
45395
|
-
"```",
|
|
45396
|
-
"",
|
|
45397
|
-
"One sentence (\u2264200 chars): what does running this script do?"
|
|
45398
|
-
].join("\n");
|
|
45840
|
+
// ../../packages/companion-core/src/pipeline/session-metadata.ts
|
|
45841
|
+
function buildLinkExtractUserPrompt(abstracts) {
|
|
45842
|
+
const lines = abstracts.map((a, i) => ` [part ${i + 1}] ${a.replace(/\s+/g, " ").trim().slice(0, 240)}`).join("\n");
|
|
45843
|
+
return `Summaries of the session's parts:
|
|
45844
|
+
${lines}
|
|
45845
|
+
|
|
45846
|
+
List the references, one per line, or "none".`;
|
|
45399
45847
|
}
|
|
45400
|
-
|
|
45401
|
-
|
|
45402
|
-
|
|
45848
|
+
function groupBy(items, keyOf) {
|
|
45849
|
+
const out = /* @__PURE__ */ new Map();
|
|
45850
|
+
for (const item of items) {
|
|
45851
|
+
const key = keyOf(item);
|
|
45852
|
+
const arr = out.get(key) ?? [];
|
|
45853
|
+
arr.push(item);
|
|
45854
|
+
out.set(key, arr);
|
|
45855
|
+
}
|
|
45856
|
+
return out;
|
|
45857
|
+
}
|
|
45858
|
+
async function buildSessionMetadata(segments, events, opts = {}) {
|
|
45859
|
+
const eventsBySession = groupBy(events, (e) => e.session_id);
|
|
45860
|
+
const segsBySession = groupBy(segments, (s) => s.session_id);
|
|
45861
|
+
const sessionIds = /* @__PURE__ */ new Set([...eventsBySession.keys(), ...segsBySession.keys()]);
|
|
45862
|
+
const out = {};
|
|
45863
|
+
for (const sessionId of sessionIds) {
|
|
45864
|
+
try {
|
|
45865
|
+
const evs = eventsBySession.get(sessionId) ?? [];
|
|
45866
|
+
const segs = segsBySession.get(sessionId) ?? [];
|
|
45867
|
+
const parts = [];
|
|
45868
|
+
const cwds = /* @__PURE__ */ new Set();
|
|
45869
|
+
for (const e of evs) {
|
|
45870
|
+
if (e.cwd) cwds.add(e.cwd);
|
|
45871
|
+
if (!e.git) continue;
|
|
45872
|
+
const refs = emptyDetectedRefs();
|
|
45873
|
+
if (e.git.remote_slug) {
|
|
45874
|
+
refs.repos.push({
|
|
45875
|
+
host: e.git.remote_host ?? null,
|
|
45876
|
+
slug: e.git.remote_slug,
|
|
45877
|
+
branches: e.git.branch ? [e.git.branch] : [],
|
|
45878
|
+
source: "git"
|
|
45879
|
+
});
|
|
45880
|
+
}
|
|
45881
|
+
if (e.git.branch) refs.issues.push(...detectBranchTickets(e.git.branch));
|
|
45882
|
+
if (e.git.commit_sha) {
|
|
45883
|
+
refs.commits.push({
|
|
45884
|
+
sha: e.git.commit_sha,
|
|
45885
|
+
slug: e.git.remote_slug ?? null,
|
|
45886
|
+
url: null,
|
|
45887
|
+
source: "git",
|
|
45888
|
+
confidence: 0.6
|
|
45889
|
+
});
|
|
45890
|
+
}
|
|
45891
|
+
parts.push(refs);
|
|
45892
|
+
}
|
|
45893
|
+
if (opts.resolveGit) {
|
|
45894
|
+
for (const cwd of cwds) {
|
|
45895
|
+
let g = null;
|
|
45896
|
+
try {
|
|
45897
|
+
g = await opts.resolveGit(cwd);
|
|
45898
|
+
} catch {
|
|
45899
|
+
g = null;
|
|
45900
|
+
}
|
|
45901
|
+
if (!g?.remote_slug) continue;
|
|
45902
|
+
const refs = emptyDetectedRefs();
|
|
45903
|
+
refs.repos.push({
|
|
45904
|
+
host: g.remote_host ?? null,
|
|
45905
|
+
slug: g.remote_slug,
|
|
45906
|
+
branches: g.branch ? [g.branch] : [],
|
|
45907
|
+
source: "git"
|
|
45908
|
+
});
|
|
45909
|
+
if (g.branch) refs.issues.push(...detectBranchTickets(g.branch));
|
|
45910
|
+
parts.push(refs);
|
|
45911
|
+
}
|
|
45912
|
+
}
|
|
45913
|
+
for (const e of evs) {
|
|
45914
|
+
if (e.references) parts.push(e.references);
|
|
45915
|
+
}
|
|
45916
|
+
for (const e of evs) {
|
|
45917
|
+
if (e.content_excerpt) parts.push(detectReferences(e.content_excerpt, "content"));
|
|
45918
|
+
}
|
|
45919
|
+
const abstracts = [...segs].sort((a, b) => a.started_at.localeCompare(b.started_at)).map((s) => stripCognitionSuffix(s.abstract)).filter((a) => a.length > 0);
|
|
45920
|
+
for (const a of abstracts) parts.push(detectReferences(a, "content"));
|
|
45921
|
+
if (opts.extractLinks && abstracts.length > 0) {
|
|
45922
|
+
try {
|
|
45923
|
+
const reply = await opts.extractLinks({
|
|
45924
|
+
abstracts: sampleAbstracts(abstracts, LINK_EXTRACT_MAX_ABSTRACTS)
|
|
45925
|
+
});
|
|
45926
|
+
if (reply) parts.push(detectReferences(reply, "model"));
|
|
45927
|
+
} catch {
|
|
45928
|
+
}
|
|
45929
|
+
}
|
|
45930
|
+
const meta = dedupeSessionMetadata(parts);
|
|
45931
|
+
if (!isEmptySessionMetadata(meta)) out[sessionId] = meta;
|
|
45932
|
+
} catch {
|
|
45933
|
+
}
|
|
45934
|
+
}
|
|
45935
|
+
return out;
|
|
45936
|
+
}
|
|
45937
|
+
var LINK_EXTRACT_SYSTEM_PROMPT, LINK_EXTRACT_MAX_TOKENS, LINK_EXTRACT_TEMPERATURE, LINK_EXTRACT_MAX_ABSTRACTS;
|
|
45938
|
+
var init_session_metadata2 = __esm({
|
|
45939
|
+
"../../packages/companion-core/src/pipeline/session-metadata.ts"() {
|
|
45403
45940
|
"use strict";
|
|
45404
|
-
|
|
45405
|
-
|
|
45406
|
-
|
|
45407
|
-
|
|
45408
|
-
|
|
45409
|
-
|
|
45410
|
-
Rules:
|
|
45411
|
-
- Output ONE plain sentence (at most 200 characters) stating what the script DOES when it runs.
|
|
45412
|
-
- Be concrete and factual: the actions it performs and the systems it touches.
|
|
45413
|
-
- No preamble ("This script\u2026"), no markdown, no quotes, no line breaks.
|
|
45414
|
-
- Do not invent behaviour that is not in the file. If the file is trivial or unreadable, say so briefly.
|
|
45415
|
-
- Never include secrets, tokens, passwords, or personal data, even if they appear in the file.`;
|
|
45941
|
+
init_session_metadata();
|
|
45942
|
+
init_title();
|
|
45943
|
+
LINK_EXTRACT_SYSTEM_PROMPT = "You extract code-collaboration references from one-sentence summaries of an AI-coding session. Report ONLY references that explicitly appear in the text: pull/merge request URLs, issue URLs, commit URLs, the `org/repo#123` shorthand, and ticket keys like ENG-123 or PROJ-4567. Output one reference per line, verbatim, nothing else \u2014 no prose, no numbering, no markdown. If the summaries contain no such reference, reply with exactly `none`. Never invent a reference, a repo, a number, or a URL that is not present in the text.";
|
|
45944
|
+
LINK_EXTRACT_MAX_TOKENS = 120;
|
|
45945
|
+
LINK_EXTRACT_TEMPERATURE = 0.1;
|
|
45946
|
+
LINK_EXTRACT_MAX_ABSTRACTS = 12;
|
|
45416
45947
|
}
|
|
45417
45948
|
});
|
|
45418
45949
|
|
|
@@ -45727,8 +46258,9 @@ var init_pipeline = __esm({
|
|
|
45727
46258
|
init_redact();
|
|
45728
46259
|
init_cognition();
|
|
45729
46260
|
init_prompts();
|
|
45730
|
-
init_title();
|
|
45731
46261
|
init_script_summary();
|
|
46262
|
+
init_session_metadata2();
|
|
46263
|
+
init_title();
|
|
45732
46264
|
SEGMENT_TIME_GAP_MS = 15 * 6e4;
|
|
45733
46265
|
SEGMENT_TOPIC_THRESHOLD = 0.35;
|
|
45734
46266
|
SEGMENT_MAX_TURNS = 100;
|
|
@@ -45875,116 +46407,6 @@ var init_file_queue_store = __esm({
|
|
|
45875
46407
|
}
|
|
45876
46408
|
});
|
|
45877
46409
|
|
|
45878
|
-
// ../../packages/companion-core/src/node/ollama.ts
|
|
45879
|
-
function defaultOllamaConfig() {
|
|
45880
|
-
const base = globalThis.process?.env ?? {};
|
|
45881
|
-
return {
|
|
45882
|
-
baseUrl: base.OLLAMA_URL ?? "http://localhost:11434",
|
|
45883
|
-
embedModel: base.OLLAMA_EMBED_MODEL ?? OLLAMA_EMBED_MODEL,
|
|
45884
|
-
chatModel: base.OLLAMA_CHAT_MODEL ?? OLLAMA_CHAT_MODEL
|
|
45885
|
-
};
|
|
45886
|
-
}
|
|
45887
|
-
function ollamaEmbed(cfg = defaultOllamaConfig()) {
|
|
45888
|
-
return async (text) => {
|
|
45889
|
-
const res = await fetch(`${cfg.baseUrl.replace(/\/+$/, "")}/api/embeddings`, {
|
|
45890
|
-
method: "POST",
|
|
45891
|
-
headers: { "content-type": "application/json" },
|
|
45892
|
-
body: JSON.stringify({ model: cfg.embedModel, prompt: text })
|
|
45893
|
-
});
|
|
45894
|
-
if (!res.ok) {
|
|
45895
|
-
throw new Error(`ollama embed failed: ${res.status} ${await res.text().catch(() => "")}`);
|
|
45896
|
-
}
|
|
45897
|
-
const body = await res.json();
|
|
45898
|
-
const v = body.embedding ?? [];
|
|
45899
|
-
let norm = 0;
|
|
45900
|
-
for (const x of v) norm += x * x;
|
|
45901
|
-
norm = Math.sqrt(norm) || 1;
|
|
45902
|
-
return v.map((x) => x / norm);
|
|
45903
|
-
};
|
|
45904
|
-
}
|
|
45905
|
-
function ollamaTokenize() {
|
|
45906
|
-
return (text) => Math.max(1, Math.ceil(text.length / QWEN_CHARS_PER_TOKEN));
|
|
45907
|
-
}
|
|
45908
|
-
function ollamaSummarize(cfg = defaultOllamaConfig()) {
|
|
45909
|
-
return async ({ prompt, maxTokens }) => {
|
|
45910
|
-
const res = await fetch(`${cfg.baseUrl.replace(/\/+$/, "")}/api/chat`, {
|
|
45911
|
-
method: "POST",
|
|
45912
|
-
headers: { "content-type": "application/json" },
|
|
45913
|
-
body: JSON.stringify({
|
|
45914
|
-
model: cfg.chatModel,
|
|
45915
|
-
stream: false,
|
|
45916
|
-
// Disable reasoning. qwen3 (the default summariser family) is a
|
|
45917
|
-
// thinking model: with `think` on it spends the entire
|
|
45918
|
-
// `num_predict` budget on a <think> block and returns EMPTY
|
|
45919
|
-
// content, so the summariser saw "" and the whole pipeline
|
|
45920
|
-
// crash-looped at preflight. We only want the final terse
|
|
45921
|
-
// abstract, never the chain-of-thought. Ollama ignores this
|
|
45922
|
-
// field for non-thinking models, so it's safe across families.
|
|
45923
|
-
think: false,
|
|
45924
|
-
options: {
|
|
45925
|
-
temperature: SUMMARISER_TEMPERATURE,
|
|
45926
|
-
num_predict: Math.min(maxTokens, SUMMARISER_MAX_TOKENS)
|
|
45927
|
-
},
|
|
45928
|
-
messages: [
|
|
45929
|
-
{ role: "system", content: SUMMARISER_SYSTEM_PROMPT },
|
|
45930
|
-
{ role: "user", content: prompt }
|
|
45931
|
-
]
|
|
45932
|
-
})
|
|
45933
|
-
});
|
|
45934
|
-
if (!res.ok) {
|
|
45935
|
-
throw new Error(`ollama summarize failed: ${res.status} ${await res.text().catch(() => "")}`);
|
|
45936
|
-
}
|
|
45937
|
-
const body = await res.json();
|
|
45938
|
-
return (body.message?.content ?? "").trim().slice(0, 240);
|
|
45939
|
-
};
|
|
45940
|
-
}
|
|
45941
|
-
function ollamaCognize(cfg = defaultOllamaConfig()) {
|
|
45942
|
-
return async ({ abstract }) => {
|
|
45943
|
-
if (!abstract || abstract.trim().length < 12) return null;
|
|
45944
|
-
let res;
|
|
45945
|
-
try {
|
|
45946
|
-
res = await fetch(`${cfg.baseUrl.replace(/\/+$/, "")}/api/chat`, {
|
|
45947
|
-
method: "POST",
|
|
45948
|
-
headers: { "content-type": "application/json" },
|
|
45949
|
-
body: JSON.stringify({
|
|
45950
|
-
model: cfg.chatModel,
|
|
45951
|
-
stream: false,
|
|
45952
|
-
format: "json",
|
|
45953
|
-
// Same reason as the summariser: no thinking budget, just the
|
|
45954
|
-
// JSON cognition tags. Thinking models otherwise emit a long
|
|
45955
|
-
// <think> block and return empty content.
|
|
45956
|
-
think: false,
|
|
45957
|
-
options: {
|
|
45958
|
-
temperature: COGNITION_TEMPERATURE,
|
|
45959
|
-
num_predict: COGNITION_MAX_TOKENS
|
|
45960
|
-
},
|
|
45961
|
-
messages: [
|
|
45962
|
-
{ role: "system", content: COGNITION_SYSTEM_PROMPT },
|
|
45963
|
-
{ role: "user", content: buildCognitionUserPrompt(abstract) }
|
|
45964
|
-
]
|
|
45965
|
-
})
|
|
45966
|
-
});
|
|
45967
|
-
} catch {
|
|
45968
|
-
return null;
|
|
45969
|
-
}
|
|
45970
|
-
if (!res.ok) return null;
|
|
45971
|
-
let body;
|
|
45972
|
-
try {
|
|
45973
|
-
body = await res.json();
|
|
45974
|
-
} catch {
|
|
45975
|
-
return null;
|
|
45976
|
-
}
|
|
45977
|
-
return parseCognitionReply(body.message?.content ?? "");
|
|
45978
|
-
};
|
|
45979
|
-
}
|
|
45980
|
-
var init_ollama = __esm({
|
|
45981
|
-
"../../packages/companion-core/src/node/ollama.ts"() {
|
|
45982
|
-
"use strict";
|
|
45983
|
-
init_prompts();
|
|
45984
|
-
init_cognition();
|
|
45985
|
-
}
|
|
45986
|
-
});
|
|
45987
|
-
|
|
45988
46410
|
// ../../packages/companion-core/src/node/llama.ts
|
|
45989
46411
|
import { existsSync as existsSync7 } from "fs";
|
|
45990
46412
|
import { mkdir } from "fs/promises";
|
|
@@ -46113,7 +46535,14 @@ async function loadOnce(cfg) {
|
|
|
46113
46535
|
contextSequence: scriptContext.getSequence(),
|
|
46114
46536
|
systemPrompt: SCRIPT_SUMMARY_SYSTEM_PROMPT
|
|
46115
46537
|
});
|
|
46116
|
-
|
|
46538
|
+
const linkExtractContext = await model.createContext({
|
|
46539
|
+
contextSize: cfg.contextSize
|
|
46540
|
+
});
|
|
46541
|
+
const linkExtractor = new llamaMod.LlamaChatSession({
|
|
46542
|
+
contextSequence: linkExtractContext.getSequence(),
|
|
46543
|
+
systemPrompt: LINK_EXTRACT_SYSTEM_PROMPT
|
|
46544
|
+
});
|
|
46545
|
+
loaded = { summarizer, cognizer, entitler, scriptSummarizer: scriptSummarizer2, linkExtractor };
|
|
46117
46546
|
return loaded;
|
|
46118
46547
|
})();
|
|
46119
46548
|
try {
|
|
@@ -46232,6 +46661,34 @@ function llamaScriptSummarize(cfg = defaultLlamaConfig()) {
|
|
|
46232
46661
|
}
|
|
46233
46662
|
};
|
|
46234
46663
|
}
|
|
46664
|
+
function llamaExtractLinks(cfg = defaultLlamaConfig()) {
|
|
46665
|
+
return async ({ abstracts }) => {
|
|
46666
|
+
if (abstracts.length === 0) return null;
|
|
46667
|
+
let loadedSessions;
|
|
46668
|
+
try {
|
|
46669
|
+
loadedSessions = await loadOnce(cfg);
|
|
46670
|
+
} catch {
|
|
46671
|
+
return null;
|
|
46672
|
+
}
|
|
46673
|
+
const { linkExtractor } = loadedSessions;
|
|
46674
|
+
const run = inflight.then(async () => {
|
|
46675
|
+
linkExtractor.resetChatHistory();
|
|
46676
|
+
const raw = await linkExtractor.prompt(buildLinkExtractUserPrompt(abstracts), {
|
|
46677
|
+
temperature: LINK_EXTRACT_TEMPERATURE,
|
|
46678
|
+
// Same thinking-budget rationale as cognition/title: the answer is a
|
|
46679
|
+
// few short lines but Qwen3.5 reasons first.
|
|
46680
|
+
maxTokens: LINK_EXTRACT_MAX_TOKENS + 400
|
|
46681
|
+
});
|
|
46682
|
+
return stripThinking(raw ?? "") || null;
|
|
46683
|
+
});
|
|
46684
|
+
inflight = run.catch(() => void 0);
|
|
46685
|
+
try {
|
|
46686
|
+
return await run;
|
|
46687
|
+
} catch {
|
|
46688
|
+
return null;
|
|
46689
|
+
}
|
|
46690
|
+
};
|
|
46691
|
+
}
|
|
46235
46692
|
var DEFAULT_LLAMA_MODEL_URL, LLAMA_MAX_TOKENS, loaded, loadPromise, inflight;
|
|
46236
46693
|
var init_llama = __esm({
|
|
46237
46694
|
"../../packages/companion-core/src/node/llama.ts"() {
|
|
@@ -46239,6 +46696,7 @@ var init_llama = __esm({
|
|
|
46239
46696
|
init_cognition();
|
|
46240
46697
|
init_prompts();
|
|
46241
46698
|
init_script_summary();
|
|
46699
|
+
init_session_metadata2();
|
|
46242
46700
|
init_title();
|
|
46243
46701
|
DEFAULT_LLAMA_MODEL_URL = "https://huggingface.co/lmstudio-community/Qwen3.5-4B-GGUF/resolve/main/Qwen3.5-4B-Q4_K_M.gguf";
|
|
46244
46702
|
LLAMA_MAX_TOKENS = 1024;
|
|
@@ -46248,6 +46706,116 @@ var init_llama = __esm({
|
|
|
46248
46706
|
}
|
|
46249
46707
|
});
|
|
46250
46708
|
|
|
46709
|
+
// ../../packages/companion-core/src/node/ollama.ts
|
|
46710
|
+
function defaultOllamaConfig() {
|
|
46711
|
+
const base = globalThis.process?.env ?? {};
|
|
46712
|
+
return {
|
|
46713
|
+
baseUrl: base.OLLAMA_URL ?? "http://localhost:11434",
|
|
46714
|
+
embedModel: base.OLLAMA_EMBED_MODEL ?? OLLAMA_EMBED_MODEL,
|
|
46715
|
+
chatModel: base.OLLAMA_CHAT_MODEL ?? OLLAMA_CHAT_MODEL
|
|
46716
|
+
};
|
|
46717
|
+
}
|
|
46718
|
+
function ollamaEmbed(cfg = defaultOllamaConfig()) {
|
|
46719
|
+
return async (text) => {
|
|
46720
|
+
const res = await fetch(`${cfg.baseUrl.replace(/\/+$/, "")}/api/embeddings`, {
|
|
46721
|
+
method: "POST",
|
|
46722
|
+
headers: { "content-type": "application/json" },
|
|
46723
|
+
body: JSON.stringify({ model: cfg.embedModel, prompt: text })
|
|
46724
|
+
});
|
|
46725
|
+
if (!res.ok) {
|
|
46726
|
+
throw new Error(`ollama embed failed: ${res.status} ${await res.text().catch(() => "")}`);
|
|
46727
|
+
}
|
|
46728
|
+
const body = await res.json();
|
|
46729
|
+
const v = body.embedding ?? [];
|
|
46730
|
+
let norm = 0;
|
|
46731
|
+
for (const x of v) norm += x * x;
|
|
46732
|
+
norm = Math.sqrt(norm) || 1;
|
|
46733
|
+
return v.map((x) => x / norm);
|
|
46734
|
+
};
|
|
46735
|
+
}
|
|
46736
|
+
function ollamaTokenize() {
|
|
46737
|
+
return (text) => Math.max(1, Math.ceil(text.length / QWEN_CHARS_PER_TOKEN));
|
|
46738
|
+
}
|
|
46739
|
+
function ollamaSummarize(cfg = defaultOllamaConfig()) {
|
|
46740
|
+
return async ({ prompt, maxTokens }) => {
|
|
46741
|
+
const res = await fetch(`${cfg.baseUrl.replace(/\/+$/, "")}/api/chat`, {
|
|
46742
|
+
method: "POST",
|
|
46743
|
+
headers: { "content-type": "application/json" },
|
|
46744
|
+
body: JSON.stringify({
|
|
46745
|
+
model: cfg.chatModel,
|
|
46746
|
+
stream: false,
|
|
46747
|
+
// Disable reasoning. qwen3 (the default summariser family) is a
|
|
46748
|
+
// thinking model: with `think` on it spends the entire
|
|
46749
|
+
// `num_predict` budget on a <think> block and returns EMPTY
|
|
46750
|
+
// content, so the summariser saw "" and the whole pipeline
|
|
46751
|
+
// crash-looped at preflight. We only want the final terse
|
|
46752
|
+
// abstract, never the chain-of-thought. Ollama ignores this
|
|
46753
|
+
// field for non-thinking models, so it's safe across families.
|
|
46754
|
+
think: false,
|
|
46755
|
+
options: {
|
|
46756
|
+
temperature: SUMMARISER_TEMPERATURE,
|
|
46757
|
+
num_predict: Math.min(maxTokens, SUMMARISER_MAX_TOKENS)
|
|
46758
|
+
},
|
|
46759
|
+
messages: [
|
|
46760
|
+
{ role: "system", content: SUMMARISER_SYSTEM_PROMPT },
|
|
46761
|
+
{ role: "user", content: prompt }
|
|
46762
|
+
]
|
|
46763
|
+
})
|
|
46764
|
+
});
|
|
46765
|
+
if (!res.ok) {
|
|
46766
|
+
throw new Error(`ollama summarize failed: ${res.status} ${await res.text().catch(() => "")}`);
|
|
46767
|
+
}
|
|
46768
|
+
const body = await res.json();
|
|
46769
|
+
return (body.message?.content ?? "").trim().slice(0, 240);
|
|
46770
|
+
};
|
|
46771
|
+
}
|
|
46772
|
+
function ollamaCognize(cfg = defaultOllamaConfig()) {
|
|
46773
|
+
return async ({ abstract }) => {
|
|
46774
|
+
if (!abstract || abstract.trim().length < 12) return null;
|
|
46775
|
+
let res;
|
|
46776
|
+
try {
|
|
46777
|
+
res = await fetch(`${cfg.baseUrl.replace(/\/+$/, "")}/api/chat`, {
|
|
46778
|
+
method: "POST",
|
|
46779
|
+
headers: { "content-type": "application/json" },
|
|
46780
|
+
body: JSON.stringify({
|
|
46781
|
+
model: cfg.chatModel,
|
|
46782
|
+
stream: false,
|
|
46783
|
+
format: "json",
|
|
46784
|
+
// Same reason as the summariser: no thinking budget, just the
|
|
46785
|
+
// JSON cognition tags. Thinking models otherwise emit a long
|
|
46786
|
+
// <think> block and return empty content.
|
|
46787
|
+
think: false,
|
|
46788
|
+
options: {
|
|
46789
|
+
temperature: COGNITION_TEMPERATURE,
|
|
46790
|
+
num_predict: COGNITION_MAX_TOKENS
|
|
46791
|
+
},
|
|
46792
|
+
messages: [
|
|
46793
|
+
{ role: "system", content: COGNITION_SYSTEM_PROMPT },
|
|
46794
|
+
{ role: "user", content: buildCognitionUserPrompt(abstract) }
|
|
46795
|
+
]
|
|
46796
|
+
})
|
|
46797
|
+
});
|
|
46798
|
+
} catch {
|
|
46799
|
+
return null;
|
|
46800
|
+
}
|
|
46801
|
+
if (!res.ok) return null;
|
|
46802
|
+
let body;
|
|
46803
|
+
try {
|
|
46804
|
+
body = await res.json();
|
|
46805
|
+
} catch {
|
|
46806
|
+
return null;
|
|
46807
|
+
}
|
|
46808
|
+
return parseCognitionReply(body.message?.content ?? "");
|
|
46809
|
+
};
|
|
46810
|
+
}
|
|
46811
|
+
var init_ollama = __esm({
|
|
46812
|
+
"../../packages/companion-core/src/node/ollama.ts"() {
|
|
46813
|
+
"use strict";
|
|
46814
|
+
init_prompts();
|
|
46815
|
+
init_cognition();
|
|
46816
|
+
}
|
|
46817
|
+
});
|
|
46818
|
+
|
|
46251
46819
|
// ../../packages/companion-core/src/optional-module.ts
|
|
46252
46820
|
function isMissingOptionalModuleError(err) {
|
|
46253
46821
|
const code = err?.code;
|
|
@@ -46346,6 +46914,7 @@ __export(node_exports, {
|
|
|
46346
46914
|
ensureLlamaModel: () => ensureLlamaModel,
|
|
46347
46915
|
llamaCognize: () => llamaCognize,
|
|
46348
46916
|
llamaEntitle: () => llamaEntitle,
|
|
46917
|
+
llamaExtractLinks: () => llamaExtractLinks,
|
|
46349
46918
|
llamaScriptSummarize: () => llamaScriptSummarize,
|
|
46350
46919
|
llamaSummarize: () => llamaSummarize,
|
|
46351
46920
|
ollamaCognize: () => ollamaCognize,
|
|
@@ -46357,9 +46926,8 @@ var init_node2 = __esm({
|
|
|
46357
46926
|
"../../packages/companion-core/src/node/index.ts"() {
|
|
46358
46927
|
"use strict";
|
|
46359
46928
|
init_file_queue_store();
|
|
46360
|
-
init_file_queue_store();
|
|
46361
|
-
init_ollama();
|
|
46362
46929
|
init_llama();
|
|
46930
|
+
init_ollama();
|
|
46363
46931
|
init_transformersjs_embed();
|
|
46364
46932
|
}
|
|
46365
46933
|
});
|
|
@@ -46548,6 +47116,7 @@ var init_enrich_scripts = __esm({
|
|
|
46548
47116
|
var pipeline_exports = {};
|
|
46549
47117
|
__export(pipeline_exports, {
|
|
46550
47118
|
buildSegments: () => buildSegments,
|
|
47119
|
+
buildSessionMetadata: () => buildSessionMetadata2,
|
|
46551
47120
|
buildSessionTitles: () => buildSessionTitles2,
|
|
46552
47121
|
enrichScripts: () => enrichScripts,
|
|
46553
47122
|
preflightSummariser: () => preflightSummariser
|
|
@@ -46571,6 +47140,13 @@ async function bundledAdapters() {
|
|
|
46571
47140
|
// sessions-list title in the dashboard). Best-effort like cognize:
|
|
46572
47141
|
// failures fall back to a deterministic title in buildSessionTitles.
|
|
46573
47142
|
entitle: llamaEntitle(llamaCfg),
|
|
47143
|
+
// Link-extraction pass — same bundled model, fifth chat session with
|
|
47144
|
+
// LINK_EXTRACT_SYSTEM_PROMPT. One short call per session that surfaces
|
|
47145
|
+
// PR/issue/commit references from the redacted abstracts, so detection
|
|
47146
|
+
// works even for clients whose logs carry no git data. Best-effort:
|
|
47147
|
+
// failures fall back to the deterministic git + content channels in
|
|
47148
|
+
// buildSessionMetadata.
|
|
47149
|
+
extractLinks: llamaExtractLinks(llamaCfg),
|
|
46574
47150
|
// Model-based PII redactor (OpenAI Privacy Filter via
|
|
46575
47151
|
// transformers.js / ONNX). Runs locally on CPU after the regex
|
|
46576
47152
|
// pass in packages/core/redact.ts. ~1 GB model downloaded on
|
|
@@ -46602,6 +47178,13 @@ async function buildSessionTitles2(segments) {
|
|
|
46602
47178
|
const a = await getAdapters();
|
|
46603
47179
|
return buildSessionTitles(segments, a.entitle);
|
|
46604
47180
|
}
|
|
47181
|
+
async function buildSessionMetadata2(segments, events) {
|
|
47182
|
+
const a = await getAdapters();
|
|
47183
|
+
return buildSessionMetadata(segments, events, {
|
|
47184
|
+
resolveGit: resolveGitContext,
|
|
47185
|
+
extractLinks: a.extractLinks
|
|
47186
|
+
});
|
|
47187
|
+
}
|
|
46605
47188
|
async function enrichScripts(drafts, contexts = []) {
|
|
46606
47189
|
if (contexts.length === 0 || drafts.length === 0) return;
|
|
46607
47190
|
await getAdapters();
|
|
@@ -46635,6 +47218,7 @@ var init_pipeline2 = __esm({
|
|
|
46635
47218
|
init_node2();
|
|
46636
47219
|
init_pipeline();
|
|
46637
47220
|
init_privacy_filter();
|
|
47221
|
+
init_src2();
|
|
46638
47222
|
init_enrich_scripts();
|
|
46639
47223
|
adapters = null;
|
|
46640
47224
|
MAX_SCRIPT_READ_BYTES = 64 * 1024;
|
|
@@ -46702,6 +47286,14 @@ async function scanAll(cb = {}) {
|
|
|
46702
47286
|
} catch (e) {
|
|
46703
47287
|
console.warn("codex scan skipped:", e.message);
|
|
46704
47288
|
}
|
|
47289
|
+
const ordered = (await Promise.all(
|
|
47290
|
+
jobs.map(async (j) => ({
|
|
47291
|
+
job: j,
|
|
47292
|
+
mtimeMs: (await stat2(j.path).catch(() => null))?.mtimeMs ?? 0
|
|
47293
|
+
}))
|
|
47294
|
+
)).sort((a, b) => b.mtimeMs - a.mtimeMs).map((x) => x.job);
|
|
47295
|
+
const MAX_FILES_PER_SCAN = 12;
|
|
47296
|
+
let morePending = false;
|
|
46705
47297
|
let filesScanned = 0;
|
|
46706
47298
|
let filesUnchanged = 0;
|
|
46707
47299
|
let batchesUploaded = 0;
|
|
@@ -46730,6 +47322,15 @@ async function scanAll(cb = {}) {
|
|
|
46730
47322
|
} catch (e) {
|
|
46731
47323
|
console.warn("session titling failed \u2014 shipping batch untitled:", e.message);
|
|
46732
47324
|
}
|
|
47325
|
+
let sessionMetadata = {};
|
|
47326
|
+
try {
|
|
47327
|
+
sessionMetadata = await buildSessionMetadata2(titleInput, events);
|
|
47328
|
+
} catch (e) {
|
|
47329
|
+
console.warn(
|
|
47330
|
+
"session metadata detection failed \u2014 shipping batch without it:",
|
|
47331
|
+
e.message
|
|
47332
|
+
);
|
|
47333
|
+
}
|
|
46733
47334
|
const callSegmentByEvent = /* @__PURE__ */ new Map();
|
|
46734
47335
|
for (const sessionId of new Set(toolCallBuffer.map((c) => c.session_id))) {
|
|
46735
47336
|
for (const seg of runSegmentsBySession.get(sessionId) ?? []) {
|
|
@@ -46743,7 +47344,8 @@ async function scanAll(cb = {}) {
|
|
|
46743
47344
|
events,
|
|
46744
47345
|
segments,
|
|
46745
47346
|
tool_calls: attachSegmentIdsByMap(toolCallBuffer, callSegmentByEvent),
|
|
46746
|
-
...Object.keys(sessionTitles).length ? { session_titles: sessionTitles } : {}
|
|
47347
|
+
...Object.keys(sessionTitles).length ? { session_titles: sessionTitles } : {},
|
|
47348
|
+
...Object.keys(sessionMetadata).length ? { session_metadata: sessionMetadata } : {}
|
|
46747
47349
|
};
|
|
46748
47350
|
cb.onUpload?.({ events: events.length, segments: segments.length });
|
|
46749
47351
|
const res = await uploadBatch(batch);
|
|
@@ -46773,9 +47375,9 @@ async function scanAll(cb = {}) {
|
|
|
46773
47375
|
toolCallBuffer.push(c);
|
|
46774
47376
|
}
|
|
46775
47377
|
};
|
|
46776
|
-
for (let i = 0; i <
|
|
46777
|
-
const job =
|
|
46778
|
-
cb.onFile?.(job.path, i,
|
|
47378
|
+
for (let i = 0; i < ordered.length; i++) {
|
|
47379
|
+
const job = ordered[i];
|
|
47380
|
+
cb.onFile?.(job.path, i, ordered.length);
|
|
46779
47381
|
const cur = state.getCursor(job.path);
|
|
46780
47382
|
const cs = await quickChecksum(job.path).catch(() => null);
|
|
46781
47383
|
if (cs && cur && cur.size === cs.size && cur.tailHash === cs.tailHash) {
|
|
@@ -46798,9 +47400,20 @@ async function scanAll(cb = {}) {
|
|
|
46798
47400
|
} catch (e) {
|
|
46799
47401
|
console.warn(` ! parse failed for ${job.path}:`, e.message);
|
|
46800
47402
|
}
|
|
47403
|
+
if (filesScanned >= MAX_FILES_PER_SCAN) {
|
|
47404
|
+
morePending = true;
|
|
47405
|
+
break;
|
|
47406
|
+
}
|
|
46801
47407
|
}
|
|
46802
47408
|
await flushBatch();
|
|
46803
|
-
return {
|
|
47409
|
+
return {
|
|
47410
|
+
filesScanned,
|
|
47411
|
+
filesUnchanged,
|
|
47412
|
+
batchesUploaded,
|
|
47413
|
+
eventsUploaded,
|
|
47414
|
+
segmentsUploaded,
|
|
47415
|
+
morePending
|
|
47416
|
+
};
|
|
46804
47417
|
}
|
|
46805
47418
|
var AGENT_VERSION, BATCH_MAX_EVENTS, BATCH_MAX_TOOL_CALLS, BATCH_BUFFER_HARD_CAP, ZERO_TOKENS;
|
|
46806
47419
|
var init_scan = __esm({
|
|
@@ -46813,7 +47426,7 @@ var init_scan = __esm({
|
|
|
46813
47426
|
init_api();
|
|
46814
47427
|
init_config2();
|
|
46815
47428
|
init_pipeline2();
|
|
46816
|
-
AGENT_VERSION = true ? "agent-0.0
|
|
47429
|
+
AGENT_VERSION = true ? "agent-0.1.0" : "agent-dev";
|
|
46817
47430
|
BATCH_MAX_EVENTS = INGEST_BATCH_MAX_EVENTS;
|
|
46818
47431
|
BATCH_MAX_TOOL_CALLS = 2e4;
|
|
46819
47432
|
BATCH_BUFFER_HARD_CAP = BATCH_MAX_EVENTS * 2;
|
|
@@ -49177,8 +49790,13 @@ async function runScanCycle(reason) {
|
|
|
49177
49790
|
});
|
|
49178
49791
|
bumpStat("files_scanned", r.filesScanned);
|
|
49179
49792
|
bumpStat("files_unchanged", r.filesUnchanged);
|
|
49180
|
-
|
|
49181
|
-
|
|
49793
|
+
if (r.morePending) {
|
|
49794
|
+
setPhase("processing", "Catching up on history\u2026");
|
|
49795
|
+
setTimeout(() => void requestScan("backfill"), 250);
|
|
49796
|
+
} else {
|
|
49797
|
+
setPhase("watching", "Waiting for new events");
|
|
49798
|
+
setProgress(0, 0);
|
|
49799
|
+
}
|
|
49182
49800
|
} catch (e) {
|
|
49183
49801
|
setStat("segments_sending", 0);
|
|
49184
49802
|
setPhase("offline", `Upload failed: ${describeErrorWithCause(e)}`);
|
|
@@ -49313,7 +49931,7 @@ var init_daemon = __esm({
|
|
|
49313
49931
|
init_machine_key();
|
|
49314
49932
|
init_scan();
|
|
49315
49933
|
init_single_flight();
|
|
49316
|
-
AGENT_VERSION2 = true ? "agent-0.0
|
|
49934
|
+
AGENT_VERSION2 = true ? "agent-0.1.0" : "agent-dev";
|
|
49317
49935
|
HEARTBEAT_INTERVAL_MS = 1e4;
|
|
49318
49936
|
SCAN_INTERVAL_MS = 5 * 60 * 1e3;
|
|
49319
49937
|
DISCOVERY_INTERVAL_MS = 6e4;
|
|
@@ -49527,6 +50145,8 @@ function installNativeRuntime(sourceCli) {
|
|
|
49527
50145
|
const childEnv = { ...process.env };
|
|
49528
50146
|
delete childEnv.npm_config_global;
|
|
49529
50147
|
delete childEnv.npm_config_prefix;
|
|
50148
|
+
process.stderr.write(` \xB7 staging summariser runtime (node-llama-cpp@${version})\u2026
|
|
50149
|
+
`);
|
|
49530
50150
|
const r = spawnSync2(
|
|
49531
50151
|
"npm",
|
|
49532
50152
|
[
|
|
@@ -49538,6 +50158,13 @@ function installNativeRuntime(sourceCli) {
|
|
|
49538
50158
|
"--omit=dev",
|
|
49539
50159
|
"--no-audit",
|
|
49540
50160
|
"--no-fund",
|
|
50161
|
+
// Prefer the npm cache the current install already populated (node-llama-cpp
|
|
50162
|
+
// is a direct dep, so the platform prebuilt is cached) — an offline ~3s copy
|
|
50163
|
+
// instead of a redundant network re-fetch. Only a genuine cache miss touches
|
|
50164
|
+
// the network, and a capped per-request timeout makes that fail fast (the
|
|
50165
|
+
// daemon's summariser preflight re-stages) rather than hang indefinitely.
|
|
50166
|
+
"--prefer-offline",
|
|
50167
|
+
"--fetch-timeout=60000",
|
|
49541
50168
|
"--loglevel=error",
|
|
49542
50169
|
`node-llama-cpp@${version}`
|
|
49543
50170
|
],
|
|
@@ -49906,7 +50533,7 @@ function tryOpenBrowser(url) {
|
|
|
49906
50533
|
return false;
|
|
49907
50534
|
}
|
|
49908
50535
|
}
|
|
49909
|
-
var AGENT_VERSION3 = true ? "agent-0.0
|
|
50536
|
+
var AGENT_VERSION3 = true ? "agent-0.1.0" : "agent-dev";
|
|
49910
50537
|
function osFamily() {
|
|
49911
50538
|
const p = platform5();
|
|
49912
50539
|
if (p === "darwin") return "macos";
|