Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
1d3e2f2
Resync transport session states after server-link reconnect
Jul 24, 2026
9406cb1
Report daemon disk capacity and surface insufficient-capacity uploads
Jul 24, 2026
3b07221
Stop timeline metadata events from faking an idle session
Jul 25, 2026
48ba2cf
Anchor the chat viewport when revealing cached older messages
Jul 25, 2026
ed6335a
Wait for audit phases instead of fixed sleeps in the rework-loop test
Jul 25, 2026
dc4af39
Force dialog answers past the queue instead of deadlocking behind it
Jul 25, 2026
92b5c8b
Drop the lower bound when healing the timeline after resume/reconnect
Jul 25, 2026
8493a61
Stop the link-restore resync from re-firing local idle side effects
Jul 25, 2026
675633c
Count timeline events dropped before they reach a browser
Jul 25, 2026
5ccfad8
Raise counter cardinality cap to 10k and de-duplicate the metrics store
Jul 25, 2026
32a26a3
Stop a refresh from leaving the chat above the bottom
Jul 25, 2026
3a8db27
Hold the chat at the bottom through the post-refresh layout settle
Jul 25, 2026
490cd39
Give injected memory a redeemable handle
Jul 25, 2026
7af1073
Persist memory handles in the context store instead of a JSON file
Jul 25, 2026
39ec403
Make memory-handle persistence failures observable
Jul 25, 2026
3ef1d98
Stop a daemon link restore from firing one push per idle session
Jul 25, 2026
6491e33
Route handle persistence to the store outside tests
Jul 25, 2026
b0bb4e8
Keep the routing regression test out of the real home directory
Jul 25, 2026
6cc67e1
Import handles from the retired JSON cache during warm-load
Jul 25, 2026
da7e077
Refuse to resolve a colliding handle rather than guessing
Jul 25, 2026
e8ca770
Return every record behind a colliding handle instead of refusing
Jul 25, 2026
490635d
Close the ambiguous-handle contract and report discarded handles
Jul 25, 2026
daaae83
Report every discarded handle row and stop overstating candidates
Jul 25, 2026
d261227
Discard handles whose stored namespace cannot be validated
Jul 25, 2026
aa3c618
Validate namespace identity and report a corrupt handle cache
Jul 26, 2026
b46a62e
Keep handles written under the previous namespace key readable
Jul 26, 2026
af4154c
Carry handle-persistence failures off the machine that failed
Jul 26, 2026
8ee6329
Deliver handle-persistence health to the browser and fail closed on null
Jul 26, 2026
a7abd97
Show handle-persistence failures in the status bar
Jul 26, 2026
8bf5951
Cover the last hop of the short-ref failure signal
Jul 26, 2026
c740cee
Gate rework finalization on fresh audit
Jul 26, 2026
485b2bb
Keep legacy memory handles readable on Node 24
Jul 26, 2026
98be3ac
Align memory MCP E2E refs with current handles
Jul 26, 2026
d2236b6
Self-heal blocked daemon npm upgrades
Jul 26, 2026
ca29334
Fix Web upgrade test root invocation
Jul 26, 2026
edbe525
Stop discarding memory handles that have no explicit owner
Jul 26, 2026
96cd604
Narrow the owner-less handle-key rescue to the rows that need it
Jul 26, 2026
df63fec
Carry the pre-backfill namespace instead of guessing what it was
Jul 26, 2026
15ba3ba
Hydrate memory short refs from SQLite
Jul 26, 2026
3588a4d
Show redeemable refs in memory recall cards
Jul 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions server/src/routes/file-transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { WsBridge } from '../ws/bridge.js';
import { randomHex } from '../security/crypto.js';
import {
FILE_TRANSFER_LIMITS,
FILE_TRANSFER_UPLOAD_ERROR_CODE,
FILE_TRANSFER_UPLOAD_FETCH_CAPABILITY,
FILE_TRANSFER_DOWNLOAD_STREAM_CAPABILITY,
FILE_TRANSFER_PATH_HANDLE_CAPABILITY,
Expand Down Expand Up @@ -685,8 +686,13 @@ fileTransferRoutes.post('/:id/upload', async (c) => {
);

if (result.type === 'file.upload_error') {
logger.warn({ serverId, uploadId, error: result.message }, 'Daemon upload error');
return c.json({ error: 'upload_failed', message: result.message }, 500);
const code = typeof (result as { code?: unknown }).code === 'string'
? (result as { code: string }).code
: 'upload_failed';
logger.warn({ serverId, uploadId, error: result.message, code }, 'Daemon upload error');
// 507 Insufficient Storage when the daemon's disk is full; 500 otherwise.
const status = code === FILE_TRANSFER_UPLOAD_ERROR_CODE.INSUFFICIENT_CAPACITY ? 507 : 500;
return c.json({ error: code, message: result.message }, status);
}

const attachment = result.attachment as AttachmentRef;
Expand Down
58 changes: 17 additions & 41 deletions server/src/util/metrics.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,17 @@
export type MetricLabels = Record<string, string>;

const counters = new Map<string, number>();
const MAX_COUNTERS = 1000;

function labelsKey(labels?: MetricLabels): string {
if (!labels) return '';
const entries = Object.entries(labels)
.filter(([, value]) => typeof value === 'string')
.sort(([a], [b]) => a.localeCompare(b));
return entries.map(([key, value]) => `${key}=${value}`).join(',');
}

function counterKey(name: string, labels?: MetricLabels): string {
const suffix = labelsKey(labels);
return suffix ? `${name}{${suffix}}` : name;
}

export function incrementCounter(name: string, labels?: MetricLabels): void {
addCounter(name, 1, labels);
}

export function addCounter(name: string, amount: number, labels?: MetricLabels): void {
if (!name) return;
if (!Number.isFinite(amount) || amount <= 0) return;
const key = counterKey(name, labels);
if (!counters.has(key) && counters.size >= MAX_COUNTERS) return;
counters.set(key, (counters.get(key) ?? 0) + amount);
}

export function getCounter(name: string, labels?: MetricLabels): number {
return counters.get(counterKey(name, labels)) ?? 0;
}

export function snapshotCounters(): Record<string, number> {
return Object.fromEntries(counters.entries());
}

export function resetMetricsForTests(): void {
counters.clear();
}
/**
* Server-facing entry point for in-process counters.
*
* The implementation lives in `shared/metrics.ts` so the daemon and the server
* cannot drift apart again. Note the scope: counters are per-process, and the
* server runs multiple replicas, so a number here reflects ONE pod's traffic and
* resets on every deploy.
*/
export {
addCounter,
counterKeyCount,
getCounter,
incrementCounter,
resetMetricsForTests,
snapshotCounters,
} from '../../../shared/metrics.js';
export type { MetricLabels } from '../../../shared/metrics.js';
Loading
Loading