@almadar/workspace 0.11.5 → 0.11.6

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.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.d.ts CHANGED
@@ -11,5 +11,6 @@ export { listWorkspaces } from './list-workspaces.js';
11
11
  export { deleteWorkspace } from './delete-workspace.js';
12
12
  export { openAccount } from './account.js';
13
13
  export type { WorkspaceService, InstantiateWorkspaceSeed, WorkspaceObserver, WorkspaceWriteEvent, WorkspaceWatchEvent, OpenWorkspaceOptions, MountEntry, ListWorkspacesOptions, WorkspaceSummary, RestoreBackend, GitHubConfig, GitStatusInfo, SnapshotMeta, WorkspaceArchiveBackend, FileTreeNode, AccountService, AccountConfig, ProviderCredential, AuthToken, AccountIdentity, OpenAccountOptions, } from './types.js';
14
- export type { WorkspaceIndex, EmbedderPort, ResolveResult, ResolveOptions, TraitRefEmit, WorkspaceIndexStats, OrbitalIndexEntry, ExtraTraitIdentity, RetrievalResult, RetrievalOptions, RecentlyEditedOptions, EventEdge, EntityBinding, RuleBinding, RecencyEntry, IntentMaps, ComposedMaps, BM25Document, BM25Table, BM25Options, WorkspaceIndexManifest, } from './workspace-index/types.js';
15
- export { DEFAULT_COERCION_THRESHOLD, DEFAULT_RETRIEVAL_TOP_K, RRF_K, WORKSPACE_INDEX_SCHEMA_VERSION, } from './workspace-index/types.js';
14
+ export type { WorkspaceIndex, EmbedderPort, WorkspaceIndexStats, OrbitalIndexEntry, ExtraTraitIdentity, RetrievalResult, RetrievalOptions, RecentlyEditedOptions, EventEdge, EntityBinding, RuleBinding, RecencyEntry, IntentMaps, ComposedMaps, BM25Document, BM25Table, BM25Options, WorkspaceIndexManifest, } from './workspace-index/types.js';
15
+ export { DEFAULT_RETRIEVAL_TOP_K, RRF_K, WORKSPACE_INDEX_SCHEMA_VERSION, } from './workspace-index/types.js';
16
+ export type { WorkspaceLedger } from './workspace-ledger/types.js';
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ import fs, { existsSync } from 'fs';
3
3
  import { execFile } from 'child_process';
4
4
  import dugite from 'dugite';
5
5
  import { randomBytes, createHash } from 'crypto';
6
+ import { ledgerResolveName, mintId, ledgerRename, ledgerCurName, IdentityLedgerSchema } from '@almadar/core';
6
7
  import { EmbeddingClient } from '@almadar/llm';
7
8
  import os from 'os';
8
9
 
@@ -22,6 +23,10 @@ var LocalBackend = class {
22
23
  fs.mkdirSync(path2.dirname(absPath), { recursive: true });
23
24
  fs.writeFileSync(absPath, content, "utf-8");
24
25
  }
26
+ async appendFile(absPath, content) {
27
+ await fs.promises.mkdir(path2.dirname(absPath), { recursive: true });
28
+ await fs.promises.appendFile(absPath, content, "utf-8");
29
+ }
25
30
  async readFileBytes(absPath) {
26
31
  return fs.promises.readFile(absPath);
27
32
  }
