@almadar/workspace 0.11.5 → 0.11.7

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.
@@ -4,6 +4,7 @@ export declare class LocalBackend implements WorkspaceBackend {
4
4
  readFileSync(absPath: string): string;
5
5
  writeFile(absPath: string, content: string): Promise<void>;
6
6
  writeFileSync(absPath: string, content: string): void;
7
+ appendFile(absPath: string, content: string): Promise<void>;
7
8
  readFileBytes(absPath: string): Promise<Uint8Array>;
8
9
  writeFileBytes(absPath: string, bytes: Uint8Array): Promise<void>;
9
10
  exists(absPath: string): boolean;
@@ -10,6 +10,7 @@ export declare class MemoryBackend implements WorkspaceBackend {
10
10
  readFileSync(absPath: string): string;
11
11
  writeFile(absPath: string, content: string): Promise<void>;
12
12
  writeFileSync(absPath: string, content: string): void;
13
+ appendFile(absPath: string, content: string): Promise<void>;
13
14
  readFileBytes(absPath: string): Promise<Uint8Array>;
14
15
  writeFileBytes(absPath: string, bytes: Uint8Array): Promise<void>;
15
16
  exists(absPath: string): boolean;
@@ -22,5 +22,11 @@ export declare function readJsonLines<T extends JsonObject>(backend: WorkspaceBa
22
22
  * Append a single JSON value as a line. Used for trace + history streams.
23
23
  * Caller is responsible for any per-path serialization (the service's
24
24
  * write queue handles that).
25
+ *
26
+ * Must be a REAL append (O_APPEND), never read-concat-rewrite: a rewrite
27
+ * truncates first, so a process killed mid-write zeroes the entire stream
28
+ * (C-TRACE-APPEND-TRUNCATE — the 0-byte trace.jsonl class). This function is
29
+ * the sole writer of these streams, so every line it writes ends in `\n` and
30
+ * the file never needs a separator probe.
25
31
  */
26
32
  export declare function appendJsonLine<T extends JsonObject>(backend: WorkspaceBackend, absPath: string, value: T): Promise<void>;
@@ -11,16 +11,16 @@
11
11
  */
12
12
  /** Single source of truth for workspace directory layout. */
13
13
  export declare const WORKSPACE_LAYOUT: {
14
- readonly ALMADAR_DIR: ".almadar";
15
- readonly ORBITALS_DIR: "orbitals";
16
- readonly SESSIONS_DIR: ".almadar/sessions";
17
- readonly COORDINATOR_DIR: ".almadar/sessions/Coordinator";
18
- readonly TRACE_FILE: ".almadar/trace.jsonl";
19
- readonly SCHEMA_FILE: "schema.orb";
20
- readonly COMPILED_DIR: "apps";
21
- readonly APP_MARKER: ".almadar/app-marker.json";
22
- readonly USER_MEMORY: ".almadar/user.orb";
23
- readonly PROJECT_MEMORY: ".almadar/project.orb";
14
+ readonly ALMADAR_DIR: '.almadar';
15
+ readonly ORBITALS_DIR: 'orbitals';
16
+ readonly SESSIONS_DIR: '.almadar/sessions';
17
+ readonly COORDINATOR_DIR: '.almadar/sessions/Coordinator';
18
+ readonly TRACE_FILE: '.almadar/trace.jsonl';
19
+ readonly SCHEMA_FILE: 'schema.orb';
20
+ readonly COMPILED_DIR: 'apps';
21
+ readonly APP_MARKER: '.almadar/app-marker.json';
22
+ readonly USER_MEMORY: '.almadar/user.orb';
23
+ readonly PROJECT_MEMORY: '.almadar/project.orb';
24
24
  };
25
25
  /** Logical orbital name → absolute `.orb` path. */
26
26
  export declare function orbitalFile(workDir: string, name: string): string;
@@ -46,6 +46,8 @@ export declare function compiledFile(workDir: string, relPath: string): string;
46
46
  export declare function appMarkerFile(workDir: string): string;
47
47
  /** Workspace-level index manifest path. */
48
48
  export declare function workspaceIndexManifestFile(workDir: string): string;
49
+ /** Workspace-level identity-ledger path. */
50
+ export declare function workspaceLedgerFile(workDir: string): string;
49
51
  /**
50
52
  * Resolve a user-supplied relative path within the sandbox. Rejects
51
53
  * absolute paths, `..` traversal, and any path that resolves outside
@@ -13,6 +13,12 @@ export interface WorkspaceBackend {
13
13
  readFileSync(absPath: string): string;
14
14
  writeFile(absPath: string, content: string): Promise<void>;
15
15
  writeFileSync(absPath: string, content: string): void;
16
+ /**
17
+ * Append to a file (create if absent) without truncating — O_APPEND on real
18
+ * filesystems. Required for line streams (trace/history): a truncate-rewrite
19
+ * "append" zeroes the whole stream when the process is killed mid-write.
20
+ */
21
+ appendFile(absPath: string, content: string): Promise<void>;
16
22
  /** Binary read/write — for git bundle artifacts (durable archive bytes). */
17
23
  readFileBytes(absPath: string): Promise<Uint8Array>;
18
24
  writeFileBytes(absPath: string, bytes: Uint8Array): Promise<void>;
package/dist/service.d.ts CHANGED
@@ -12,6 +12,7 @@ import type { WorkspaceBackend } from './internal/types.js';
12
12
  import { SinkManager } from './internal/sink-manager.js';
13
13
  import { GitClient } from './internal/git-client.js';
14
14
  import type { EmbedderPort, WorkspaceIndex } from './workspace-index/types.js';
15
+ import type { WorkspaceLedger } from './workspace-ledger/types.js';
15
16
  interface ServiceCtorArgs {
16
17
  workDir: string;
17
18
  backend: WorkspaceBackend;
@@ -36,6 +37,7 @@ export declare class WorkspaceServiceImpl implements WorkspaceService {
36
37
  /** Mount prefix → resolved absolute directory. Built from constructor `mounts` + runtime `mountReadonly`. */
37
38
  private readonly mountMap;
38
39
  readonly index: WorkspaceIndex;
40
+ readonly ledger: WorkspaceLedger;
39
41
  constructor(args: ServiceCtorArgs);
40
42
  get appId(): string | undefined;
41
43
  setAppId(id: string): void;
package/dist/types.d.ts CHANGED
@@ -9,6 +9,7 @@
9
9
  import type { JsonObject, JsonValue, OrbitalSchema } from '@almadar/core';
10
10
  import type { ProviderConfig } from '@almadar/llm';
11
11
  import type { EmbedderPort, WorkspaceIndex } from './workspace-index/types.js';
12
+ import type { WorkspaceLedger } from './workspace-ledger/types.js';
12
13
  /**
13
14
  * The only extension point. Consumers register one observer via
14
15
  * `service.subscribe(observer)`; every workspace write fans out
@@ -100,6 +101,9 @@ export type WorkspaceWriteEvent = {
100
101
  } | {
101
102
  kind: 'workspace-index-manifest';
102
103
  content: JsonObject;
104
+ } | {
105
+ kind: 'workspace-ledger';
106
+ content: JsonObject;
103
107
  };
104
108
  /**
105
109
  * Emitted by {@link WorkspaceService.watch} when storage changes underneath the
@@ -383,6 +387,7 @@ export interface WorkspaceService {
383
387
  */
384
388
  watch(relPath: string, onChange: (event: WorkspaceWatchEvent) => void): () => void;
385
389
  readonly index: WorkspaceIndex;
390
+ readonly ledger: WorkspaceLedger;
386
391
  dispose(): Promise<void>;
387
392
  }
388
393
  /**
@@ -45,8 +45,7 @@ export declare function composeExtraTraitIdentityFingerprint(emit: {
45
45
  /**
46
46
  * Derive the alias an entry would canonically be referenced by. Used by
47
47
  * the sidecar baker to store the `alias` field next to each extraTrait
48
- * identity vector — also the value `resolveTraitRef` returns as
49
- * `coercedTo` when a match fires.
48
+ * identity vector.
50
49
  */
51
50
  export declare function deriveExtraTraitAlias(emit: {
52
51
  ref: string;
@@ -9,7 +9,7 @@
9
9
  import type { JsonObject } from '@almadar/core';
10
10
  import type { WorkspaceBackend } from '../internal/types.js';
11
11
  import type { SinkManager } from '../internal/sink-manager.js';
12
- import type { EmbedderPort, EntityBinding, EventEdge, RecentlyEditedOptions, ResolveOptions, ResolveResult, RetrievalOptions, RetrievalResult, RuleBinding, TraitRefEmit, WorkspaceIndex, WorkspaceIndexStats } from './types.js';
12
+ import type { EmbedderPort, EntityBinding, EventEdge, RecentlyEditedOptions, RetrievalOptions, RetrievalResult, RuleBinding, WorkspaceIndex, WorkspaceIndexStats } from './types.js';
13
13
  export interface WorkspaceIndexDeps {
14
14
  workDir: string;
15
15
  backend: WorkspaceBackend;
@@ -42,10 +42,6 @@ export declare class WorkspaceIndexImpl implements WorkspaceIndex {
42
42
  constructor(deps: WorkspaceIndexDeps);
43
43
  private onWorkspaceWrite;
44
44
  warm(): Promise<void>;
45
- resolveOrbitalName(name: string, opts?: ResolveOptions): Promise<ResolveResult>;
46
- resolveTraitRef(emit: TraitRefEmit, orbitalContext: {
47
- orbitalName: string;
48
- }, opts?: ResolveOptions): Promise<ResolveResult>;
49
45
  stats(): WorkspaceIndexStats;
50
46
  retrieveOrbitalsForPrompt(prompt: string, opts?: RetrievalOptions): Promise<readonly RetrievalResult[]>;
51
47
  findByToken(query: string): readonly string[];
@@ -4,8 +4,8 @@
4
4
  *
5
5
  * @packageDocumentation
6
6
  */
7
- export type { EmbedderPort, ExtraTraitIdentity, OrbitalIndexEntry, ResolveOptions, ResolveResult, TraitRefEmit, WorkspaceIndex, WorkspaceIndexStats, } from './types.js';
8
- export { DEFAULT_COERCION_THRESHOLD, WORKSPACE_INDEX_SCHEMA_VERSION, } from './types.js';
7
+ export type { EmbedderPort, ExtraTraitIdentity, OrbitalIndexEntry, WorkspaceIndex, WorkspaceIndexStats, } from './types.js';
8
+ export { WORKSPACE_INDEX_SCHEMA_VERSION, } from './types.js';
9
9
  export { WorkspaceIndexImpl } from './index-impl.js';
10
10
  export type { WorkspaceIndexDeps } from './index-impl.js';
11
11
  export { createDefaultEmbedder } from './embedder.js';
@@ -14,8 +14,6 @@ import type { JsonObject } from '@almadar/core';
14
14
  * Existing v1 sidecars get re-baked transparently on next openWorkspace.
15
15
  */
16
16
  export declare const WORKSPACE_INDEX_SCHEMA_VERSION: 2;
17
- /** Default coercion threshold per the doc's locked decision. */
18
- export declare const DEFAULT_COERCION_THRESHOLD: 0.85;
19
17
  /** RRF fusion constant (Elastic / OpenSearch convention). */
20
18
  export declare const RRF_K: 60;
21
19
  /** Default top-K for retrieveOrbitalsForPrompt. */
@@ -68,33 +66,6 @@ export interface OrbitalIndexEntry {
68
66
  /** Epoch ms at bake time — debugging only. */
69
67
  bakedAt: number;
70
68
  }
71
- /**
72
- * Input to `resolveTraitRef` — the LLM's emitted extraTraits entry,
73
- * narrowed to the fields the coercion needs to see. Mirrors the
74
- * `RawExtraTraitRef` shape rabit's analyzer constructs.
75
- */
76
- export interface TraitRefEmit {
77
- ref: string;
78
- name?: string;
79
- linkedEntity?: string;
80
- }
81
- /**
82
- * Common result shape for both `resolveOrbitalName` and
83
- * `resolveTraitRef`. `coercedTo` is null when no existing entity
84
- * exceeded the threshold — the emit is genuinely new.
85
- */
86
- export interface ResolveResult {
87
- coercedTo: string | null;
88
- similarity: number;
89
- method: 'identity-vector';
90
- }
91
- /**
92
- * Options for both `resolveOrbitalName` and `resolveTraitRef`. The
93
- * default threshold (0.85) lives in `DEFAULT_COERCION_THRESHOLD`.
94
- */
95
- export interface ResolveOptions {
96
- threshold?: number;
97
- }
98
69
  /**
99
70
  * Stats surface for diagnostics. Cheap to compute (read in-memory
100
71
  * state).
@@ -116,26 +87,6 @@ export interface WorkspaceIndex {
116
87
  * already-warm sidecar is a no-op (checksum matches).
117
88
  */
118
89
  warm(): Promise<void>;
119
- /**
120
- * R-10 coercion. Embed `name`, cosine-match against every orbital's
121
- * identity vector, return the best match if it exceeds the
122
- * threshold. Returns `{ coercedTo: null, ... }` when below threshold
123
- * or when no orbitals exist in the workspace.
124
- */
125
- resolveOrbitalName(name: string, opts?: ResolveOptions): Promise<ResolveResult>;
126
- /**
127
- * R-8 coercion. Embed an LLM-emitted trait emit, cosine-match against
128
- * the existing `extraTraits[]` identity vectors on the named orbital.
129
- * Returns the existing entry's `ref` (or its `name` rename) as
130
- * `coercedTo` when match exceeds threshold; null when the emit is a
131
- * genuinely new addition.
132
- *
133
- * Scoped per orbital — the same trait import on two different orbitals
134
- * is structurally legitimate, so we don't cross-coerce.
135
- */
136
- resolveTraitRef(emit: TraitRefEmit, orbitalContext: {
137
- orbitalName: string;
138
- }, opts?: ResolveOptions): Promise<ResolveResult>;
139
90
  /**
140
91
  * Phase B — RRF-hybrid prompt retrieval. Embed the prompt, run sparse
141
92
  * BM25 against the workspace token table, fuse via Reciprocal Rank
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Public surface of the workspace identity-ledger module. Re-exported from
3
+ * the top-level `@almadar/workspace` barrel.
4
+ *
5
+ * @packageDocumentation
6
+ */
7
+ export type { WorkspaceLedger } from './types.js';
8
+ export { WorkspaceLedgerImpl } from './ledger-impl.js';
9
+ export type { WorkspaceLedgerDeps } from './ledger-impl.js';
@@ -0,0 +1,42 @@
1
+ /**
2
+ * `WorkspaceLedgerImpl` — the runtime holding one `IdentityLedger` per
3
+ * workspace. A sibling of `WorkspaceIndexImpl`: it subscribes to the
4
+ * `SinkManager` to auto-ingest `.orb` ledger slices on `orbital` writes, and
5
+ * persists to `.almadar/ledger.json`, emitting a `workspace-ledger` mirror
6
+ * event after each persist.
7
+ *
8
+ * The workspace ledger is runtime-owned and authoritative: `ingestOrbSlice`
9
+ * only ever introduces NEW ids; an id already present keeps its row. Renames
10
+ * flow through explicit `rename()` calls (from rabit), never inferred from
11
+ * file events.
12
+ *
13
+ * @packageDocumentation
14
+ */
15
+ import type { IdKind, IdentityLedger, LedgerEntry, LedgerKind } from '@almadar/core';
16
+ import type { WorkspaceBackend } from '../internal/types.js';
17
+ import type { SinkManager } from '../internal/sink-manager.js';
18
+ import type { WorkspaceLedger } from './types.js';
19
+ export interface WorkspaceLedgerDeps {
20
+ workDir: string;
21
+ backend: WorkspaceBackend;
22
+ sinks: SinkManager;
23
+ listOrbitals: () => string[];
24
+ readOrbital: (name: string) => string | null;
25
+ }
26
+ export declare class WorkspaceLedgerImpl implements WorkspaceLedger {
27
+ private readonly deps;
28
+ private ledger;
29
+ /** Serialized persists so concurrent writes don't race the file. */
30
+ private persistQueue;
31
+ constructor(deps: WorkspaceLedgerDeps);
32
+ private onWorkspaceWrite;
33
+ resolve(kind: LedgerKind, name: string): string | null;
34
+ mint(kind: IdKind, name: string): string;
35
+ rename(id: string, to: string, at: number): void;
36
+ curName(id: string): string | null;
37
+ entry(id: string): LedgerEntry | null;
38
+ snapshot(): IdentityLedger;
39
+ ingestOrbSlice(slice: IdentityLedger): void;
40
+ warm(): Promise<void>;
41
+ private persist;
42
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Identity-ledger persistence — the single JSON file `.almadar/ledger.json`.
3
+ *
4
+ * Mirrors `workspace-index/manifest.ts`: same file helpers, same path-layout
5
+ * discipline. Reads validate against the core `IdentityLedgerSchema`; an
6
+ * unparseable / wrong-version file reads as `null` (the caller substitutes an
7
+ * empty ledger), matching the sidecar null-on-mismatch posture.
8
+ *
9
+ * @packageDocumentation
10
+ */
11
+ import type { JsonObject } from '@almadar/core';
12
+ import type { IdentityLedger } from '@almadar/core';
13
+ import type { WorkspaceBackend } from '../internal/types.js';
14
+ export declare function ledgerPath(workDir: string): string;
15
+ export declare function readLedger(backend: WorkspaceBackend, workDir: string): Promise<IdentityLedger | null>;
16
+ export declare function writeLedger(backend: WorkspaceBackend, workDir: string, ledger: IdentityLedger): Promise<void>;
17
+ /** Project the ledger into a plain `JsonObject` for persistence + the mirror event. */
18
+ export declare function serializeLedger(ledger: IdentityLedger): JsonObject;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Public surface of the workspace identity ledger (Rabit V4, Phase 6).
3
+ *
4
+ * One `IdentityLedger` (the core type, schemaVersion 1) per workspace — the
5
+ * runtime-owned, authoritative name↔id map. Resolution is exact within a kind;
6
+ * there is no similarity, no threshold, no fallback. Baked/emitted `.orb`
7
+ * slices only ever introduce NEW ids (row-level merge); an id already present
8
+ * keeps its workspace row.
9
+ *
10
+ * @packageDocumentation
11
+ */
12
+ import type { IdKind, IdentityLedger, LedgerEntry, LedgerKind } from '@almadar/core';
13
+ export interface WorkspaceLedger {
14
+ /** Exact `curName` match within `kind`, else null. No similarity. */
15
+ resolve(kind: LedgerKind, name: string): string | null;
16
+ /**
17
+ * Mint a fresh id, insert a workspace-owned row, persist. Throws when
18
+ * `resolve(kind, name)` already returns an id (duplicate curName within a
19
+ * kind is a caller bug) or when `kind` is `'palette'` (manifest-owned).
20
+ */
21
+ mint(kind: IdKind, name: string): string;
22
+ /** Rename an id's `curName` + append rename history, persist. Throws if id unknown. */
23
+ rename(id: string, to: string, at: number): void;
24
+ /** Current display name for an id, or null when unknown. */
25
+ curName(id: string): string | null;
26
+ /** The row for an id, or null when unknown. */
27
+ entry(id: string): LedgerEntry | null;
28
+ /** A deep clone of the ledger — no aliasing of internal state. */
29
+ snapshot(): IdentityLedger;
30
+ /**
31
+ * Row-level merge of a baked/emitted `.orb` ledger slice: add rows whose id
32
+ * is NOT already present; present ids keep the workspace row untouched.
33
+ * Persists once when the merge changed anything.
34
+ */
35
+ ingestOrbSlice(slice: IdentityLedger): void;
36
+ /** Load `.almadar/ledger.json`, then ingest every existing `orbitals/*.orb` slice. */
37
+ warm(): Promise<void>;
38
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/workspace",
3
- "version": "0.11.5",
3
+ "version": "0.11.7",
4
4
  "description": "Storage-agnostic workspace primitives shared by Almadar consumers. One service, six exports, hidden paths, single observer. See docs/Almadar_Workspace.md.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -25,19 +25,21 @@
25
25
  },
26
26
  "license": "BUSL-1.1",
27
27
  "dependencies": {
28
- "@almadar/core": "^10.26.0",
29
- "@almadar/llm": "^2.31.0",
28
+ "@almadar/core": "^10.34.0",
29
+ "@almadar/llm": "^2.36.0",
30
30
  "dugite": "^3.2.2",
31
31
  "typescript": "^5.4.0"
32
32
  },
33
33
  "devDependencies": {
34
- "@almadar/eslint-plugin": "^2.14.0",
34
+ "@almadar/eslint-plugin": "^2.15.0",
35
35
  "@types/node": "^22.0.0",
36
36
  "@typescript-eslint/parser": "8.56.0",
37
37
  "eslint": "^10.0.0",
38
38
  "tsup": "^8.0.0",
39
39
  "vitest": "^3.2.4",
40
- "turbo": "^2.8.17"
40
+ "turbo": "^2.8.17",
41
+ "@typescript/native": "npm:typescript@^7.0.2",
42
+ "typescript": "npm:@typescript/typescript6@^6.0.2"
41
43
  },
42
44
  "scripts": {
43
45
  "build": "tsup && tsc --declaration --emitDeclarationOnly --outDir dist --rootDir src --skipLibCheck || true",
@@ -1,14 +0,0 @@
1
- /**
2
- * End-to-end retrieval spec on the REAL `@almadar/llm` EmbeddingClient
3
- * (bge-base-en-v1.5, 768-d). Skipped automatically when
4
- * `OPEN_ROUTER_API_KEY` is not set in the environment.
5
- *
6
- * Purpose: validate before consumer integration that the workspace
7
- * index actually catches the R-10 (orbital name drift) and R-8
8
- * (extraTraits[] duplicate-ref) failure modes the design doc targets.
9
- * Mock-embedder tests prove the wiring; this spec proves the geometry.
10
- *
11
- * Also reports per-fixture cosine to confirm
12
- * `DEFAULT_COERCION_THRESHOLD = 0.85` is in the right ballpark.
13
- */
14
- export {};