@openchambery/web 1.18.2-beta.11 → 1.18.2-beta.13
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/assets/{AssistantView-BDxTxI4C.js → AssistantView-Bbqfj9CK.js} +1 -1
- package/dist/assets/{ChatContainer-C26Rw1h2.js → ChatContainer-hDTv6UXl.js} +7 -7
- package/dist/assets/{DiagramView-siSsyD6n.js → DiagramView-1hFDtyEy.js} +1 -1
- package/dist/assets/{MarkdownRendererImpl-Crm21KO-.js → MarkdownRendererImpl-5BLw0qYH.js} +1 -1
- package/dist/assets/{MobileDetailNavigation-m2VCvJuL.js → MobileDetailNavigation-DIyVGM50.js} +1 -1
- package/dist/assets/{MobileShareBridge-B4X9T8I9.js → MobileShareBridge-BQM-WOll.js} +1 -1
- package/dist/assets/{MobileSurface-DUhwRQFz.js → MobileSurface-KZ-kuSM-.js} +1 -1
- package/dist/assets/{MultiRunWindow-DHm99eU5.js → MultiRunWindow-DPOSSmY_.js} +1 -1
- package/dist/assets/{OverlayScrollbar-ivKSIGWh.js → OverlayScrollbar-C2pMNVMM.js} +1 -1
- package/dist/assets/{SettingsView-CpM0iBkp.js → SettingsView-DxL07Iba.js} +1 -1
- package/dist/assets/{SettingsWindow-DW1pl40e.js → SettingsWindow-DJ2o4zvh.js} +1 -1
- package/dist/assets/{TerminalView-z4ek-zEW.js → TerminalView-BB3tvloy.js} +1 -1
- package/dist/assets/{ToolOutputDialog-BZPEjn9H.js → ToolOutputDialog-D7DqChuM.js} +1 -1
- package/dist/assets/{appThemeRegistry-BUHyoCKY.js → appThemeRegistry-DhaoeTus.js} +2 -2
- package/dist/assets/{context-menu-uy-nsYLa.js → context-menu-D88vCGyB.js} +1 -1
- package/dist/assets/{gitApi-BukyCDHf.js → gitApi-DVNC4iYD.js} +1 -1
- package/dist/assets/{insertionBoundaries-Cn--FUpW.js → insertionBoundaries-hcIB6RSd.js} +1 -1
- package/dist/assets/{main-DBMzIIq8.js → main-CQrMAdMF.js} +4 -4
- package/dist/assets/{main-B9LVDYC3.js → main-D4aa2ZJz.js} +2 -2
- package/dist/assets/{miniChat-nHXgh499.js → miniChat-4N2-z2vf.js} +2 -2
- package/dist/assets/{mobile-DheffaWK.js → mobile-C-RRGcDW.js} +2 -2
- package/dist/assets/{multirun-BkftDz-w.js → multirun-Co8LbTjb.js} +1 -1
- package/dist/assets/{projectMeta-Cn8JzItU.js → projectMeta-D-ncdr5Z.js} +1 -1
- package/dist/assets/{radio-C9csxweA.js → radio-CD4P-ogh.js} +1 -1
- package/dist/assets/{renderElectronMiniChatApp-DLaYpuy5.js → renderElectronMiniChatApp-D5QcQTL0.js} +2 -2
- package/dist/assets/{renderMobileApp-CxDgYCxR.js → renderMobileApp-CnZZVbHU.js} +4 -4
- package/dist/assets/{runtimeEndpointReset-CCpti76q.js → runtimeEndpointReset-BQ5F5VZS.js} +2 -2
- package/dist/assets/{sortable-tabs-strip-sSkepr7C.js → sortable-tabs-strip-DQsv96ke.js} +1 -1
- package/dist/assets/{src-BcPciZGI.css → src-C8jn0x69.css} +1 -1
- package/dist/assets/{useAppFontEffects-BSolc5HW.js → useAppFontEffects-DygV6JSW.js} +2 -2
- package/dist/assets/{useDesktopWindowControlsLayout-CXQ-Ublz.js → useDesktopWindowControlsLayout-Cj63sNDg.js} +1 -1
- package/dist/assets/{useEffectiveDirectory-Dm7djJX8.js → useEffectiveDirectory-Dc904Fjg.js} +1 -1
- package/dist/assets/{useMobileNavigationStore-a9a3gOnd.js → useMobileNavigationStore-DNoNQA4y.js} +2 -2
- package/dist/assets/{useSessionAutoCleanup-CH6_9K8S.js → useSessionAutoCleanup-bBkcz_hu.js} +1 -1
- package/dist/assets/{useWorkerHighlightedLines-Qg2o83Ps.js → useWorkerHighlightedLines-mhBD0Do9.js} +1 -1
- package/dist/index.html +2 -2
- package/dist/mini-chat.html +2 -2
- package/dist/mobile.html +2 -2
- package/package.json +1 -1
- package/server/lib/event-stream/DOCUMENTATION.md +2 -2
- package/server/lib/event-stream/diff-summary.js +28 -19
- package/server/lib/event-stream/diff-summary.test.js +37 -10
- package/server/lib/event-stream/protocol.test.js +66 -3
- package/server/lib/opencode/DOCUMENTATION.md +1 -1
- package/server/lib/session-turn-pages/DOCUMENTATION.md +3 -3
- package/server/lib/session-turn-pages/reconcile.routes.test.js +6 -2
- package/server/lib/session-turn-pages/reconcile.service.test.js +6 -2
- package/server/lib/session-turn-pages/routes.js +3 -3
- package/server/lib/session-turn-pages/routes.test.js +41 -13
- package/server/lib/session-turn-pages/service.js +4 -2
- package/server/lib/session-turn-pages/service.test.js +13 -3
|
@@ -79,11 +79,11 @@ Every turn-page response summarizes `tool`, `reasoning`, and `file` parts plus m
|
|
|
79
79
|
|
|
80
80
|
| Layer | Surface | Payload |
|
|
81
81
|
|---|---|---|
|
|
82
|
-
| **L1** | Turn pages, reconcile response pages, message/session SSE fan-out, exact `GET /api/session/:sessionID/message/:messageID` | Any `info.summary.diffs` **array** is projected to `summary.diffCount: number`
|
|
82
|
+
| **L1** | Turn pages, reconcile response pages, message/session SSE fan-out, exact `GET /api/session/:sessionID/message/:messageID` | Any `info.summary.diffs` **array** is projected to a **slim file list** `[{ file, status?, additions, deletions }]` plus additive markers `summary.diffCount: number` and `summary.hasDiffs: boolean`. Patch/before/after/from/to and other heavy bodies are stripped. Other lightweight summary fields are preserved. `diffCount` equals the slim array length; `hasDiffs === diffCount > 0`. Consumers may use either the slim array or the markers. |
|
|
83
83
|
| **L2** | `GET /api/openchamber/sessions/:sessionID/changes?messageID=` (no `file`) | Host reads official `session.message` locally, returns only `{ files: [{ file, status?, additions, deletions }] }`. Never returns patch/before/after/from/to or the full message envelope. |
|
|
84
84
|
| **L3** | Same route with `file=` | Host calls official `session.diff({ sessionID, messageID, directory })`, returns `{ diff: SnapshotFileDiff }` for the exact `file` string match (includes that file's patch). No match → HTTP 404 `{ error: 'change file not found', code: 'change_not_found' }`. |
|
|
85
85
|
|
|
86
|
-
Exact message GET keeps the official `{ info, parts }` response shape and only applies L1 summary projection so materialize/recovery never relays
|
|
86
|
+
Exact message GET keeps the official `{ info, parts }` response shape and only applies L1 summary projection so materialize/recovery never relays raw multi-megabyte patch bodies through the generic proxy. Abort/45s timeout semantics match the turn-page routes. Global `/api` auth still applies.
|
|
87
87
|
|
|
88
88
|
Changes query guards: `sessionID` / `messageID` length `1..512`; optional `file` length `1..4096` and rejects control characters / NUL. Failures: invalid params → 400, abort → 499, upstream/malformed → 502. Logs carry only safe flags and ids — never paths, patch bodies, or tokens.
|
|
89
89
|
|
|
@@ -100,7 +100,7 @@ Rules:
|
|
|
100
100
|
- Slim tool input keeps only short locator and task-identity fields (path, pattern, query, command, `subagent_type`, `description`, skill `name` / `id`). Slim metadata keeps `sessionId`, skill `name`, plus edit `additions`/`deletions`. Projection still drops result bodies, task prompts, patches, and large write payloads.
|
|
101
101
|
- Assistant `text` stays intact. User rows are no longer wholesale pass-through: their `file` parts are projected; other user parts stay as-is.
|
|
102
102
|
- `file` projection keeps already-present size/width/height (top-level or `metadata`). `byteSize` is derived from a data URL when present. PNG/GIF/JPEG/WebP headers may fill missing `width`/`height`. Unknown dimensions omit those fields.
|
|
103
|
-
- Message `info.summary.diffs` is L1-projected to `diffCount` / `hasDiffs` (
|
|
103
|
+
- Message `info.summary.diffs` is L1-projected to a slim file list plus `diffCount` / `hasDiffs` (never patch bodies). Reconcile preserves full parts and applies this same L1 summary projection to response-page byte budgeting and serialization; raw upstream scan budgets retain raw bytes. Clients that need a dedicated file-list envelope or per-file patches use the Changes L2/L3 API.
|
|
104
104
|
- A slim `file` part is a reference, not renderable content. Clients must hydrate the full message (`session.message` by `messageID`) before painting or editing the attachment. Do not treat slim file metadata as a complete part.
|
|
105
105
|
- Records that gain nothing from projection keep their original object reference, so identity-based change detection downstream still works.
|
|
106
106
|
- Projection applies to every turn-page response, including first packet, prepend, and explicit cursors (`before` present). Reconcile routes return full parts, unchanged.
|
|
@@ -244,17 +244,21 @@ describe('registerSessionTurnPageRoutes — reconcile', () => {
|
|
|
244
244
|
|
|
245
245
|
expect(res.statusCode).toBe(200);
|
|
246
246
|
expect(res.body.records[0].info.summary).toEqual({
|
|
247
|
+
diffs: [{
|
|
248
|
+
file: 'src/large.ts',
|
|
249
|
+
status: 'modified',
|
|
250
|
+
additions: 5,
|
|
251
|
+
deletions: 1,
|
|
252
|
+
}],
|
|
247
253
|
diffCount: 1,
|
|
248
254
|
hasDiffs: true,
|
|
249
255
|
});
|
|
250
|
-
expect(res.body.records[0].info.summary.diffs).toBeUndefined();
|
|
251
256
|
expect(res.body.records[0].parts[0]).toEqual({
|
|
252
257
|
id: 'prt_1',
|
|
253
258
|
type: 'text',
|
|
254
259
|
text: 'full recovery text',
|
|
255
260
|
});
|
|
256
261
|
expect(JSON.stringify(res.body)).not.toContain(patch);
|
|
257
|
-
expect(JSON.stringify(res.body)).not.toContain('src/large.ts');
|
|
258
262
|
});
|
|
259
263
|
|
|
260
264
|
it('returns HTTP 200 for resetRequired (anchor lost / budget rebuild)', async () => {
|
|
@@ -512,13 +512,17 @@ describe('createSessionReconcileService', () => {
|
|
|
512
512
|
|
|
513
513
|
expect(result.ok).toBe(true);
|
|
514
514
|
expect(result.records[0].info.summary).toEqual({
|
|
515
|
+
diffs: [{
|
|
516
|
+
file: 'src/large.ts',
|
|
517
|
+
status: 'modified',
|
|
518
|
+
additions: 7,
|
|
519
|
+
deletions: 2,
|
|
520
|
+
}],
|
|
515
521
|
diffCount: 1,
|
|
516
522
|
hasDiffs: true,
|
|
517
523
|
});
|
|
518
|
-
expect(result.records[0].info.summary.diffs).toBeUndefined();
|
|
519
524
|
expect(result.responseBytes).toBeLessThanOrEqual(1024);
|
|
520
525
|
expect(JSON.stringify(result.records)).not.toContain(patch);
|
|
521
|
-
expect(JSON.stringify(result.records)).not.toContain('src/large.ts');
|
|
522
526
|
});
|
|
523
527
|
|
|
524
528
|
it('rejects continuation that does not bind runtime/directory/session/anchor/head', async () => {
|
|
@@ -307,7 +307,7 @@ const createSdkFetchDiff = ({ buildOpenCodeUrl, getOpenCodeAuthHeaders, logger }
|
|
|
307
307
|
|
|
308
308
|
/**
|
|
309
309
|
* Project exact message GET payload: keep original `{ info, parts }` shape,
|
|
310
|
-
* only L1-project `info.summary.diffs` → diffCount/hasDiffs.
|
|
310
|
+
* only L1-project `info.summary.diffs` → slim file list + diffCount/hasDiffs.
|
|
311
311
|
*/
|
|
312
312
|
export const projectExactMessagePayload = (payload) => {
|
|
313
313
|
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
@@ -625,8 +625,8 @@ export const registerSessionTurnPageRoutes = (app, dependencies = {}) => {
|
|
|
625
625
|
|
|
626
626
|
/**
|
|
627
627
|
* Exact message GET — registered before generic `/api` proxy so materialize /
|
|
628
|
-
* recovery never relay
|
|
629
|
-
* `{ info, parts }` shape with L1
|
|
628
|
+
* recovery never relay raw multi-megabyte patch bodies in `summary.diffs`.
|
|
629
|
+
* Returns the official `{ info, parts }` shape with L1 slim-list projection.
|
|
630
630
|
*/
|
|
631
631
|
app.get('/api/session/:sessionID/message/:messageID', async (req, res) => {
|
|
632
632
|
const sessionID = req.params?.sessionID;
|
|
@@ -320,7 +320,7 @@ describe('registerSessionTurnPageRoutes', () => {
|
|
|
320
320
|
expect(part.state.output).toBeUndefined();
|
|
321
321
|
});
|
|
322
322
|
|
|
323
|
-
it('projects message summary.diffs to L1
|
|
323
|
+
it('projects message summary.diffs to L1 slim list + markers before serializing a turn-page response', async () => {
|
|
324
324
|
const patch = 'diff-body-'.repeat(20_000);
|
|
325
325
|
const loadPage = vi.fn(async () => ({
|
|
326
326
|
ok: true,
|
|
@@ -335,6 +335,8 @@ describe('registerSessionTurnPageRoutes', () => {
|
|
|
335
335
|
additions: 9,
|
|
336
336
|
deletions: 3,
|
|
337
337
|
patch,
|
|
338
|
+
before: 'old',
|
|
339
|
+
after: 'new',
|
|
338
340
|
}],
|
|
339
341
|
},
|
|
340
342
|
},
|
|
@@ -356,15 +358,21 @@ describe('registerSessionTurnPageRoutes', () => {
|
|
|
356
358
|
|
|
357
359
|
expect(res.statusCode).toBe(200);
|
|
358
360
|
expect(res.body.records[0].info.summary).toEqual({
|
|
361
|
+
diffs: [{
|
|
362
|
+
file: 'src/large.ts',
|
|
363
|
+
status: 'modified',
|
|
364
|
+
additions: 9,
|
|
365
|
+
deletions: 3,
|
|
366
|
+
}],
|
|
359
367
|
diffCount: 1,
|
|
360
368
|
hasDiffs: true,
|
|
361
369
|
});
|
|
362
|
-
expect(res.body.records[0].info.summary.diffs).toBeUndefined();
|
|
363
370
|
expect(JSON.stringify(res.body)).not.toContain(patch);
|
|
364
|
-
expect(JSON.stringify(res.body)).not.toContain('
|
|
371
|
+
expect(JSON.stringify(res.body)).not.toContain('"before"');
|
|
372
|
+
expect(JSON.stringify(res.body)).not.toContain('"after"');
|
|
365
373
|
});
|
|
366
374
|
|
|
367
|
-
it('keeps L1 turn-page serialization under
|
|
375
|
+
it('keeps L1 turn-page serialization under 256KiB for a 463-file / ~14MB patch summary', async () => {
|
|
368
376
|
const patchBody = 'P'.repeat(Math.floor((14 * 1024 * 1024) / 463));
|
|
369
377
|
const diffs = Array.from({ length: 463 }, (_, i) => ({
|
|
370
378
|
file: `src/generated/file-${i}.ts`,
|
|
@@ -374,6 +382,8 @@ describe('registerSessionTurnPageRoutes', () => {
|
|
|
374
382
|
patch: `${patchBody}-${i}`,
|
|
375
383
|
before: `before-${i}`,
|
|
376
384
|
after: `after-${i}`,
|
|
385
|
+
from: `from-${i}`,
|
|
386
|
+
to: `to-${i}`,
|
|
377
387
|
}));
|
|
378
388
|
const loadPage = vi.fn(async () => ({
|
|
379
389
|
ok: true,
|
|
@@ -401,16 +411,29 @@ describe('registerSessionTurnPageRoutes', () => {
|
|
|
401
411
|
|
|
402
412
|
expect(res.statusCode).toBe(200);
|
|
403
413
|
const serialized = JSON.stringify(res.body);
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
414
|
+
const summary = res.body.records[0].info.summary;
|
|
415
|
+
expect(summary.diffCount).toBe(463);
|
|
416
|
+
expect(summary.hasDiffs).toBe(true);
|
|
417
|
+
expect(summary.diffs).toHaveLength(463);
|
|
418
|
+
expect(summary.diffs[0]).toEqual({
|
|
419
|
+
file: 'src/generated/file-0.ts',
|
|
420
|
+
status: 'modified',
|
|
421
|
+
additions: 0,
|
|
422
|
+
deletions: 0,
|
|
423
|
+
});
|
|
424
|
+
expect(summary.diffs[462]).toEqual({
|
|
425
|
+
file: 'src/generated/file-462.ts',
|
|
426
|
+
status: 'modified',
|
|
427
|
+
additions: 462 % 7,
|
|
428
|
+
deletions: 462 % 3,
|
|
408
429
|
});
|
|
409
|
-
expect(res.body.records[0].info.summary.diffs).toBeUndefined();
|
|
410
|
-
expect(serialized).not.toContain('"diffs"');
|
|
411
430
|
expect(serialized).not.toContain(patchBody.slice(0, 64));
|
|
412
|
-
expect(serialized).not.toContain('
|
|
413
|
-
expect(
|
|
431
|
+
expect(serialized).not.toContain('"patch"');
|
|
432
|
+
expect(serialized).not.toContain('"before"');
|
|
433
|
+
expect(serialized).not.toContain('"after"');
|
|
434
|
+
expect(serialized).not.toContain('"from"');
|
|
435
|
+
expect(serialized).not.toContain('"to"');
|
|
436
|
+
expect(Buffer.byteLength(serialized, 'utf8')).toBeLessThan(256 * 1024);
|
|
414
437
|
});
|
|
415
438
|
|
|
416
439
|
it('projects file parts on first packet and prepend the same way', async () => {
|
|
@@ -630,6 +653,12 @@ describe('registerSessionTurnPageRoutes — exact message GET', () => {
|
|
|
630
653
|
role: 'user',
|
|
631
654
|
summary: {
|
|
632
655
|
title: 'turn',
|
|
656
|
+
diffs: [{
|
|
657
|
+
file: 'a.ts',
|
|
658
|
+
status: 'modified',
|
|
659
|
+
additions: 2,
|
|
660
|
+
deletions: 1,
|
|
661
|
+
}],
|
|
633
662
|
diffCount: 1,
|
|
634
663
|
hasDiffs: true,
|
|
635
664
|
},
|
|
@@ -637,7 +666,6 @@ describe('registerSessionTurnPageRoutes — exact message GET', () => {
|
|
|
637
666
|
parts: [{ id: 'prt_1', type: 'text', text: 'hello' }],
|
|
638
667
|
});
|
|
639
668
|
expect(JSON.stringify(res.body)).not.toContain(patch);
|
|
640
|
-
expect(JSON.stringify(res.body)).not.toContain('a.ts');
|
|
641
669
|
});
|
|
642
670
|
|
|
643
671
|
it('rejects oversize messageID / sessionID with 400', async () => {
|
|
@@ -459,8 +459,10 @@ const isUserRecord = (record) => {
|
|
|
459
459
|
};
|
|
460
460
|
|
|
461
461
|
/**
|
|
462
|
-
* L1: project message `summary.diffs`
|
|
463
|
-
*
|
|
462
|
+
* L1: project message `summary.diffs` to a slim file list
|
|
463
|
+
* `{ file, status?, additions, deletions }` plus additive `diffCount` /
|
|
464
|
+
* `hasDiffs`. Never retains patch/before/after/from/to. Preserves every other
|
|
465
|
+
* message / summary field.
|
|
464
466
|
*/
|
|
465
467
|
export const projectMessageDiffSummaries = (records) => {
|
|
466
468
|
if (!Array.isArray(records) || records.length === 0) return records;
|
|
@@ -172,7 +172,7 @@ describe('projectSlimParts', () => {
|
|
|
172
172
|
},
|
|
173
173
|
});
|
|
174
174
|
|
|
175
|
-
it('projects message summary.diffs to L1
|
|
175
|
+
it('projects message summary.diffs to L1 slim list + markers even when parts are empty', () => {
|
|
176
176
|
const patch = 'diff-body-'.repeat(20_000);
|
|
177
177
|
const [projected] = projectSlimParts([{
|
|
178
178
|
info: {
|
|
@@ -186,6 +186,10 @@ describe('projectSlimParts', () => {
|
|
|
186
186
|
additions: 12,
|
|
187
187
|
deletions: 4,
|
|
188
188
|
patch,
|
|
189
|
+
before: 'old-body',
|
|
190
|
+
after: 'new-body',
|
|
191
|
+
from: 'from-blob',
|
|
192
|
+
to: 'to-blob',
|
|
189
193
|
}],
|
|
190
194
|
},
|
|
191
195
|
},
|
|
@@ -194,12 +198,18 @@ describe('projectSlimParts', () => {
|
|
|
194
198
|
|
|
195
199
|
expect(projected.info.summary).toEqual({
|
|
196
200
|
title: 'kept',
|
|
201
|
+
diffs: [{
|
|
202
|
+
file: 'src/large.ts',
|
|
203
|
+
status: 'modified',
|
|
204
|
+
additions: 12,
|
|
205
|
+
deletions: 4,
|
|
206
|
+
}],
|
|
197
207
|
diffCount: 1,
|
|
198
208
|
hasDiffs: true,
|
|
199
209
|
});
|
|
200
|
-
expect(projected.info.summary.diffs).toBeUndefined();
|
|
201
210
|
expect(JSON.stringify(projected)).not.toContain(patch);
|
|
202
|
-
expect(JSON.stringify(projected)).not.toContain('
|
|
211
|
+
expect(JSON.stringify(projected)).not.toContain('old-body');
|
|
212
|
+
expect(JSON.stringify(projected)).not.toContain('from-blob');
|
|
203
213
|
});
|
|
204
214
|
|
|
205
215
|
it('drops tool output and metadata but keeps identity and status', () => {
|