@bongos/core 1.19.380 → 1.19.382
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/.bongos-core.json +46 -31
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +4 -0
- package/clients/bongos-client/index.cjs +4 -0
- package/clients/bongos-client/index.d.ts +6 -0
- package/clients/bongos-client/index.mjs +4 -0
- package/docs/api/openapi.json +141 -3
- package/docs/api-reference.md +5 -3
- package/docs/copy-inventory.md +14 -9
- package/docs/copy-registry.json +87 -42
- package/migrations/core_228_idea_inbox_captured_by_idx.sql +61 -0
- package/modules/dev-box/app/src/vendor/bongos-client.cjs +4 -0
- package/modules/hall-ui/public/hall-kit.js +67 -6
- package/modules/hall-ui/public/profile.html +34 -2
- package/modules/hall-ui/public/profile.js +279 -0
- package/modules/ideas/inbox.js +98 -0
- package/modules/ideas/routes/inbox.js +39 -0
- package/modules/lifecycle/activity.js +205 -0
- package/modules/lifecycle/lifecycle.js +7 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/ship-memory-sync.js +62 -5
- package/scripts/gds/ship-session-upload.js +12 -10
- package/scripts/hall-preview/server.js +13 -3
- package/src/bongos/routes/builders.js +67 -0
- package/src/module-api.js +5 -1
- package/tests/profile_activity.mjs +625 -0
- package/tests/session_records.mjs +122 -5
|
@@ -112,27 +112,144 @@ test('snippetOf flattens whitespace, caps length, and scrubs', () => {
|
|
|
112
112
|
});
|
|
113
113
|
|
|
114
114
|
// ---------- resolveTranscriptPath (#551: worktree sessions) ----------
|
|
115
|
+
//
|
|
116
|
+
// FIXTURE NOTE: the expected transcript path is DERIVED via encodeProjectPath(
|
|
117
|
+
// path.resolve(dir)) rather than hard-coded as its POSIX encoding. path.resolve
|
|
118
|
+
// is platform-dependent — on win32 '/proj' resolves to 'C:\\\\proj', which encodes
|
|
119
|
+
// to 'C--proj', not '-proj' — so hard-coded encodings made these two tests fail
|
|
120
|
+
// on Windows while passing in Linux CI. Deriving them asserts the same behaviour
|
|
121
|
+
// on both. (POSIX encoding itself is pinned separately below.)
|
|
122
|
+
const sidPath = (base, dir, sid) =>
|
|
123
|
+
path.join(base, 'projects', encodeProjectPath(path.resolve(dir)), `${sid}.jsonl`);
|
|
115
124
|
|
|
116
125
|
test('resolveTranscriptPath prefers the session CWD (worktree) over REPO_ROOT', () => {
|
|
117
126
|
const sid = 'abc-123';
|
|
118
127
|
const base = '/fake/.claude';
|
|
119
128
|
const worktree = '/proj/.claude/worktrees/wt1';
|
|
120
129
|
const mainRepo = '/proj';
|
|
121
|
-
const wtPath =
|
|
130
|
+
const wtPath = sidPath(base, worktree, sid);
|
|
122
131
|
// Only the worktree-encoded path exists — the bug looked at REPO_ROOT first.
|
|
123
|
-
const got = resolveTranscriptPath(sid, [worktree, mainRepo], {
|
|
132
|
+
const got = resolveTranscriptPath(sid, [worktree, mainRepo], {
|
|
133
|
+
base,
|
|
134
|
+
existsFn: (p) => p === wtPath,
|
|
135
|
+
readdirFn: () => [],
|
|
136
|
+
});
|
|
124
137
|
assert.equal(got, wtPath);
|
|
125
138
|
});
|
|
126
139
|
|
|
127
140
|
test('resolveTranscriptPath falls back to REPO_ROOT, else null', () => {
|
|
128
141
|
const sid = 'abc-123';
|
|
129
142
|
const base = '/fake/.claude';
|
|
130
|
-
const mainPath =
|
|
143
|
+
const mainPath = sidPath(base, '/proj', sid);
|
|
131
144
|
assert.equal(
|
|
132
|
-
resolveTranscriptPath(sid, ['/proj/.claude/worktrees/wt1', '/proj'], {
|
|
145
|
+
resolveTranscriptPath(sid, ['/proj/.claude/worktrees/wt1', '/proj'], {
|
|
146
|
+
base,
|
|
147
|
+
existsFn: (p) => p === mainPath,
|
|
148
|
+
readdirFn: () => [],
|
|
149
|
+
}),
|
|
133
150
|
mainPath
|
|
134
151
|
);
|
|
135
|
-
assert.equal(
|
|
152
|
+
assert.equal(
|
|
153
|
+
resolveTranscriptPath(sid, ['/a', '/b'], { base, existsFn: () => false, readdirFn: () => [] }),
|
|
154
|
+
null
|
|
155
|
+
);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// ---------- resolveTranscriptPath scan fallback (task 1002505) ----------
|
|
159
|
+
//
|
|
160
|
+
// The two fast paths above NARROW the miss, they do not seal it: the session
|
|
161
|
+
// driving a ship need not be rooted in process.cwd() OR REPO_ROOT. A session
|
|
162
|
+
// rooted in ANOTHER repo can file a task here and ship it (proven on task
|
|
163
|
+
// 1002475), and before this fallback both probes missed, classifyUploadSkip
|
|
164
|
+
// returned transcript_not_found, no session_record was written, and the
|
|
165
|
+
// cost-plus reward — the only stream this instance pays (ADR 0146) — never
|
|
166
|
+
// booked. The fallback scans every dir under <base>/projects for
|
|
167
|
+
// <sessionId>.jsonl; a session id is a UUID, so a filename hit is unambiguous.
|
|
168
|
+
|
|
169
|
+
test('resolveTranscriptPath finds a transcript rooted in a DIFFERENT project dir (the 1002475 shape)', () => {
|
|
170
|
+
const sid = 'e53f445b-308a-454e-9ac1-9c22d0188a37';
|
|
171
|
+
const base = '/fake/.claude';
|
|
172
|
+
// The orchestrating session lived under an unrelated repo's encoded dir, so
|
|
173
|
+
// neither the claim worktree nor REPO_ROOT could ever match it.
|
|
174
|
+
const foreign = path.join(base, 'projects', 'C--Users-x-Git-repos-other-project', `${sid}.jsonl`);
|
|
175
|
+
const got = resolveTranscriptPath(sid, ['/proj/.claude/worktrees/wt1', '/proj'], {
|
|
176
|
+
base,
|
|
177
|
+
existsFn: (p) => p === foreign,
|
|
178
|
+
readdirFn: () => ['C--Users-x-Git-repos-other-project', '-proj', '-proj--claude-worktrees-wt1'],
|
|
179
|
+
});
|
|
180
|
+
assert.equal(got, foreign);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test('resolveTranscriptPath scan only matches the exact session id — no mis-attribution', () => {
|
|
184
|
+
const sid = 'abc-123';
|
|
185
|
+
const base = '/fake/.claude';
|
|
186
|
+
const other = path.join(base, 'projects', '-somewhere-else', 'def-456.jsonl');
|
|
187
|
+
assert.equal(
|
|
188
|
+
resolveTranscriptPath(sid, ['/proj'], {
|
|
189
|
+
base,
|
|
190
|
+
existsFn: (p) => p === other,
|
|
191
|
+
readdirFn: () => ['-somewhere-else'],
|
|
192
|
+
}),
|
|
193
|
+
null
|
|
194
|
+
);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test('resolveTranscriptPath does not scan when a fast path hits (precedence unchanged)', () => {
|
|
198
|
+
const sid = 'abc-123';
|
|
199
|
+
const base = '/fake/.claude';
|
|
200
|
+
const wtPath = sidPath(base, '/proj/.claude/worktrees/wt1', sid);
|
|
201
|
+
let scanned = 0;
|
|
202
|
+
const got = resolveTranscriptPath(sid, ['/proj/.claude/worktrees/wt1', '/proj'], {
|
|
203
|
+
base,
|
|
204
|
+
existsFn: (p) => p === wtPath,
|
|
205
|
+
readdirFn: () => { scanned++; return []; },
|
|
206
|
+
});
|
|
207
|
+
assert.equal(got, wtPath);
|
|
208
|
+
assert.equal(scanned, 0, 'the scan must not run once a fast path has matched');
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test('resolveTranscriptPath fills opts.tried: the fast paths, plus ONE scan summary', () => {
|
|
212
|
+
const sid = 'abc-123';
|
|
213
|
+
const base = '/fake/.claude';
|
|
214
|
+
const tried = [];
|
|
215
|
+
const got = resolveTranscriptPath(sid, ['/proj/.claude/worktrees/wt1', '/proj'], {
|
|
216
|
+
base,
|
|
217
|
+
existsFn: () => false,
|
|
218
|
+
readdirFn: () => ['-a', '-b', '-c'],
|
|
219
|
+
tried,
|
|
220
|
+
});
|
|
221
|
+
assert.equal(got, null);
|
|
222
|
+
// Two exact fast paths...
|
|
223
|
+
assert.equal(tried[0], sidPath(base, '/proj/.claude/worktrees/wt1', sid));
|
|
224
|
+
assert.equal(tried[1], sidPath(base, '/proj', sid));
|
|
225
|
+
// ...then a single bounded summary, not one line per scanned dir, so the
|
|
226
|
+
// transcript_not_found reason stays readable however many projects exist.
|
|
227
|
+
assert.equal(tried.length, 3);
|
|
228
|
+
assert.ok(tried[2].includes('scanned 3 project dirs'), tried[2]);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test('resolveTranscriptPath survives an unreadable projects dir and says so', () => {
|
|
232
|
+
const sid = 'abc-123';
|
|
233
|
+
const base = '/fake/.claude';
|
|
234
|
+
const tried = [];
|
|
235
|
+
const got = resolveTranscriptPath(sid, ['/proj'], {
|
|
236
|
+
base,
|
|
237
|
+
existsFn: () => false,
|
|
238
|
+
readdirFn: () => { throw new Error('ENOENT'); },
|
|
239
|
+
tried,
|
|
240
|
+
});
|
|
241
|
+
assert.equal(got, null, 'a missing/unreadable projects dir must not throw into the ship path');
|
|
242
|
+
assert.ok(tried[tried.length - 1].includes('could not read the projects dir'), tried.join(' ; '));
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test('resolveTranscriptPath tolerates a null/absent projectDirs list', () => {
|
|
246
|
+
const sid = 'abc-123';
|
|
247
|
+
const base = '/fake/.claude';
|
|
248
|
+
const hit = path.join(base, 'projects', '-only-dir', `${sid}.jsonl`);
|
|
249
|
+
assert.equal(
|
|
250
|
+
resolveTranscriptPath(sid, null, { base, existsFn: (p) => p === hit, readdirFn: () => ['-only-dir'] }),
|
|
251
|
+
hit
|
|
252
|
+
);
|
|
136
253
|
});
|
|
137
254
|
|
|
138
255
|
// ---------- encodeProjectPath (task 1002471: win32 separators) ----------
|