amalgm 0.1.239 → 0.1.241
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/lib/shared-realtime-tunnel.js +18 -4
- package/package.json +1 -1
- package/runtime/scripts/amalgm-mcp/browser/cookie-jar.js +68 -20
- package/runtime/scripts/amalgm-mcp/project-context/store.js +87 -19
- package/runtime/scripts/amalgm-mcp/server/routes/state.js +13 -0
- package/runtime/scripts/amalgm-mcp/state/attachments.js +40 -12
- package/runtime/scripts/amalgm-mcp/state/db.js +216 -17
- package/runtime/scripts/amalgm-mcp/state/doc-disk.js +550 -0
- package/runtime/scripts/amalgm-mcp/state/docs.js +1101 -301
- package/runtime/scripts/amalgm-mcp/state/mutation-contracts.js +99 -0
- package/runtime/scripts/amalgm-mcp/state/mutations.js +507 -130
- package/runtime/scripts/amalgm-mcp/state/promotions.js +21 -1
- package/runtime/scripts/amalgm-mcp/state/replicas.js +134 -13
- package/runtime/scripts/amalgm-mcp/state/shared-rest.js +14 -8
- package/runtime/scripts/amalgm-mcp/tests/browser-cookie-cloud.test.js +193 -0
- package/runtime/scripts/amalgm-mcp/tests/code-project-stream.test.js +313 -94
- package/runtime/scripts/amalgm-mcp/tests/fixtures/code-project-stream-v2.json +1 -0
- package/runtime/scripts/amalgm-mcp/tests/project-context.test.js +32 -17
- package/runtime/scripts/amalgm-mcp/tests/shared-tunnel-rehydration.test.js +84 -0
- package/runtime/scripts/amalgm-mcp/tests/state-docs.test.js +376 -13
- package/runtime/scripts/amalgm-mcp/tests/state-mutations.test.js +309 -0
- package/runtime/scripts/amalgm-mcp/tests/state-shared-replica.test.js +758 -30
- package/runtime/scripts/amalgm-mcp/tests/workspace-cards-store.test.js +72 -3
- package/runtime/scripts/amalgm-mcp/tests/workspace-cards.test.js +33 -6
- package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint-reducer.test.js +27 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-checkpoint.test.js +334 -191
- package/runtime/scripts/amalgm-mcp/tests/workspace-durable.test.js +24 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-local-intent.test.js +1999 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-native-file-transaction.test.js +364 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-object-tunnel.test.js +98 -4
- package/runtime/scripts/amalgm-mcp/tests/workspace-objects.test.js +156 -8
- package/runtime/scripts/amalgm-mcp/tests/workspace-provenance.test.js +185 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-publication-invariants.test.js +174 -269
- package/runtime/scripts/amalgm-mcp/tests/workspace-reducer-liveness.test.js +26 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-reducer.test.js +2060 -407
- package/runtime/scripts/amalgm-mcp/tests/workspace-sync-stats.test.js +119 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-transition.test.js +1446 -0
- package/runtime/scripts/amalgm-mcp/tests/workspace-tree-cloud.test.js +5319 -3
- package/runtime/scripts/amalgm-mcp/tests/workspace-tree-core.test.js +191 -1
- package/runtime/scripts/amalgm-mcp/tests/workspace-tree-store.test.js +3140 -17
- package/runtime/scripts/amalgm-mcp/tests/workspace-wall.test.js +135 -637
- package/runtime/scripts/amalgm-mcp/workspace/access-store.js +5 -1
- package/runtime/scripts/amalgm-mcp/workspace/card.js +7 -7
- package/runtime/scripts/amalgm-mcp/workspace/cards.js +36 -12
- package/runtime/scripts/amalgm-mcp/workspace/checkpoint.js +436 -173
- package/runtime/scripts/amalgm-mcp/workspace/content.js +32 -0
- package/runtime/scripts/amalgm-mcp/workspace/durable.js +45 -0
- package/runtime/scripts/amalgm-mcp/workspace/git.js +391 -37
- package/runtime/scripts/amalgm-mcp/workspace/intent.js +535 -0
- package/runtime/scripts/amalgm-mcp/workspace/merge.js +89 -6
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/BUILD.md +31 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/bin/darwin-arm64/amalgm-file-transaction +0 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/bin/linux-x64/amalgm-file-transaction +0 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/build.mjs +72 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/client.js +165 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/go.mod +5 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/go.sum +2 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/main.go +2201 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/manifest.json +19 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/move_darwin.go +18 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/move_linux.go +18 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/process_darwin.go +33 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/process_linux.go +47 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/verify.mjs +43 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/writers_darwin.go +114 -0
- package/runtime/scripts/amalgm-mcp/workspace/native-file-transaction/writers_linux.go +84 -0
- package/runtime/scripts/amalgm-mcp/workspace/objects.js +185 -55
- package/runtime/scripts/amalgm-mcp/workspace/project-stream.js +616 -168
- package/runtime/scripts/amalgm-mcp/workspace/provenance.js +240 -0
- package/runtime/scripts/amalgm-mcp/workspace/reducer.js +1979 -570
- package/runtime/scripts/amalgm-mcp/workspace/rest.js +49 -28
- package/runtime/scripts/amalgm-mcp/workspace/store.js +77 -4
- package/runtime/scripts/amalgm-mcp/workspace/sync-stats.js +240 -0
- package/runtime/scripts/amalgm-mcp/workspace/transition.js +1729 -192
- package/runtime/scripts/amalgm-mcp/workspace/tree/floor.js +100 -0
- package/runtime/scripts/amalgm-mcp/workspace/tree/inclusion.js +7 -1
- package/runtime/scripts/amalgm-mcp/workspace/tree/local-state.js +73 -61
- package/runtime/scripts/amalgm-mcp/workspace/tree/observation-holds.js +127 -0
- package/runtime/scripts/amalgm-mcp/workspace/tree/path-state.js +12 -0
- package/runtime/scripts/amalgm-mcp/workspace/tree/reducer.js +51 -6
- package/runtime/scripts/amalgm-mcp/workspace/tree/root-binding.js +125 -0
- package/runtime/scripts/amalgm-mcp/workspace/tree/snapshot.js +79 -8
- package/runtime/scripts/amalgm-mcp/workspace/tree/symlinks.js +76 -0
- package/runtime/scripts/amalgm-mcp/workspace/tree-cloud.js +3480 -165
- package/runtime/scripts/amalgm-mcp/workspace/tree-materialization.js +3766 -0
- package/runtime/scripts/amalgm-mcp/workspace/tree-store.js +899 -71
- package/runtime/scripts/amalgm-mcp/workspace/wall.js +352 -497
|
@@ -52,7 +52,11 @@ function resolveAccess(nodeId, userIdInput, options = {}) {
|
|
|
52
52
|
const database = options.database || openLocalDb();
|
|
53
53
|
const userId = cleanUserId(userIdInput);
|
|
54
54
|
const target = nodeRow(database, nodeId);
|
|
55
|
-
|
|
55
|
+
// `missing_at` is a durable observation that the physical node disappeared;
|
|
56
|
+
// it does not erase the UUID or its inherited authority. Reconciliation must
|
|
57
|
+
// still be able to authorize and journal that offline deletion. Purge alone
|
|
58
|
+
// retires the identity.
|
|
59
|
+
if (!target || target.purged_at) return null;
|
|
56
60
|
const parents = new Map(
|
|
57
61
|
database.prepare('SELECT id, parent_id FROM workspace_nodes WHERE workspace_id = ?')
|
|
58
62
|
.all(target.workspace_id)
|
|
@@ -23,7 +23,7 @@ const {
|
|
|
23
23
|
} = require('./git');
|
|
24
24
|
const { cacheRootCommit, machineId, replicaForGitDir } = require('./identity');
|
|
25
25
|
|
|
26
|
-
const CARD_CONTRACT = 'repo-card@
|
|
26
|
+
const CARD_CONTRACT = 'repo-card@3';
|
|
27
27
|
|
|
28
28
|
// A transportable identity needs a dotted host: bare names resolve through
|
|
29
29
|
// this machine's own config (ssh aliases, LAN names) and identify nothing
|
|
@@ -62,7 +62,6 @@ function cardKind(classified) {
|
|
|
62
62
|
|
|
63
63
|
function reusableObjectSource(previousOid, currentOid, source, resourceId) {
|
|
64
64
|
if (previousOid !== currentOid) return null;
|
|
65
|
-
if (source?.kind === 'remote') return source;
|
|
66
65
|
return source?.kind === 'bundle' && source.resourceId === resourceId ? source : null;
|
|
67
66
|
}
|
|
68
67
|
|
|
@@ -83,8 +82,9 @@ async function observeWorktree(classified, previousCard = null) {
|
|
|
83
82
|
}
|
|
84
83
|
|
|
85
84
|
const repoIdentity = remote === null ? null : remoteIdentity(remote);
|
|
86
|
-
const
|
|
87
|
-
|
|
85
|
+
const repoKey = repoIdentity || (anchor ? `root:${anchor}` : null);
|
|
86
|
+
const resourceId = repoKey
|
|
87
|
+
? require('./wall').projectForPath(worktreeRoot)?.resourceId
|
|
88
88
|
: null;
|
|
89
89
|
const previous = previousCard?.replica === replica.replicaId
|
|
90
90
|
&& previousCard.repo?.remote === repoIdentity
|
|
@@ -93,10 +93,10 @@ async function observeWorktree(classified, previousCard = null) {
|
|
|
93
93
|
const previousRefs = new Map((previous?.refs || []).map((ref) => [ref.name, ref]));
|
|
94
94
|
const objectSources = new Map();
|
|
95
95
|
const sourceFor = (oid, priorOid, priorSource) => {
|
|
96
|
-
if (!oid || !
|
|
96
|
+
if (!oid || !repoKey) return null;
|
|
97
97
|
if (objectSources.has(oid)) return objectSources.get(oid);
|
|
98
98
|
const source = reusableObjectSource(priorOid, oid, priorSource, resourceId)
|
|
99
|
-
|| require('./objects').objectSourceFor(worktreeRoot,
|
|
99
|
+
|| require('./objects').objectSourceFor(worktreeRoot, repoKey, oid);
|
|
100
100
|
objectSources.set(oid, source);
|
|
101
101
|
return source;
|
|
102
102
|
};
|
|
@@ -131,7 +131,7 @@ async function observeWorktree(classified, previousCard = null) {
|
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
/**
|
|
134
|
-
* Build the repo-card@
|
|
134
|
+
* Build the repo-card@3 for one worktree. `dirPath` may be any directory
|
|
135
135
|
* inside the worktree; the card is always for the worktree root. Throws
|
|
136
136
|
* on anything that is not a worktree — discovery filters first.
|
|
137
137
|
*
|
|
@@ -37,7 +37,7 @@ const service = {
|
|
|
37
37
|
reconcileTimer: null,
|
|
38
38
|
workspaceTimer: null,
|
|
39
39
|
unsubscribe: null,
|
|
40
|
-
doorbells: new Map(), // replicaId → {
|
|
40
|
+
doorbells: new Map(), // replicaId → { watchSet, timer }
|
|
41
41
|
counters: { refreshes: 0, doorbellRings: 0 },
|
|
42
42
|
lastRefresh: null,
|
|
43
43
|
};
|
|
@@ -443,7 +443,7 @@ function nestedDirectories(root) {
|
|
|
443
443
|
}
|
|
444
444
|
|
|
445
445
|
function watchGitDir(card) {
|
|
446
|
-
const
|
|
446
|
+
const watchSet = { handles: [], complete: true };
|
|
447
447
|
const ring = () => ringDoorbell(card.replica);
|
|
448
448
|
// The git dir itself carries HEAD, index, and operation markers; logs/
|
|
449
449
|
// carries the reflog, which moves on every commit, checkout, and reset.
|
|
@@ -459,9 +459,13 @@ function watchGitDir(card) {
|
|
|
459
459
|
for (const dir of new Set([...direct, ...refDirectories])) {
|
|
460
460
|
try {
|
|
461
461
|
const watcher = fs.watch(dir, ring);
|
|
462
|
-
watcher.on('error', () =>
|
|
463
|
-
|
|
462
|
+
watcher.on('error', () => {
|
|
463
|
+
watchSet.complete = false;
|
|
464
|
+
watcher.close();
|
|
465
|
+
});
|
|
466
|
+
watchSet.handles.push(watcher);
|
|
464
467
|
} catch {
|
|
468
|
+
watchSet.complete = false;
|
|
465
469
|
// The directory vanished between observation and watch; the next
|
|
466
470
|
// reconcile pass re-syncs the bells.
|
|
467
471
|
}
|
|
@@ -472,27 +476,31 @@ function watchGitDir(card) {
|
|
|
472
476
|
if (relative === '.git' || relative.startsWith('.git/')) return;
|
|
473
477
|
ring();
|
|
474
478
|
});
|
|
475
|
-
watcher.on('error', () =>
|
|
476
|
-
|
|
479
|
+
watcher.on('error', () => {
|
|
480
|
+
watchSet.complete = false;
|
|
481
|
+
watcher.close();
|
|
482
|
+
});
|
|
483
|
+
watchSet.handles.push(watcher);
|
|
477
484
|
} catch {
|
|
485
|
+
watchSet.complete = false;
|
|
478
486
|
// The periodic card refresh remains the fallback on filesystems that
|
|
479
487
|
// cannot watch a checkout recursively.
|
|
480
488
|
}
|
|
481
|
-
return
|
|
489
|
+
return watchSet;
|
|
482
490
|
}
|
|
483
491
|
|
|
484
492
|
function rebuildDoorbell(replicaId, card) {
|
|
485
493
|
const bell = service.doorbells.get(replicaId);
|
|
486
494
|
if (!bell) return;
|
|
487
|
-
const previous = bell.
|
|
488
|
-
|
|
489
|
-
|
|
495
|
+
const previous = bell.watchSet;
|
|
496
|
+
for (const watcher of previous.handles) watcher.close();
|
|
497
|
+
bell.watchSet = watchGitDir(card);
|
|
490
498
|
}
|
|
491
499
|
|
|
492
500
|
function dropDoorbell(replicaId) {
|
|
493
501
|
const bell = service.doorbells.get(replicaId);
|
|
494
502
|
if (!bell) return;
|
|
495
|
-
for (const watcher of bell.
|
|
503
|
+
for (const watcher of bell.watchSet.handles) watcher.close();
|
|
496
504
|
if (bell.timer) clearTimeout(bell.timer);
|
|
497
505
|
service.doorbells.delete(replicaId);
|
|
498
506
|
}
|
|
@@ -507,7 +515,9 @@ function syncDoorbells(cards) {
|
|
|
507
515
|
}
|
|
508
516
|
for (const [replicaId, card] of healthy) {
|
|
509
517
|
if (!service.doorbells.has(replicaId)) {
|
|
510
|
-
service.doorbells.set(replicaId, {
|
|
518
|
+
service.doorbells.set(replicaId, { watchSet: watchGitDir(card), timer: null });
|
|
519
|
+
} else if (!service.doorbells.get(replicaId).watchSet.complete) {
|
|
520
|
+
rebuildDoorbell(replicaId, card);
|
|
511
521
|
}
|
|
512
522
|
}
|
|
513
523
|
}
|
|
@@ -552,6 +562,8 @@ function repoCardServiceStats() {
|
|
|
552
562
|
refreshes: service.counters.refreshes,
|
|
553
563
|
doorbellRings: service.counters.doorbellRings,
|
|
554
564
|
doorbells: service.doorbells.size,
|
|
565
|
+
incompleteDoorbells: Array.from(service.doorbells.values())
|
|
566
|
+
.filter((bell) => !bell.watchSet.complete).length,
|
|
555
567
|
lastRefresh: service.lastRefresh,
|
|
556
568
|
};
|
|
557
569
|
}
|
|
@@ -563,4 +575,16 @@ module.exports = {
|
|
|
563
575
|
repoCardServiceStats,
|
|
564
576
|
startRepoCardService,
|
|
565
577
|
stopRepoCardService,
|
|
578
|
+
// Test hooks only. Filesystem delivery itself belongs to the host; these
|
|
579
|
+
// expose the deterministic doorbell boundary behind that latency hint.
|
|
580
|
+
_private: {
|
|
581
|
+
rebuildDoorbell,
|
|
582
|
+
ringDoorbell,
|
|
583
|
+
watcherCount(replicaId) {
|
|
584
|
+
return service.doorbells.get(replicaId)?.watchSet.handles.length || 0;
|
|
585
|
+
},
|
|
586
|
+
watcherComplete(replicaId) {
|
|
587
|
+
return service.doorbells.get(replicaId)?.watchSet.complete === true;
|
|
588
|
+
},
|
|
589
|
+
},
|
|
566
590
|
};
|