@@ -102,6 +107,9 @@ var MemoryBackend = class {
102
107
  writeFileSync(absPath, content) {
103
108
  this.files.set(absPath, content);
104
109
  }
110
+ async appendFile(absPath, content) {
111
+ this.files.set(absPath, (this.files.get(absPath) ?? "") + content);
112
+ }
105
113
  async readFileBytes(absPath) {
106
114
  const b = this.bytes.get(absPath);
107
115
  if (b === void 0) throw new Error(`ENOENT: ${absPath}`);
@@ -293,6 +301,9 @@ function appMarkerFile(workDir) {
293
301
  function workspaceIndexManifestFile(workDir) {
294
302
  return path2.join(workDir, WORKSPACE_LAYOUT.ALMADAR_DIR, "index.json");
295
303
  }
304
+ function workspaceLedgerFile(workDir) {
305
+ return path2.join(workDir, WORKSPACE_LAYOUT.ALMADAR_DIR, "ledger.json");
306
+ }
296
307
  function sandboxedPath(workDir, relPath, mounts) {
297
308
  if (typeof relPath !== "string" || relPath.length === 0) {
298
309
  throw new Error("sandbox: empty relative path");
@@ -676,14 +687,11 @@ async function writeJsonFile(backend, absPath, value) {
676
687
  await backend.writeFile(absPath, JSON.stringify(value, null, 2));
677
688
  }
678
689
  async function appendJsonLine(backend, absPath, value) {
679
- const existing = backend.exists(absPath) ? await backend.readFile(absPath) : "";
680
- const ending = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
681
- await backend.writeFile(absPath, existing + ending + JSON.stringify(value) + "\n");
690
+ await backend.appendFile(absPath, JSON.stringify(value) + "\n");
682
691
  }
683
692
 
684
693
  // src/workspace-index/types.ts
685
694
  var WORKSPACE_INDEX_SCHEMA_VERSION = 2;
686
- var DEFAULT_COERCION_THRESHOLD = 0.85;
687
695
  var RRF_K = 60;
688
696
  var DEFAULT_RETRIEVAL_TOP_K = 3;
689
697
 
@@ -1339,62 +1347,6 @@ var WorkspaceIndexImpl = class {
1339
1347
  }
1340
1348
  await this.rebuildManifest();
1341
1349
  }
1342
- async resolveOrbitalName(name, opts) {
1343
- const threshold = opts?.threshold ?? DEFAULT_COERCION_THRESHOLD;
1344
- if (this.entries.size === 0) {
1345
- return { coercedTo: null, similarity: 0, method: "identity-vector" };
1346
- }
1347
- let vector;
1348
- try {
1349
- [vector] = await this.embedOne(name);
1350
- } catch (err) {
1351
- const reason = err instanceof Error ? err.message : String(err);
1352
- console.warn(`[workspace-index] resolveOrbitalName: embedder failed, returning null (${reason})`);
1353
- return { coercedTo: null, similarity: 0, method: "identity-vector" };
1354
- }
1355
- let bestOrbital = null;
1356
- let bestSim = 0;
1357
- for (const [orbital, entry] of this.entries) {
1358
- const sim = cosineSimilarity(vector, entry.identityVector);
1359
- if (sim > bestSim) {
1360
- bestSim = sim;
1361
- bestOrbital = orbital;
1362
- }
1363
- }
1364
- if (bestOrbital !== null && bestSim >= threshold) {
1365
- return { coercedTo: bestOrbital, similarity: bestSim, method: "identity-vector" };
1366
- }
1367
- return { coercedTo: null, similarity: bestSim, method: "identity-vector" };
1368
- }
1369
- async resolveTraitRef(emit, orbitalContext, opts) {
1370
- const threshold = opts?.threshold ?? DEFAULT_COERCION_THRESHOLD;
1371
- const entry = this.entries.get(orbitalContext.orbitalName);
1372
- if (!entry || entry.extraTraitIdentities.length === 0) {
1373
- return { coercedTo: null, similarity: 0, method: "identity-vector" };
1374
- }
1375
- const fingerprint = composeExtraTraitIdentityFingerprint(emit);
1376
- let vector;
1377
- try {
1378
- [vector] = await this.embedOne(fingerprint);
1379
- } catch (err) {
1380
- const reason = err instanceof Error ? err.message : String(err);
1381
- console.warn(`[workspace-index] resolveTraitRef: embedder failed, returning null (${reason})`);
1382
- return { coercedTo: null, similarity: 0, method: "identity-vector" };
1383
- }
1384
- let bestAlias = null;
1385
- let bestSim = 0;
1386
- for (const candidate of entry.extraTraitIdentities) {
1387
- const sim = cosineSimilarity(vector, candidate.vector);
1388
- if (sim > bestSim) {
1389
- bestSim = sim;
1390
- bestAlias = candidate.alias;
1391
- }
1392
- }
1393
- if (bestAlias !== null && bestSim >= threshold) {
1394
- return { coercedTo: bestAlias, similarity: bestSim, method: "identity-vector" };
1395
- }
1396
- return { coercedTo: null, similarity: bestSim, method: "identity-vector" };
1397
- }
1398
1350
  stats() {
1399
1351
  let extraTraitIdentityCount = 0;
1400
1352
  for (const entry of this.entries.values()) {
@@ -1764,6 +1716,172 @@ function serializeMapOfArrays(map, projectOne) {
1764
1716
  }
1765
1717
  return out;
1766
1718
  }
1719
+ function ledgerPath(workDir) {
1720
+ return workspaceLedgerFile(workDir);
1721
+ }
1722
+ async function readLedger(backend, workDir) {
1723
+ const p = ledgerPath(workDir);
1724
+ if (!backend.exists(p)) return null;
1725
+ try {
1726
+ const raw = await backend.readFile(p);
1727
+ const result = IdentityLedgerSchema.safeParse(JSON.parse(raw));
1728
+ return result.success ? result.data : null;
1729
+ } catch {
1730
+ return null;
1731
+ }
1732
+ }
1733
+ async function writeLedger(backend, workDir, ledger) {
1734
+ await backend.writeFile(ledgerPath(workDir), JSON.stringify(serializeLedger(ledger), null, 2));
1735
+ }
1736
+ function serializeLedger(ledger) {
1737
+ const entries = {};
1738
+ for (const [id, entry] of Object.entries(ledger.entries)) {
1739
+ entries[id] = serializeEntry(entry);
1740
+ }
1741
+ return { schemaVersion: ledger.schemaVersion, entries };
1742
+ }
1743
+ function serializeEntry(entry) {
1744
+ const row = {
1745
+ id: entry.id,
1746
+ kind: entry.kind,
1747
+ bakedName: entry.bakedName,
1748
+ curName: entry.curName,
1749
+ renames: entry.renames.map((r) => ({ from: r.from, to: r.to, at: r.at })),
1750
+ owner: entry.owner
1751
+ };
1752
+ if (entry.parent !== void 0) row.parent = entry.parent;
1753
+ return row;
1754
+ }
1755
+
1756
+ // src/workspace-ledger/ledger-impl.ts
1757
+ var WorkspaceLedgerImpl = class {
1758
+ constructor(deps) {
1759
+ this.deps = deps;
1760
+ this.ledger = emptyLedger();
1761
+ /** Serialized persists so concurrent writes don't race the file. */
1762
+ this.persistQueue = Promise.resolve();
1763
+ this.deps.sinks.subscribe({
1764
+ onWrite: (event) => this.onWorkspaceWrite(event)
1765
+ });
1766
+ }
1767
+ async onWorkspaceWrite(event) {
1768
+ if (event.kind !== "orbital") return;
1769
+ const slice = extractLedgerSlice(event.content);
1770
+ if (slice !== null) {
1771
+ this.ingestOrbSlice(slice);
1772
+ } else if (isJsonObject(event.content) && this.resolve("orbital", event.name) === null) {
1773
+ this.mint("orbital", event.name);
1774
+ }
1775
+ await this.persistQueue;
1776
+ }
1777
+ resolve(kind, name) {
1778
+ return ledgerResolveName(this.ledger, kind, name);
1779
+ }
1780
+ mint(kind, name) {
1781
+ if (kind === "palette") {
1782
+ throw new Error("WorkspaceLedger.mint: palette ids are manifest-owned, not name-ledger rows");
1783
+ }
1784
+ if (this.resolve(kind, name) !== null) {
1785
+ throw new Error(`WorkspaceLedger.mint: duplicate curName "${name}" within kind "${kind}"`);
1786
+ }
1787
+ const id = mintId(kind);
1788
+ const entry = {
1789
+ id,
1790
+ kind,
1791
+ bakedName: name,
1792
+ curName: name,
1793
+ renames: [],
1794
+ owner: "workspace"
1795
+ };
1796
+ this.ledger = { ...this.ledger, entries: { ...this.ledger.entries, [id]: entry } };
1797
+ void this.persist();
1798
+ return id;
1799
+ }
1800
+ rename(id, to, at) {
1801
+ if (this.ledger.entries[id] === void 0) {
1802
+ throw new Error(`WorkspaceLedger.rename: unknown id "${id}"`);
1803
+ }
1804
+ this.ledger = ledgerRename(this.ledger, id, to, String(at));
1805
+ void this.persist();
1806
+ }
1807
+ curName(id) {
1808
+ return ledgerCurName(this.ledger, id);
1809
+ }
1810
+ entry(id) {
1811
+ return this.ledger.entries[id] ?? null;
1812
+ }
1813
+ snapshot() {
1814
+ return cloneLedger(this.ledger);
1815
+ }
1816
+ ingestOrbSlice(slice) {
1817
+ const entries = { ...this.ledger.entries };
1818
+ let changed = false;
1819
+ for (const [id, entry] of Object.entries(slice.entries)) {
1820
+ if (entries[id] === void 0) {
1821
+ entries[id] = entry;
1822
+ changed = true;
1823
+ }
1824
+ }
1825
+ if (!changed) return;
1826
+ this.ledger = { ...this.ledger, entries };
1827
+ void this.persist();
1828
+ }
1829
+ async warm() {
1830
+ const loaded = await readLedger(this.deps.backend, this.deps.workDir);
1831
+ this.ledger = loaded ?? emptyLedger();
1832
+ const names = this.deps.listOrbitals();
1833
+ for (const name of names) {
1834
+ const content = this.deps.readOrbital(name);
1835
+ if (content === null) continue;
1836
+ const slice = extractLedgerSlice(content);
1837
+ if (slice !== null) this.ingestOrbSlice(slice);
1838
+ }
1839
+ for (const name of names) {
1840
+ if (this.resolve("orbital", name) === null) this.mint("orbital", name);
1841
+ }
1842
+ await this.persistQueue;
1843
+ }
1844
+ persist() {
1845
+ const snapshot = this.ledger;
1846
+ const next = this.persistQueue.then(async () => {
1847
+ await writeLedger(this.deps.backend, this.deps.workDir, snapshot);
1848
+ await this.deps.sinks.notifyAll({
1849
+ kind: "workspace-ledger",
1850
+ content: serializeLedger(snapshot)
1851
+ });
1852
+ });
1853
+ this.persistQueue = next.catch(() => void 0);
1854
+ return next;
1855
+ }
1856
+ };
1857
+ function emptyLedger() {
1858
+ return { schemaVersion: 1, entries: {} };
1859
+ }
1860
+ function cloneLedger(ledger) {
1861
+ const entries = {};
1862
+ for (const [id, entry] of Object.entries(ledger.entries)) {
1863
+ entries[id] = { ...entry, renames: entry.renames.map((r) => ({ ...r })) };
1864
+ }
1865
+ return { schemaVersion: ledger.schemaVersion, entries };
1866
+ }
1867
+ function isJsonObject(content) {
1868
+ try {
1869
+ const root = JSON.parse(content);
1870
+ return root !== null && typeof root === "object" && !Array.isArray(root);
1871
+ } catch {
1872
+ return false;
1873
+ }
1874
+ }
1875
+ function extractLedgerSlice(content) {
1876
+ try {
1877
+ const root = JSON.parse(content);
1878
+ if (root === null || typeof root !== "object" || Array.isArray(root)) return null;
1879
+ const result = IdentityLedgerSchema.safeParse(root.ledger);
1880
+ return result.success ? result.data : null;
1881
+ } catch {
1882
+ return null;
1883
+ }
1884
+ }
1767
1885
 
1768
1886
  // src/service.ts
1769
1887
  var COORD_FILES = {
@@ -1803,6 +1921,13 @@ var WorkspaceServiceImpl = class {
1803
1921
  readSchema: () => this.readSchema(),
1804
1922
  readHistory: (orbital) => this.readHistory(orbital)
1805
1923
  });
1924
+ this.ledger = new WorkspaceLedgerImpl({
1925
+ workDir: this.workDir,
1926
+ backend: this.backend,
1927
+ sinks: this.sinks,
1928
+ listOrbitals: () => this.listOrbitals(),
1929
+ readOrbital: (name) => this.readOrbital(name)
1930
+ });
1806
1931
  if (args.mounts) {
1807
1932
  for (const m of args.mounts) this.mountMap.set(m.prefix, path2.resolve(m.absPath));
1808
1933
  }
@@ -2409,6 +2534,25 @@ async function openWorkspace(opts) {
2409
2534
  }
2410
2535
  return openWorkspaceInternal(opts);
2411
2536
  }
2537
+ var WORKSPACE_OWNED_ENTRIES = /* @__PURE__ */ new Set([".almadar", "orbitals", "schema.orb", ".git", ".gitignore"]);
2538
+ async function guardForeignMintTarget(backend, workDir) {
2539
+ if (readAppMarker(backend, workDir) !== null) return;
2540
+ if (backend.exists(path2.join(workDir, "schema.orb"))) return;
2541
+ if (backend.exists(path2.join(workDir, ".almadar", "sessions"))) return;
2542
+ if (!backend.exists(workDir)) return;
2543
+ if (process.env["ALMADAR_WORKSPACE_ADOPT_NONEMPTY"] === "1") return;
2544
+ let entries;
2545
+ try {
2546
+ entries = await backend.readdir(workDir);
2547
+ } catch {
2548
+ return;
2549
+ }
2550
+ const foreign = entries.filter((e) => !WORKSPACE_OWNED_ENTRIES.has(e));
2551
+ if (foreign.length === 0) return;
2552
+ throw new Error(
2553
+ `refusing to mint a workspace into ${workDir} \u2014 it already contains non-workspace content (${foreign.slice(0, 5).join(", ")}${foreign.length > 5 ? ", \u2026" : ""}). Pass an empty/dedicated workDir, or set ALMADAR_WORKSPACE_ADOPT_NONEMPTY=1 to adopt this directory anyway.`
2554
+ );
2555
+ }
2412
2556
  async function openWorkspaceInternal(opts) {
2413
2557
  const backend = opts.backend === "memory" ? new MemoryBackend() : new LocalBackend();
2414
2558
  const sinks = new SinkManager();
@@ -2416,6 +2560,7 @@ async function openWorkspaceInternal(opts) {
2416
2560
  if (resolved === null) return null;
2417
2561
  const bare = opts.bare === true && opts.adopt !== void 0;
2418
2562
  if (!bare) {
2563
+ await guardForeignMintTarget(backend, resolved.workDir);
2419
2564
  await ensureSkeleton(backend, resolved.workDir);
2420
2565
  await writeMintTemplatesIfMissing(
2421
2566
  backend,
@@ -2458,6 +2603,7 @@ async function openWorkspaceInternal(opts) {
2458
2603
  });
2459
2604
  if (!bare) {
2460
2605
  await service.index.warm();
2606
+ await service.ledger.warm();
2461
2607
  }
2462
2608
  return service;
2463
2609
  }
@@ -2647,6 +2793,6 @@ async function openAccount(opts = {}) {
2647
2793
  };
2648
2794
  }
2649
2795
 
2650
- export { DEFAULT_COERCION_THRESHOLD, DEFAULT_RETRIEVAL_TOP_K, RRF_K, WORKSPACE_INDEX_SCHEMA_VERSION, deleteWorkspace, listWorkspaces, openAccount, openWorkspace };
2796
+ export { DEFAULT_RETRIEVAL_TOP_K, RRF_K, WORKSPACE_INDEX_SCHEMA_VERSION, deleteWorkspace, listWorkspaces, openAccount, openWorkspace };
2651
2797
  //# sourceMappingURL=index.js.map
2652
2798
  //# sourceMappingURL=index.js.map