modelstat 0.0.51 → 0.0.53
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 +870 -184
- package/dist/cli.mjs.map +1 -1
- package/package.json +4 -3
- package/vendor/ModelstatTray.app/Contents/Info.plist +42 -0
- package/vendor/ModelstatTray.app/Contents/MacOS/modelstat-tray +0 -0
- package/vendor/ModelstatTray.app/Contents/PkgInfo +1 -0
- package/vendor/ModelstatTray.app/Contents/_CodeSignature/CodeResources +115 -0
- package/vendor/tray-mac/Sources/ModelstatTray/main.swift +15 -6
- package/vendor/tray-mac/build-app.sh +53 -17
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;
|
|
@@ -46730,6 +47314,15 @@ async function scanAll(cb = {}) {
|
|
|
46730
47314
|
} catch (e) {
|
|
46731
47315
|
console.warn("session titling failed \u2014 shipping batch untitled:", e.message);
|
|
46732
47316
|
}
|
|
47317
|
+
let sessionMetadata = {};
|
|
47318
|
+
try {
|
|
47319
|
+
sessionMetadata = await buildSessionMetadata2(titleInput, events);
|
|
47320
|
+
} catch (e) {
|
|
47321
|
+
console.warn(
|
|
47322
|
+
"session metadata detection failed \u2014 shipping batch without it:",
|
|
47323
|
+
e.message
|
|
47324
|
+
);
|
|
47325
|
+
}
|
|
46733
47326
|
const callSegmentByEvent = /* @__PURE__ */ new Map();
|
|
46734
47327
|
for (const sessionId of new Set(toolCallBuffer.map((c) => c.session_id))) {
|
|
46735
47328
|
for (const seg of runSegmentsBySession.get(sessionId) ?? []) {
|
|
@@ -46743,7 +47336,8 @@ async function scanAll(cb = {}) {
|
|
|
46743
47336
|
events,
|
|
46744
47337
|
segments,
|
|
46745
47338
|
tool_calls: attachSegmentIdsByMap(toolCallBuffer, callSegmentByEvent),
|
|
46746
|
-
...Object.keys(sessionTitles).length ? { session_titles: sessionTitles } : {}
|
|
47339
|
+
...Object.keys(sessionTitles).length ? { session_titles: sessionTitles } : {},
|
|
47340
|
+
...Object.keys(sessionMetadata).length ? { session_metadata: sessionMetadata } : {}
|
|
46747
47341
|
};
|
|
46748
47342
|
cb.onUpload?.({ events: events.length, segments: segments.length });
|
|
46749
47343
|
const res = await uploadBatch(batch);
|
|
@@ -46813,7 +47407,7 @@ var init_scan = __esm({
|
|
|
46813
47407
|
init_api();
|
|
46814
47408
|
init_config2();
|
|
46815
47409
|
init_pipeline2();
|
|
46816
|
-
AGENT_VERSION = true ? "agent-0.0.
|
|
47410
|
+
AGENT_VERSION = true ? "agent-0.0.53" : "agent-dev";
|
|
46817
47411
|
BATCH_MAX_EVENTS = INGEST_BATCH_MAX_EVENTS;
|
|
46818
47412
|
BATCH_MAX_TOOL_CALLS = 2e4;
|
|
46819
47413
|
BATCH_BUFFER_HARD_CAP = BATCH_MAX_EVENTS * 2;
|
|
@@ -49313,7 +49907,7 @@ var init_daemon = __esm({
|
|
|
49313
49907
|
init_machine_key();
|
|
49314
49908
|
init_scan();
|
|
49315
49909
|
init_single_flight();
|
|
49316
|
-
AGENT_VERSION2 = true ? "agent-0.0.
|
|
49910
|
+
AGENT_VERSION2 = true ? "agent-0.0.53" : "agent-dev";
|
|
49317
49911
|
HEARTBEAT_INTERVAL_MS = 1e4;
|
|
49318
49912
|
SCAN_INTERVAL_MS = 5 * 60 * 1e3;
|
|
49319
49913
|
DISCOVERY_INTERVAL_MS = 6e4;
|
|
@@ -49438,12 +50032,12 @@ init_config2();
|
|
|
49438
50032
|
init_identity();
|
|
49439
50033
|
init_machine_key();
|
|
49440
50034
|
init_scan();
|
|
49441
|
-
import { spawn } from "child_process";
|
|
50035
|
+
import { spawn as spawn2 } from "child_process";
|
|
49442
50036
|
import { arch as cpuArch, hostname as hostname2, platform as platform5, release } from "os";
|
|
49443
50037
|
import { createInterface as createInterface3 } from "readline";
|
|
49444
50038
|
|
|
49445
50039
|
// src/service.ts
|
|
49446
|
-
import { spawnSync as spawnSync2 } from "child_process";
|
|
50040
|
+
import { spawn, spawnSync as spawnSync2 } from "child_process";
|
|
49447
50041
|
import {
|
|
49448
50042
|
copyFileSync,
|
|
49449
50043
|
existsSync as existsSync9,
|
|
@@ -49518,10 +50112,7 @@ function installNativeRuntime(sourceCli) {
|
|
|
49518
50112
|
const dest = binDir();
|
|
49519
50113
|
try {
|
|
49520
50114
|
const have = JSON.parse(
|
|
49521
|
-
readFileSync4(
|
|
49522
|
-
join7(dest, "node_modules", "node-llama-cpp", "package.json"),
|
|
49523
|
-
"utf8"
|
|
49524
|
-
)
|
|
50115
|
+
readFileSync4(join7(dest, "node_modules", "node-llama-cpp", "package.json"), "utf8")
|
|
49525
50116
|
);
|
|
49526
50117
|
if (have.version === version) return [`node-llama-cpp@${version} (cached)`];
|
|
49527
50118
|
} catch {
|
|
@@ -49530,6 +50121,8 @@ function installNativeRuntime(sourceCli) {
|
|
|
49530
50121
|
const childEnv = { ...process.env };
|
|
49531
50122
|
delete childEnv.npm_config_global;
|
|
49532
50123
|
delete childEnv.npm_config_prefix;
|
|
50124
|
+
process.stderr.write(` \xB7 staging summariser runtime (node-llama-cpp@${version})\u2026
|
|
50125
|
+
`);
|
|
49533
50126
|
const r = spawnSync2(
|
|
49534
50127
|
"npm",
|
|
49535
50128
|
[
|
|
@@ -49541,6 +50134,13 @@ function installNativeRuntime(sourceCli) {
|
|
|
49541
50134
|
"--omit=dev",
|
|
49542
50135
|
"--no-audit",
|
|
49543
50136
|
"--no-fund",
|
|
50137
|
+
// Prefer the npm cache the current install already populated (node-llama-cpp
|
|
50138
|
+
// is a direct dep, so the platform prebuilt is cached) — an offline ~3s copy
|
|
50139
|
+
// instead of a redundant network re-fetch. Only a genuine cache miss touches
|
|
50140
|
+
// the network, and a capped per-request timeout makes that fail fast (the
|
|
50141
|
+
// daemon's summariser preflight re-stages) rather than hang indefinitely.
|
|
50142
|
+
"--prefer-offline",
|
|
50143
|
+
"--fetch-timeout=60000",
|
|
49544
50144
|
"--loglevel=error",
|
|
49545
50145
|
`node-llama-cpp@${version}`
|
|
49546
50146
|
],
|
|
@@ -49757,7 +50357,7 @@ function installTrayApp(sourceAppPath) {
|
|
|
49757
50357
|
}
|
|
49758
50358
|
return { installedAt: dest };
|
|
49759
50359
|
}
|
|
49760
|
-
function bundledTrayAppPath() {
|
|
50360
|
+
async function bundledTrayAppPath(progress) {
|
|
49761
50361
|
if (platform3() !== "darwin") return null;
|
|
49762
50362
|
const here2 = dirname6(fileURLToPath2(import.meta.url));
|
|
49763
50363
|
const candidates = [
|
|
@@ -49769,22 +50369,53 @@ function bundledTrayAppPath() {
|
|
|
49769
50369
|
for (const c of candidates) {
|
|
49770
50370
|
if (existsSync9(c)) return c;
|
|
49771
50371
|
}
|
|
49772
|
-
const sourceDirs = [
|
|
49773
|
-
join7(here2, "..", "vendor", "tray-mac"),
|
|
49774
|
-
join7(here2, "..", "..", "tray-mac")
|
|
49775
|
-
];
|
|
50372
|
+
const sourceDirs = [join7(here2, "..", "vendor", "tray-mac"), join7(here2, "..", "..", "tray-mac")];
|
|
49776
50373
|
for (const src of sourceDirs) {
|
|
49777
50374
|
const build = join7(src, "build-app.sh");
|
|
49778
50375
|
if (!existsSync9(build)) continue;
|
|
49779
50376
|
if (!hasSwift()) return null;
|
|
49780
|
-
const
|
|
49781
|
-
if (
|
|
50377
|
+
const code = await runTrayBuild(src, build, progress);
|
|
50378
|
+
if (code === 0) {
|
|
49782
50379
|
const app = join7(src, "build", "ModelstatTray.app");
|
|
49783
50380
|
if (existsSync9(app)) return app;
|
|
49784
50381
|
}
|
|
49785
50382
|
}
|
|
49786
50383
|
return null;
|
|
49787
50384
|
}
|
|
50385
|
+
function createLineSplitter(onLine) {
|
|
50386
|
+
let buf = "";
|
|
50387
|
+
return {
|
|
50388
|
+
push(chunk) {
|
|
50389
|
+
buf += chunk;
|
|
50390
|
+
for (; ; ) {
|
|
50391
|
+
const nl = buf.indexOf("\n");
|
|
50392
|
+
if (nl === -1) break;
|
|
50393
|
+
const line = buf.slice(0, nl).trimEnd();
|
|
50394
|
+
buf = buf.slice(nl + 1);
|
|
50395
|
+
if (line) onLine(line);
|
|
50396
|
+
}
|
|
50397
|
+
},
|
|
50398
|
+
flush() {
|
|
50399
|
+
const line = buf.trim();
|
|
50400
|
+
buf = "";
|
|
50401
|
+
if (line) onLine(line);
|
|
50402
|
+
}
|
|
50403
|
+
};
|
|
50404
|
+
}
|
|
50405
|
+
function runTrayBuild(cwd, buildScript, progress) {
|
|
50406
|
+
return new Promise((resolve6) => {
|
|
50407
|
+
const child = spawn("bash", [buildScript], { cwd });
|
|
50408
|
+
const splitter = createLineSplitter((line) => progress?.onLine?.(line));
|
|
50409
|
+
const pump = (chunk) => splitter.push(chunk.toString("utf8"));
|
|
50410
|
+
child.stdout?.on("data", pump);
|
|
50411
|
+
child.stderr?.on("data", pump);
|
|
50412
|
+
child.on("error", () => resolve6(null));
|
|
50413
|
+
child.on("close", (code) => {
|
|
50414
|
+
splitter.flush();
|
|
50415
|
+
resolve6(code);
|
|
50416
|
+
});
|
|
50417
|
+
});
|
|
50418
|
+
}
|
|
49788
50419
|
function hasSwift() {
|
|
49789
50420
|
const r = spawnSync2("swift", ["--version"], { encoding: "utf8" });
|
|
49790
50421
|
return r.status === 0;
|
|
@@ -49868,7 +50499,7 @@ function tryOpenBrowser(url) {
|
|
|
49868
50499
|
const cmd = p === "darwin" ? "open" : p === "win32" ? "cmd" : "xdg-open";
|
|
49869
50500
|
const args = p === "win32" ? ["/c", "start", "", url] : [url];
|
|
49870
50501
|
try {
|
|
49871
|
-
const child =
|
|
50502
|
+
const child = spawn2(cmd, args, {
|
|
49872
50503
|
stdio: "ignore",
|
|
49873
50504
|
detached: true
|
|
49874
50505
|
});
|
|
@@ -49878,7 +50509,7 @@ function tryOpenBrowser(url) {
|
|
|
49878
50509
|
return false;
|
|
49879
50510
|
}
|
|
49880
50511
|
}
|
|
49881
|
-
var AGENT_VERSION3 = true ? "agent-0.0.
|
|
50512
|
+
var AGENT_VERSION3 = true ? "agent-0.0.53" : "agent-dev";
|
|
49882
50513
|
function osFamily() {
|
|
49883
50514
|
const p = platform5();
|
|
49884
50515
|
if (p === "darwin") return "macos";
|
|
@@ -49973,6 +50604,54 @@ function emitEvent(opts, event, fields = {}) {
|
|
|
49973
50604
|
process.stdout.write(`${JSON.stringify({ v: 1, ts: Date.now(), event, ...fields })}
|
|
49974
50605
|
`);
|
|
49975
50606
|
}
|
|
50607
|
+
function createTrayBuildUi(opts) {
|
|
50608
|
+
const isTty = !opts.json && process.stdout.isTTY === true;
|
|
50609
|
+
let startedAt = null;
|
|
50610
|
+
let ticker = null;
|
|
50611
|
+
const paintTicker = () => {
|
|
50612
|
+
if (startedAt === null) return;
|
|
50613
|
+
const s = Math.round((Date.now() - startedAt) / 1e3);
|
|
50614
|
+
process.stdout.write(`\r \x1B[2m\u23F3 compiling menu-bar tray from source\u2026 ${s}s\x1B[0m\x1B[K`);
|
|
50615
|
+
};
|
|
50616
|
+
const begin = () => {
|
|
50617
|
+
if (startedAt !== null) return;
|
|
50618
|
+
startedAt = Date.now();
|
|
50619
|
+
emitEvent(opts, "tray_build_started", {});
|
|
50620
|
+
if (!opts.json) {
|
|
50621
|
+
process.stdout.write(
|
|
50622
|
+
" \x1B[2mno prebuilt tray found \u2014 compiling a small Swift app locally (first run only, ~1 min)\x1B[0m\n"
|
|
50623
|
+
);
|
|
50624
|
+
}
|
|
50625
|
+
if (isTty) {
|
|
50626
|
+
paintTicker();
|
|
50627
|
+
ticker = setInterval(paintTicker, 1e3);
|
|
50628
|
+
ticker.unref?.();
|
|
50629
|
+
}
|
|
50630
|
+
};
|
|
50631
|
+
return {
|
|
50632
|
+
onLine: (line) => {
|
|
50633
|
+
begin();
|
|
50634
|
+
emitEvent(opts, "tray_build_progress", { line });
|
|
50635
|
+
if (isTty) {
|
|
50636
|
+
process.stdout.write(`\r\x1B[K \x1B[2m${line}\x1B[0m
|
|
50637
|
+
`);
|
|
50638
|
+
} else if (!opts.json) {
|
|
50639
|
+
process.stdout.write(` ${line}
|
|
50640
|
+
`);
|
|
50641
|
+
}
|
|
50642
|
+
},
|
|
50643
|
+
finish: () => {
|
|
50644
|
+
if (ticker) {
|
|
50645
|
+
clearInterval(ticker);
|
|
50646
|
+
ticker = null;
|
|
50647
|
+
}
|
|
50648
|
+
if (isTty && startedAt !== null) process.stdout.write("\r\x1B[K");
|
|
50649
|
+
const elapsed = startedAt === null ? null : Date.now() - startedAt;
|
|
50650
|
+
if (elapsed !== null) emitEvent(opts, "tray_build_done", { elapsed_ms: elapsed });
|
|
50651
|
+
return elapsed;
|
|
50652
|
+
}
|
|
50653
|
+
};
|
|
50654
|
+
}
|
|
49976
50655
|
async function cmdConnect(opts) {
|
|
49977
50656
|
const step = (msg) => {
|
|
49978
50657
|
if (opts.json) return;
|
|
@@ -50045,12 +50724,18 @@ async function cmdConnect(opts) {
|
|
|
50045
50724
|
});
|
|
50046
50725
|
if (platform5() === "darwin") {
|
|
50047
50726
|
step("Installing menu-bar tray (macOS)");
|
|
50727
|
+
const buildUi = createTrayBuildUi(opts);
|
|
50048
50728
|
try {
|
|
50049
|
-
const src = bundledTrayAppPath();
|
|
50729
|
+
const src = await bundledTrayAppPath({ onLine: buildUi.onLine });
|
|
50730
|
+
const buildMs = buildUi.finish();
|
|
50050
50731
|
if (src) {
|
|
50732
|
+
if (buildMs !== null) ok(`tray compiled from source in ${Math.round(buildMs / 1e3)}s`);
|
|
50051
50733
|
const out = installTrayApp(src);
|
|
50052
50734
|
if (out) {
|
|
50053
|
-
emitEvent(opts, "tray_installed", {
|
|
50735
|
+
emitEvent(opts, "tray_installed", {
|
|
50736
|
+
path: out.installedAt,
|
|
50737
|
+
...buildMs !== null ? { build_ms: buildMs } : {}
|
|
50738
|
+
});
|
|
50054
50739
|
ok(`tray at ${out.installedAt}`);
|
|
50055
50740
|
}
|
|
50056
50741
|
} else {
|
|
@@ -50058,6 +50743,7 @@ async function cmdConnect(opts) {
|
|
|
50058
50743
|
warn("no bundled tray \u2014 skipping (install Xcode CLI tools and re-run to get the icon)");
|
|
50059
50744
|
}
|
|
50060
50745
|
} catch (e) {
|
|
50746
|
+
buildUi.finish();
|
|
50061
50747
|
emitEvent(opts, "tray_install_failed", { error: e.message });
|
|
50062
50748
|
warn(`tray install skipped: ${e.message}`);
|
|
50063
50749
|
}
|