@jungjaehoon/mama-core 1.5.0 → 1.7.0
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/README.md +48 -10
- package/db/migrations/032-add-memory-provenance-columns.sql +21 -0
- package/db/migrations/033-create-model-runs-and-tool-traces.sql +53 -0
- package/db/migrations/034-add-connector-event-scope-columns.sql +14 -0
- package/db/migrations/035-create-twin-edges.sql +63 -0
- package/db/migrations/036-create-agent-situation-packets.sql +61 -0
- package/db/migrations/037-create-context-packets.sql +32 -0
- package/dist/agent-graph/alias-write.d.ts +3 -0
- package/dist/agent-graph/alias-write.js +231 -0
- package/dist/agent-graph/entity-resolve.d.ts +3 -0
- package/dist/agent-graph/entity-resolve.js +275 -0
- package/dist/agent-graph/errors.d.ts +4 -0
- package/dist/agent-graph/errors.js +11 -0
- package/dist/agent-graph/graph-query.d.ts +5 -0
- package/dist/agent-graph/graph-query.js +359 -0
- package/dist/agent-graph/index.d.ts +6 -0
- package/dist/agent-graph/index.js +22 -0
- package/dist/agent-graph/types.d.ts +162 -0
- package/dist/agent-graph/types.js +3 -0
- package/dist/agent-situation/builder.d.ts +8 -0
- package/dist/agent-situation/builder.js +239 -0
- package/dist/agent-situation/cache-key.d.ts +4 -0
- package/dist/agent-situation/cache-key.js +64 -0
- package/dist/agent-situation/index.d.ts +7 -0
- package/dist/agent-situation/index.js +28 -0
- package/dist/agent-situation/packet-store.d.ts +27 -0
- package/dist/agent-situation/packet-store.js +228 -0
- package/dist/agent-situation/ranking-policy.d.ts +18 -0
- package/dist/agent-situation/ranking-policy.js +75 -0
- package/dist/agent-situation/source-readers.d.ts +52 -0
- package/dist/agent-situation/source-readers.js +300 -0
- package/dist/agent-situation/types.d.ts +195 -0
- package/dist/agent-situation/types.js +3 -0
- package/dist/cases/search-rollup.d.ts +4 -0
- package/dist/cases/search-rollup.js +84 -0
- package/dist/cases/wiki-page-index.js +6 -2
- package/dist/connectors/event-index.d.ts +1 -0
- package/dist/connectors/event-index.js +19 -4
- package/dist/connectors/raw-query.d.ts +18 -0
- package/dist/connectors/raw-query.js +305 -0
- package/dist/connectors/types.d.ts +40 -0
- package/dist/context-compile/boundary-defaults.d.ts +12 -0
- package/dist/context-compile/boundary-defaults.js +105 -0
- package/dist/context-compile/compiler-policy.d.ts +39 -0
- package/dist/context-compile/compiler-policy.js +147 -0
- package/dist/context-compile/compiler.d.ts +18 -0
- package/dist/context-compile/compiler.js +401 -0
- package/dist/context-compile/index.d.ts +9 -0
- package/dist/context-compile/index.js +25 -0
- package/dist/context-compile/packet-store.d.ts +9 -0
- package/dist/context-compile/packet-store.js +255 -0
- package/dist/context-compile/ref.d.ts +7 -0
- package/dist/context-compile/ref.js +102 -0
- package/dist/context-compile/source-readers.d.ts +69 -0
- package/dist/context-compile/source-readers.js +707 -0
- package/dist/context-compile/types.d.ts +138 -0
- package/dist/context-compile/types.js +11 -0
- package/dist/context-compile/visibility.d.ts +21 -0
- package/dist/context-compile/visibility.js +200 -0
- package/dist/db-adapter/node-sqlite-adapter.d.ts +9 -0
- package/dist/db-adapter/node-sqlite-adapter.js +207 -0
- package/dist/db-manager.d.ts +8 -1
- package/dist/db-manager.js +53 -5
- package/dist/edges/ref-validation.d.ts +8 -0
- package/dist/edges/ref-validation.js +301 -0
- package/dist/edges/store.d.ts +10 -0
- package/dist/edges/store.js +343 -0
- package/dist/edges/types.d.ts +88 -0
- package/dist/edges/types.js +18 -0
- package/dist/embeddings.d.ts +3 -0
- package/dist/embeddings.js +49 -9
- package/dist/entities/store.d.ts +4 -1
- package/dist/entities/store.js +206 -78
- package/dist/entities/types.d.ts +5 -1
- package/dist/entities/types.js +1 -1
- package/dist/index.d.ts +18 -3
- package/dist/index.js +54 -4
- package/dist/mama-api.d.ts +32 -35
- package/dist/mama-api.js +173 -22
- package/dist/memory/api.d.ts +17 -56
- package/dist/memory/api.js +494 -52
- package/dist/memory/event-store.d.ts +3 -0
- package/dist/memory/event-store.js +21 -2
- package/dist/memory/provenance-audit.d.ts +22 -0
- package/dist/memory/provenance-audit.js +112 -0
- package/dist/memory/provenance-query.d.ts +11 -0
- package/dist/memory/provenance-query.js +193 -0
- package/dist/memory/provenance.d.ts +29 -0
- package/dist/memory/provenance.js +137 -0
- package/dist/memory/scope-backfill.d.ts +17 -0
- package/dist/memory/scope-backfill.js +77 -0
- package/dist/memory/types.d.ts +87 -14
- package/dist/model-runs/store.d.ts +13 -0
- package/dist/model-runs/store.js +457 -0
- package/dist/model-runs/tool-trace-store.d.ts +4 -0
- package/dist/model-runs/tool-trace-store.js +114 -0
- package/dist/model-runs/types.d.ts +69 -0
- package/dist/model-runs/types.js +5 -0
- package/dist/runtime/trusted-provenance.d.ts +3 -0
- package/dist/runtime/trusted-provenance.js +6 -0
- package/dist/search/search-quality.d.ts +36 -0
- package/dist/search/search-quality.js +29 -0
- package/package.json +11 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @jungjaehoon/mama-core
|
|
2
2
|
|
|
3
|
-
Shared
|
|
3
|
+
Shared memory, provenance, graph, and context substrate for MAMA.
|
|
4
4
|
|
|
5
5
|
## What is MAMA Core?
|
|
6
6
|
|
|
@@ -10,7 +10,9 @@ MAMA Core is a shared package containing the fundamental modules used by all MAM
|
|
|
10
10
|
- **claude-code-plugin**: Claude Code plugin
|
|
11
11
|
- **standalone**: Standalone HTTP server
|
|
12
12
|
|
|
13
|
-
This package provides embedding generation, database management,
|
|
13
|
+
This package provides embedding generation, database management, scoped memory, provenance, raw
|
|
14
|
+
evidence refs, graph/entity helpers, and worker context primitives without the transport layer
|
|
15
|
+
(MCP/HTTP).
|
|
14
16
|
|
|
15
17
|
## Installation
|
|
16
18
|
|
|
@@ -78,7 +80,7 @@ const mamaApi = require('@jungjaehoon/mama-core/mama-api');
|
|
|
78
80
|
|
|
79
81
|
- **memory/api** - Scoped memory operations
|
|
80
82
|
- `saveMemory(input)` - Save typed memory with scopes and optional event_date (preference, fact, decision, lesson, constraint)
|
|
81
|
-
- `recallMemory(query, options)` - Truth-aware recall with scope filtering
|
|
83
|
+
- `recallMemory(query, options)` - Truth-aware recall with scope filtering, strictness modes, and retrieval diagnostics
|
|
82
84
|
- `buildProfile(scopes)` - Build memory profile (static/dynamic/evidence)
|
|
83
85
|
- `ingestMemory(input)` - Ingest raw content as memory
|
|
84
86
|
- `ingestConversation(input)` - Decompose conversations into typed memory units via optional LLM extraction
|
|
@@ -102,14 +104,39 @@ const mamaApi = require('@jungjaehoon/mama-core/mama-api');
|
|
|
102
104
|
- **memory/channel-summary-state-store** - Channel state management
|
|
103
105
|
- `recordChannelAudit(input)` - Accumulate audit outcomes into channel state
|
|
104
106
|
|
|
107
|
+
- **memory/provenance** - Trusted memory provenance
|
|
108
|
+
- Preserves compact runtime origin metadata in `provenance_json`
|
|
109
|
+
- Keeps underlying raw/memory/case/entity refs in `source_refs_json`
|
|
110
|
+
- Ignores caller-supplied untrusted provenance on public paths
|
|
111
|
+
|
|
112
|
+
- **model-runs** - Agent execution lineage
|
|
113
|
+
- `model-runs/store` records model run lifecycle and replay metadata
|
|
114
|
+
- `model-runs/tool-trace-store` records tool calls for later audit and reconstruction
|
|
115
|
+
|
|
116
|
+
- **connectors/raw-query** - Unified raw evidence reads
|
|
117
|
+
- Query raw connector rows through scope-aware filters
|
|
118
|
+
- Preserve raw source ids for downstream provenance
|
|
119
|
+
|
|
120
|
+
- **agent-situation** - Worker situation packets
|
|
121
|
+
- Build, rank, cache, insert, and read append-only situation packets for worker context
|
|
122
|
+
|
|
123
|
+
- **agent-graph** - Worker graph/entity helpers
|
|
124
|
+
- Query graph neighborhoods, resolve entities, and write aliases with visibility checks
|
|
125
|
+
|
|
105
126
|
### Core API (Legacy)
|
|
106
127
|
|
|
107
128
|
- **mama-api** - High-level API interface (wraps memory API)
|
|
108
129
|
- `save(decision)` - Save decision
|
|
109
130
|
- `recall(topic)` - Retrieve decision history
|
|
110
|
-
- `suggest(query)` - Semantic search with hybrid FTS5 + vector + recency
|
|
131
|
+
- `suggest(query, options)` - Semantic search with hybrid FTS5 + vector + recency, strictness controls, and diagnostics
|
|
111
132
|
- `updateOutcome(id, outcome)` - Update decision outcome
|
|
112
133
|
|
|
134
|
+
- **search/search-quality** - Search option normalization
|
|
135
|
+
- `normalizeSearchQualityOptions(options)` - Normalize recall/balanced/strict thresholds and confirmation requirements
|
|
136
|
+
|
|
137
|
+
- **edges** - Twin edge ledger
|
|
138
|
+
- Validate refs and store durable edges between memory, raw, entity, case, and packet refs
|
|
139
|
+
|
|
113
140
|
- **decision-tracker** - Decision graph management
|
|
114
141
|
- `learnDecision(decision)` - Learn from decision
|
|
115
142
|
- `createEdgesFromReasoning(reasoning)` - Parse decision links
|
|
@@ -135,7 +162,7 @@ const mamaApi = require('@jungjaehoon/mama-core/mama-api');
|
|
|
135
162
|
## Dependencies
|
|
136
163
|
|
|
137
164
|
- **@huggingface/transformers** - Local embedding generation
|
|
138
|
-
- **
|
|
165
|
+
- **better-sqlite3** - SQLite runtime with FTS5 support
|
|
139
166
|
- **Pure-TS cosine similarity** - Vector search (no native extensions)
|
|
140
167
|
|
|
141
168
|
## Development
|
|
@@ -153,7 +180,7 @@ pnpm test:watch
|
|
|
153
180
|
|
|
154
181
|
## Test Coverage
|
|
155
182
|
|
|
156
|
-
-
|
|
183
|
+
- 120+ unit/integration test files in `packages/mama-core/tests`
|
|
157
184
|
- 100% passing
|
|
158
185
|
- Tests cover:
|
|
159
186
|
- Config loader, database initialization, module exports
|
|
@@ -161,6 +188,9 @@ pnpm test:watch
|
|
|
161
188
|
- Truth store, evolution engine, scope schema
|
|
162
189
|
- Channel summary, channel summary state
|
|
163
190
|
- Event store, finding store, bootstrap builder
|
|
191
|
+
- Memory provenance, model runs, tool traces
|
|
192
|
+
- Twin edges, raw query, agent situation, agent graph
|
|
193
|
+
- Strict search diagnostics and rollup provenance
|
|
164
194
|
- Legacy shim compatibility
|
|
165
195
|
|
|
166
196
|
## Architecture
|
|
@@ -175,7 +205,7 @@ packages/mama-core/
|
|
|
175
205
|
│ ├── db-manager.ts # Database management
|
|
176
206
|
│ ├── mama-api.ts # High-level API (wraps memory API)
|
|
177
207
|
│ ├── db-adapter/ # Database adapter (SQLite)
|
|
178
|
-
│
|
|
208
|
+
│ ├── memory/ # Memory infrastructure
|
|
179
209
|
│ ├── types.ts # MemoryRecord, MemoryScopeRef, RecallBundle, etc.
|
|
180
210
|
│ ├── api.ts # saveMemory, recallMemory, buildProfile, etc.
|
|
181
211
|
│ ├── truth-store.ts # Truth projection layer
|
|
@@ -188,13 +218,19 @@ packages/mama-core/
|
|
|
188
218
|
│ ├── channel-summary-state-store.ts # Channel state reducer
|
|
189
219
|
│ ├── bootstrap-builder.ts # Memory agent bootstrap
|
|
190
220
|
│ └── profile-builder.ts # Profile classification
|
|
191
|
-
├──
|
|
192
|
-
|
|
221
|
+
│ ├── model-runs/ # Model run + tool trace lineage
|
|
222
|
+
│ ├── connectors/ # Raw connector query/index helpers
|
|
223
|
+
│ ├── edges/ # Twin edge ledger
|
|
224
|
+
│ ├── agent-situation/ # Worker situation packets
|
|
225
|
+
│ ├── agent-graph/ # Worker graph/entity helpers
|
|
226
|
+
│ └── search/ # Search quality option normalization
|
|
227
|
+
├── db/migrations/ # SQLite migrations (001-036)
|
|
228
|
+
└── tests/ # Unit and integration tests
|
|
193
229
|
```
|
|
194
230
|
|
|
195
231
|
## Migration Files
|
|
196
232
|
|
|
197
|
-
Database migrations are included in `db/migrations/` (001-
|
|
233
|
+
Database migrations are included in `db/migrations/` (001-036):
|
|
198
234
|
|
|
199
235
|
- 001-013: Core schema (decisions, embeddings, graph edges)
|
|
200
236
|
- 014: Add is_static column
|
|
@@ -204,6 +240,8 @@ Database migrations are included in `db/migrations/` (001-023):
|
|
|
204
240
|
- 019-020: Memory events and audit findings
|
|
205
241
|
- 021: Memory truth projection
|
|
206
242
|
- 022-023: Channel summaries and state
|
|
243
|
+
- 024-036: event dates, connector memory kinds, canonical entities, case-first memory, wiki/index
|
|
244
|
+
provenance, model/tool traces, twin edges, and agent situation packets
|
|
207
245
|
|
|
208
246
|
## License
|
|
209
247
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
ALTER TABLE decisions ADD COLUMN agent_id TEXT;
|
|
2
|
+
ALTER TABLE decisions ADD COLUMN model_run_id TEXT;
|
|
3
|
+
ALTER TABLE decisions ADD COLUMN envelope_hash TEXT;
|
|
4
|
+
ALTER TABLE decisions ADD COLUMN gateway_call_id TEXT;
|
|
5
|
+
ALTER TABLE decisions ADD COLUMN source_refs_json TEXT;
|
|
6
|
+
ALTER TABLE decisions ADD COLUMN provenance_json TEXT;
|
|
7
|
+
|
|
8
|
+
CREATE INDEX IF NOT EXISTS idx_decisions_envelope_hash
|
|
9
|
+
ON decisions(envelope_hash);
|
|
10
|
+
|
|
11
|
+
CREATE INDEX IF NOT EXISTS idx_decisions_model_run_id
|
|
12
|
+
ON decisions(model_run_id);
|
|
13
|
+
|
|
14
|
+
CREATE INDEX IF NOT EXISTS idx_decisions_gateway_call_id
|
|
15
|
+
ON decisions(gateway_call_id);
|
|
16
|
+
|
|
17
|
+
CREATE INDEX IF NOT EXISTS idx_memory_events_memory_created
|
|
18
|
+
ON memory_events(memory_id, created_at DESC);
|
|
19
|
+
|
|
20
|
+
INSERT OR IGNORE INTO schema_version (version, description)
|
|
21
|
+
VALUES (32, 'Add nullable memory provenance columns');
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS model_runs (
|
|
2
|
+
model_run_id TEXT PRIMARY KEY,
|
|
3
|
+
model_id TEXT,
|
|
4
|
+
model_provider TEXT,
|
|
5
|
+
prompt_version TEXT,
|
|
6
|
+
tool_manifest_version TEXT,
|
|
7
|
+
output_schema_version TEXT,
|
|
8
|
+
agent_id TEXT,
|
|
9
|
+
instance_id TEXT,
|
|
10
|
+
envelope_hash TEXT,
|
|
11
|
+
parent_model_run_id TEXT,
|
|
12
|
+
input_snapshot_ref TEXT,
|
|
13
|
+
input_refs_json TEXT,
|
|
14
|
+
completion_summary TEXT,
|
|
15
|
+
status TEXT NOT NULL CHECK (status IN ('running', 'committed', 'failed', 'legacy')),
|
|
16
|
+
error_summary TEXT,
|
|
17
|
+
token_count INTEGER DEFAULT 0,
|
|
18
|
+
cost_estimate REAL,
|
|
19
|
+
created_at INTEGER NOT NULL,
|
|
20
|
+
completed_at INTEGER
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
CREATE INDEX IF NOT EXISTS idx_model_runs_envelope_hash
|
|
24
|
+
ON model_runs(envelope_hash);
|
|
25
|
+
|
|
26
|
+
CREATE INDEX IF NOT EXISTS idx_model_runs_status_created
|
|
27
|
+
ON model_runs(status, created_at DESC);
|
|
28
|
+
|
|
29
|
+
CREATE INDEX IF NOT EXISTS idx_model_runs_agent_created
|
|
30
|
+
ON model_runs(agent_id, created_at DESC);
|
|
31
|
+
|
|
32
|
+
CREATE TABLE IF NOT EXISTS tool_traces (
|
|
33
|
+
trace_id TEXT PRIMARY KEY,
|
|
34
|
+
model_run_id TEXT NOT NULL,
|
|
35
|
+
gateway_call_id TEXT,
|
|
36
|
+
tool_name TEXT NOT NULL,
|
|
37
|
+
input_summary TEXT,
|
|
38
|
+
output_summary TEXT,
|
|
39
|
+
execution_status TEXT,
|
|
40
|
+
duration_ms INTEGER DEFAULT 0,
|
|
41
|
+
envelope_hash TEXT,
|
|
42
|
+
created_at INTEGER NOT NULL,
|
|
43
|
+
FOREIGN KEY (model_run_id) REFERENCES model_runs(model_run_id)
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
CREATE INDEX IF NOT EXISTS idx_tool_traces_model_run_id
|
|
47
|
+
ON tool_traces(model_run_id, created_at DESC);
|
|
48
|
+
|
|
49
|
+
CREATE INDEX IF NOT EXISTS idx_tool_traces_gateway_call_id
|
|
50
|
+
ON tool_traces(gateway_call_id);
|
|
51
|
+
|
|
52
|
+
INSERT OR IGNORE INTO schema_version (version, description)
|
|
53
|
+
VALUES (33, 'Create model runs and tool traces');
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
ALTER TABLE connector_event_index ADD COLUMN source_cursor TEXT;
|
|
2
|
+
ALTER TABLE connector_event_index ADD COLUMN tenant_id TEXT;
|
|
3
|
+
ALTER TABLE connector_event_index ADD COLUMN project_id TEXT;
|
|
4
|
+
ALTER TABLE connector_event_index ADD COLUMN memory_scope_kind TEXT;
|
|
5
|
+
ALTER TABLE connector_event_index ADD COLUMN memory_scope_id TEXT;
|
|
6
|
+
|
|
7
|
+
CREATE INDEX IF NOT EXISTS idx_connector_event_scope
|
|
8
|
+
ON connector_event_index(tenant_id, project_id, memory_scope_kind, memory_scope_id);
|
|
9
|
+
|
|
10
|
+
CREATE INDEX IF NOT EXISTS idx_connector_event_source_cursor
|
|
11
|
+
ON connector_event_index(source_connector, source_cursor);
|
|
12
|
+
|
|
13
|
+
INSERT OR IGNORE INTO schema_version (version, description)
|
|
14
|
+
VALUES (34, 'Add connector event scope columns');
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS twin_edges (
|
|
2
|
+
edge_id TEXT PRIMARY KEY,
|
|
3
|
+
edge_type TEXT NOT NULL CHECK (
|
|
4
|
+
edge_type IN (
|
|
5
|
+
'supersedes',
|
|
6
|
+
'builds_on',
|
|
7
|
+
'debates',
|
|
8
|
+
'synthesizes',
|
|
9
|
+
'mentions',
|
|
10
|
+
'derived_from',
|
|
11
|
+
'case_member',
|
|
12
|
+
'alias_of',
|
|
13
|
+
'next_action_for',
|
|
14
|
+
'blocks'
|
|
15
|
+
)
|
|
16
|
+
),
|
|
17
|
+
subject_kind TEXT NOT NULL CHECK (
|
|
18
|
+
subject_kind IN ('memory', 'case', 'entity', 'report', 'edge')
|
|
19
|
+
),
|
|
20
|
+
subject_id TEXT NOT NULL,
|
|
21
|
+
object_kind TEXT NOT NULL CHECK (
|
|
22
|
+
object_kind IN ('memory', 'case', 'entity', 'report', 'edge', 'raw')
|
|
23
|
+
),
|
|
24
|
+
object_id TEXT NOT NULL,
|
|
25
|
+
relation_attrs_json TEXT,
|
|
26
|
+
confidence REAL NOT NULL DEFAULT 1.0 CHECK (confidence >= 0.0 AND confidence <= 1.0),
|
|
27
|
+
source TEXT NOT NULL CHECK (source IN ('agent', 'human', 'code')),
|
|
28
|
+
agent_id TEXT,
|
|
29
|
+
model_run_id TEXT,
|
|
30
|
+
envelope_hash TEXT,
|
|
31
|
+
human_actor_id TEXT,
|
|
32
|
+
human_actor_role TEXT,
|
|
33
|
+
authority_scope_json TEXT,
|
|
34
|
+
reason_classification TEXT,
|
|
35
|
+
reason_text TEXT,
|
|
36
|
+
evidence_refs_json TEXT,
|
|
37
|
+
request_idempotency_key TEXT,
|
|
38
|
+
edge_idempotency_key TEXT,
|
|
39
|
+
content_hash BLOB NOT NULL CHECK(length(content_hash)=32),
|
|
40
|
+
created_at INTEGER NOT NULL
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
CREATE INDEX IF NOT EXISTS idx_twin_edges_subject
|
|
44
|
+
ON twin_edges(subject_kind, subject_id, created_at DESC);
|
|
45
|
+
|
|
46
|
+
CREATE INDEX IF NOT EXISTS idx_twin_edges_object
|
|
47
|
+
ON twin_edges(object_kind, object_id, created_at DESC);
|
|
48
|
+
|
|
49
|
+
CREATE INDEX IF NOT EXISTS idx_twin_edges_model_run_id
|
|
50
|
+
ON twin_edges(model_run_id, created_at DESC);
|
|
51
|
+
|
|
52
|
+
CREATE INDEX IF NOT EXISTS idx_twin_edges_request_idempotency
|
|
53
|
+
ON twin_edges(model_run_id, request_idempotency_key, created_at DESC)
|
|
54
|
+
WHERE model_run_id IS NOT NULL
|
|
55
|
+
AND request_idempotency_key IS NOT NULL;
|
|
56
|
+
|
|
57
|
+
CREATE UNIQUE INDEX IF NOT EXISTS ux_twin_edges_model_run_edge_idempotency
|
|
58
|
+
ON twin_edges(model_run_id, edge_idempotency_key)
|
|
59
|
+
WHERE model_run_id IS NOT NULL
|
|
60
|
+
AND edge_idempotency_key IS NOT NULL;
|
|
61
|
+
|
|
62
|
+
INSERT OR IGNORE INTO schema_version (version, description)
|
|
63
|
+
VALUES (35, 'Create first-class twin edge ledger');
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS agent_situation_packets (
|
|
2
|
+
packet_id TEXT PRIMARY KEY,
|
|
3
|
+
cache_key TEXT NOT NULL,
|
|
4
|
+
scope_json TEXT NOT NULL,
|
|
5
|
+
scope_hash TEXT NOT NULL,
|
|
6
|
+
range_start_ms INTEGER NOT NULL CHECK (range_start_ms >= 0),
|
|
7
|
+
range_end_ms INTEGER NOT NULL CHECK (range_end_ms >= range_start_ms),
|
|
8
|
+
focus_json TEXT NOT NULL,
|
|
9
|
+
envelope_hash TEXT NOT NULL,
|
|
10
|
+
envelope_effective_filters_json TEXT NOT NULL,
|
|
11
|
+
envelope_effective_filters_hash TEXT NOT NULL,
|
|
12
|
+
ranking_policy_version TEXT NOT NULL,
|
|
13
|
+
generated_at INTEGER NOT NULL CHECK (generated_at >= 0),
|
|
14
|
+
expires_at INTEGER NOT NULL CHECK (expires_at >= generated_at),
|
|
15
|
+
ttl_seconds INTEGER NOT NULL CHECK (ttl_seconds > 0),
|
|
16
|
+
freshness_json TEXT NOT NULL,
|
|
17
|
+
source_coverage_json TEXT NOT NULL,
|
|
18
|
+
briefing_json TEXT NOT NULL,
|
|
19
|
+
ranked_items_json TEXT NOT NULL,
|
|
20
|
+
top_memory_refs_json TEXT NOT NULL,
|
|
21
|
+
pending_human_questions_json TEXT NOT NULL,
|
|
22
|
+
entity_clusters_json TEXT NOT NULL,
|
|
23
|
+
recommended_next_tools_json TEXT NOT NULL,
|
|
24
|
+
generated_from_slice_ids_json TEXT NOT NULL DEFAULT '[]',
|
|
25
|
+
caveats_json TEXT NOT NULL,
|
|
26
|
+
agent_id TEXT NOT NULL,
|
|
27
|
+
model_run_id TEXT NOT NULL,
|
|
28
|
+
input_snapshot_ref TEXT NOT NULL,
|
|
29
|
+
source_refs_json TEXT NOT NULL DEFAULT '[]',
|
|
30
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
31
|
+
project_id TEXT NOT NULL,
|
|
32
|
+
memory_scope_kind TEXT NOT NULL,
|
|
33
|
+
memory_scope_id TEXT NOT NULL,
|
|
34
|
+
created_at INTEGER NOT NULL
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
CREATE INDEX IF NOT EXISTS idx_agent_situation_cache_fresh
|
|
38
|
+
ON agent_situation_packets(cache_key, ranking_policy_version, expires_at DESC);
|
|
39
|
+
|
|
40
|
+
CREATE INDEX IF NOT EXISTS idx_agent_situation_envelope
|
|
41
|
+
ON agent_situation_packets(envelope_hash, generated_at DESC);
|
|
42
|
+
|
|
43
|
+
CREATE INDEX IF NOT EXISTS idx_agent_situation_model_run
|
|
44
|
+
ON agent_situation_packets(model_run_id, generated_at DESC);
|
|
45
|
+
|
|
46
|
+
CREATE INDEX IF NOT EXISTS idx_agent_situation_scope
|
|
47
|
+
ON agent_situation_packets(tenant_id, project_id, memory_scope_kind, memory_scope_id, generated_at DESC);
|
|
48
|
+
|
|
49
|
+
CREATE TABLE IF NOT EXISTS agent_situation_refresh_leases (
|
|
50
|
+
cache_key TEXT PRIMARY KEY,
|
|
51
|
+
ranking_policy_version TEXT NOT NULL,
|
|
52
|
+
lease_owner TEXT NOT NULL,
|
|
53
|
+
lease_expires_at INTEGER NOT NULL,
|
|
54
|
+
created_at INTEGER NOT NULL
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
CREATE INDEX IF NOT EXISTS idx_agent_situation_leases_expiry
|
|
58
|
+
ON agent_situation_refresh_leases(lease_expires_at);
|
|
59
|
+
|
|
60
|
+
INSERT OR IGNORE INTO schema_version (version, description)
|
|
61
|
+
VALUES (36, 'Create agent situation packet cache');
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS context_packets (
|
|
2
|
+
packet_id TEXT PRIMARY KEY,
|
|
3
|
+
task TEXT NOT NULL,
|
|
4
|
+
packet_json TEXT NOT NULL,
|
|
5
|
+
scope_json TEXT NOT NULL,
|
|
6
|
+
scope_hash TEXT NOT NULL,
|
|
7
|
+
envelope_hash TEXT NOT NULL,
|
|
8
|
+
model_run_id TEXT NOT NULL,
|
|
9
|
+
agent_id TEXT NOT NULL,
|
|
10
|
+
input_snapshot_ref TEXT NOT NULL,
|
|
11
|
+
source_refs_json TEXT NOT NULL DEFAULT '[]',
|
|
12
|
+
tenant_id TEXT NOT NULL DEFAULT 'default',
|
|
13
|
+
project_id TEXT NOT NULL,
|
|
14
|
+
memory_scope_kind TEXT NOT NULL CHECK (memory_scope_kind IN ('global', 'user', 'channel', 'project')),
|
|
15
|
+
memory_scope_id TEXT NOT NULL,
|
|
16
|
+
created_at INTEGER NOT NULL CHECK (created_at >= 0)
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
CREATE INDEX IF NOT EXISTS idx_context_packets_model_run
|
|
20
|
+
ON context_packets(model_run_id, created_at DESC);
|
|
21
|
+
|
|
22
|
+
CREATE INDEX IF NOT EXISTS idx_context_packets_envelope
|
|
23
|
+
ON context_packets(envelope_hash, created_at DESC);
|
|
24
|
+
|
|
25
|
+
CREATE INDEX IF NOT EXISTS idx_context_packets_scope
|
|
26
|
+
ON context_packets(tenant_id, project_id, memory_scope_kind, memory_scope_id, created_at DESC);
|
|
27
|
+
|
|
28
|
+
CREATE INDEX IF NOT EXISTS idx_context_packets_scope_hash
|
|
29
|
+
ON context_packets(scope_hash, created_at DESC);
|
|
30
|
+
|
|
31
|
+
INSERT OR IGNORE INTO schema_version (version, description)
|
|
32
|
+
VALUES (37, 'Create context packet append-only store');
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { AgentGraphAdapter, AttachEntityAliasWithEdgeInput, AttachEntityAliasWithEdgeResult } from './types.js';
|
|
2
|
+
export declare function attachEntityAliasWithEdge(adapter: AgentGraphAdapter, input: AttachEntityAliasWithEdgeInput): AttachEntityAliasWithEdgeResult;
|
|
3
|
+
//# sourceMappingURL=alias-write.d.ts.map
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.attachEntityAliasWithEdge = attachEntityAliasWithEdge;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
const canonicalize_js_1 = require("../canonicalize.js");
|
|
6
|
+
const ref_validation_js_1 = require("../edges/ref-validation.js");
|
|
7
|
+
const store_js_1 = require("../edges/store.js");
|
|
8
|
+
const types_js_1 = require("../edges/types.js");
|
|
9
|
+
const normalization_js_1 = require("../entities/normalization.js");
|
|
10
|
+
const store_js_2 = require("../entities/store.js");
|
|
11
|
+
const errors_js_1 = require("./errors.js");
|
|
12
|
+
const TWIN_REF_KIND_SET = new Set(types_js_1.TWIN_REF_KINDS);
|
|
13
|
+
function stableAliasId(input, normalizedLabel) {
|
|
14
|
+
const replayKey = replayIdempotencyKey(input);
|
|
15
|
+
if (replayKey) {
|
|
16
|
+
const hash = (0, node_crypto_1.createHash)('sha256')
|
|
17
|
+
.update(`${input.entity_id}\0${normalizedLabel}\0${replayKey}`, 'utf8')
|
|
18
|
+
.digest('hex')
|
|
19
|
+
.slice(0, 32);
|
|
20
|
+
return `alias_${hash}`;
|
|
21
|
+
}
|
|
22
|
+
return `alias_${(0, node_crypto_1.randomUUID)().replace(/-/g, '')}`;
|
|
23
|
+
}
|
|
24
|
+
function replayIdempotencyKey(input) {
|
|
25
|
+
return input.edge_idempotency_key ?? input.request_idempotency_key;
|
|
26
|
+
}
|
|
27
|
+
function loadAlias(adapter, aliasId) {
|
|
28
|
+
const row = adapter.prepare('SELECT * FROM entity_aliases WHERE id = ?').get(aliasId);
|
|
29
|
+
if (!row) {
|
|
30
|
+
throw new Error(`Entity alias was not written: ${aliasId}`);
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
id: String(row.id),
|
|
34
|
+
entity_id: String(row.entity_id),
|
|
35
|
+
label: String(row.label),
|
|
36
|
+
normalized_label: String(row.normalized_label),
|
|
37
|
+
lang: typeof row.lang === 'string' ? row.lang : null,
|
|
38
|
+
script: typeof row.script === 'string' ? row.script : null,
|
|
39
|
+
label_type: row.label_type,
|
|
40
|
+
source_type: String(row.source_type),
|
|
41
|
+
source_ref: typeof row.source_ref === 'string' ? row.source_ref : null,
|
|
42
|
+
confidence: typeof row.confidence === 'number' ? row.confidence : null,
|
|
43
|
+
status: row.status,
|
|
44
|
+
created_at: Number(row.created_at),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function normalizeConfidence(value) {
|
|
48
|
+
if (value === undefined || value === null) {
|
|
49
|
+
return 1;
|
|
50
|
+
}
|
|
51
|
+
if (typeof value === 'number' && Number.isFinite(value) && value >= 0 && value <= 1) {
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
throw new errors_js_1.AgentGraphValidationError(`confidence must be between 0 and 1: ${String(value)}`);
|
|
55
|
+
}
|
|
56
|
+
function selectExistingAliasEdge(adapter, modelRunId, edgeIdempotencyKey) {
|
|
57
|
+
if (!edgeIdempotencyKey) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const row = adapter
|
|
61
|
+
.prepare(`
|
|
62
|
+
SELECT *
|
|
63
|
+
FROM twin_edges
|
|
64
|
+
WHERE model_run_id = ?
|
|
65
|
+
AND edge_idempotency_key = ?
|
|
66
|
+
LIMIT 1
|
|
67
|
+
`)
|
|
68
|
+
.get(modelRunId, edgeIdempotencyKey);
|
|
69
|
+
return row ? (0, store_js_1.mapTwinEdgeRow)(row) : null;
|
|
70
|
+
}
|
|
71
|
+
function normalizeSourceRefs(sourceRefs) {
|
|
72
|
+
if (!sourceRefs || sourceRefs.length === 0) {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
const refs = [];
|
|
76
|
+
const seen = new Set();
|
|
77
|
+
for (const ref of sourceRefs) {
|
|
78
|
+
if (!ref ||
|
|
79
|
+
typeof ref !== 'object' ||
|
|
80
|
+
typeof ref.kind !== 'string' ||
|
|
81
|
+
typeof ref.id !== 'string' ||
|
|
82
|
+
ref.id.trim().length === 0 ||
|
|
83
|
+
!TWIN_REF_KIND_SET.has(ref.kind)) {
|
|
84
|
+
throw new errors_js_1.AgentGraphValidationError('source_refs must be TwinRef objects with kind and id.');
|
|
85
|
+
}
|
|
86
|
+
const normalized = { kind: ref.kind, id: ref.id.trim() };
|
|
87
|
+
const key = `${normalized.kind}:${normalized.id}`;
|
|
88
|
+
if (!seen.has(key)) {
|
|
89
|
+
seen.add(key);
|
|
90
|
+
refs.push(normalized);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return refs.sort((left, right) => `${left.kind}:${left.id}`.localeCompare(`${right.kind}:${right.id}`));
|
|
94
|
+
}
|
|
95
|
+
function expectedRelationAttrs(alias, sourceRefs) {
|
|
96
|
+
const attrs = {
|
|
97
|
+
alias_id: alias.id,
|
|
98
|
+
label: alias.label,
|
|
99
|
+
normalized_label: alias.normalized_label,
|
|
100
|
+
lang: alias.lang,
|
|
101
|
+
script: alias.script,
|
|
102
|
+
label_type: alias.label_type,
|
|
103
|
+
};
|
|
104
|
+
if (sourceRefs.length > 0) {
|
|
105
|
+
attrs.source_refs = sourceRefs.map((ref) => ({ kind: ref.kind, id: ref.id }));
|
|
106
|
+
}
|
|
107
|
+
return attrs;
|
|
108
|
+
}
|
|
109
|
+
function assertAliasReplayMatches(alias, input, normalized, confidence) {
|
|
110
|
+
const expectedScript = input.script ?? normalized.script;
|
|
111
|
+
const expectedLabelType = input.label_type ?? 'alt';
|
|
112
|
+
const expectedSourceRef = input.source_ref ?? null;
|
|
113
|
+
const matches = alias.entity_id === input.entity_id &&
|
|
114
|
+
alias.label === input.label &&
|
|
115
|
+
alias.normalized_label === normalized.normalized &&
|
|
116
|
+
alias.lang === (input.lang ?? null) &&
|
|
117
|
+
alias.script === expectedScript &&
|
|
118
|
+
alias.label_type === expectedLabelType &&
|
|
119
|
+
alias.source_type === input.source_type &&
|
|
120
|
+
alias.source_ref === expectedSourceRef &&
|
|
121
|
+
alias.confidence === confidence &&
|
|
122
|
+
alias.status === 'active';
|
|
123
|
+
if (!matches) {
|
|
124
|
+
throw new errors_js_1.AgentGraphValidationError(`Conflicting entity alias replay for request idempotency key: ${replayIdempotencyKey(input)}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function assertAliasEdgeReplayMatches(edge, input, alias, confidence, edgeIdempotencyKey, sourceRefs) {
|
|
128
|
+
const matches = edge.edge_type === 'alias_of' &&
|
|
129
|
+
edge.subject_ref.kind === 'entity' &&
|
|
130
|
+
edge.subject_ref.id === input.entity_id &&
|
|
131
|
+
edge.object_ref.kind === 'entity' &&
|
|
132
|
+
edge.object_ref.id === input.entity_id &&
|
|
133
|
+
edge.relation_attrs_json === (0, canonicalize_js_1.canonicalizeJSON)(expectedRelationAttrs(alias, sourceRefs)) &&
|
|
134
|
+
edge.confidence === confidence &&
|
|
135
|
+
edge.source === 'agent' &&
|
|
136
|
+
edge.agent_id === input.agent_id &&
|
|
137
|
+
edge.model_run_id === input.model_run_id &&
|
|
138
|
+
edge.envelope_hash === input.envelope_hash &&
|
|
139
|
+
edge.request_idempotency_key === (input.request_idempotency_key ?? null) &&
|
|
140
|
+
edge.edge_idempotency_key === (edgeIdempotencyKey ?? null);
|
|
141
|
+
if (!matches) {
|
|
142
|
+
throw new errors_js_1.AgentGraphValidationError(`Conflicting alias edge replay for request idempotency key: ${replayIdempotencyKey(input)}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function insertAliasEdge(adapter, input, alias, confidence, sourceRefs) {
|
|
146
|
+
const edgeIdempotencyKey = replayIdempotencyKey(input);
|
|
147
|
+
const existing = selectExistingAliasEdge(adapter, input.model_run_id, edgeIdempotencyKey);
|
|
148
|
+
const edgeId = `edge_${(0, node_crypto_1.randomUUID)().replace(/-/g, '')}`;
|
|
149
|
+
const relationAttrs = expectedRelationAttrs(alias, sourceRefs);
|
|
150
|
+
const relationAttrsJson = (0, canonicalize_js_1.canonicalizeJSON)(relationAttrs);
|
|
151
|
+
if (existing) {
|
|
152
|
+
assertAliasEdgeReplayMatches(existing, input, alias, confidence, edgeIdempotencyKey, sourceRefs);
|
|
153
|
+
return existing;
|
|
154
|
+
}
|
|
155
|
+
const contentHash = (0, node_crypto_1.createHash)('sha256')
|
|
156
|
+
.update((0, canonicalize_js_1.canonicalizeJSON)({
|
|
157
|
+
edge_type: 'alias_of',
|
|
158
|
+
subject_ref: { kind: 'entity', id: input.entity_id },
|
|
159
|
+
object_ref: { kind: 'entity', id: input.entity_id },
|
|
160
|
+
relation_attrs_json: relationAttrsJson,
|
|
161
|
+
confidence,
|
|
162
|
+
source: 'agent',
|
|
163
|
+
agent_id: input.agent_id,
|
|
164
|
+
model_run_id: input.model_run_id,
|
|
165
|
+
envelope_hash: input.envelope_hash,
|
|
166
|
+
}), 'utf8')
|
|
167
|
+
.digest();
|
|
168
|
+
adapter
|
|
169
|
+
.prepare(`
|
|
170
|
+
INSERT INTO twin_edges (
|
|
171
|
+
edge_id, edge_type, subject_kind, subject_id, object_kind, object_id,
|
|
172
|
+
relation_attrs_json, confidence, source, agent_id, model_run_id, envelope_hash,
|
|
173
|
+
request_idempotency_key, edge_idempotency_key, content_hash, created_at
|
|
174
|
+
)
|
|
175
|
+
VALUES (?, 'alias_of', 'entity', ?, 'entity', ?, ?, ?, 'agent', ?, ?, ?, ?, ?, ?, ?)
|
|
176
|
+
`)
|
|
177
|
+
.run(edgeId, input.entity_id, input.entity_id, relationAttrsJson, confidence, input.agent_id, input.model_run_id, input.envelope_hash, input.request_idempotency_key ?? null, edgeIdempotencyKey ?? null, contentHash, Date.now());
|
|
178
|
+
const row = adapter.prepare('SELECT * FROM twin_edges WHERE edge_id = ?').get(edgeId);
|
|
179
|
+
if (!row) {
|
|
180
|
+
throw new Error(`Entity alias edge was not written: ${edgeId}`);
|
|
181
|
+
}
|
|
182
|
+
return (0, store_js_1.mapTwinEdgeRow)(row);
|
|
183
|
+
}
|
|
184
|
+
function attachEntityAliasWithEdge(adapter, input) {
|
|
185
|
+
const entity = (0, store_js_2.getEntityNode)(input.entity_id, adapter);
|
|
186
|
+
if (!entity || entity.status !== 'active') {
|
|
187
|
+
throw new errors_js_1.AgentGraphValidationError(`Active entity not found: ${input.entity_id}`);
|
|
188
|
+
}
|
|
189
|
+
const visibility = {
|
|
190
|
+
scopes: input.scopes,
|
|
191
|
+
connectors: input.connectors,
|
|
192
|
+
projectRefs: input.project_refs,
|
|
193
|
+
tenantId: input.tenant_id,
|
|
194
|
+
};
|
|
195
|
+
const sourceRefs = normalizeSourceRefs(input.source_refs);
|
|
196
|
+
if (sourceRefs.length === 0) {
|
|
197
|
+
throw new errors_js_1.AgentGraphValidationError('source_refs must include at least one visible source.');
|
|
198
|
+
}
|
|
199
|
+
try {
|
|
200
|
+
(0, ref_validation_js_1.assertTwinRefsVisible)(adapter, [{ kind: 'entity', id: input.entity_id }, ...sourceRefs], visibility);
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
throw new errors_js_1.AgentGraphValidationError(error instanceof Error ? error.message : String(error));
|
|
204
|
+
}
|
|
205
|
+
let normalized;
|
|
206
|
+
try {
|
|
207
|
+
normalized = (0, normalization_js_1.normalizeEntityLabel)(input.label);
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
throw new errors_js_1.AgentGraphValidationError(error instanceof Error ? error.message : String(error));
|
|
211
|
+
}
|
|
212
|
+
const confidence = normalizeConfidence(input.confidence);
|
|
213
|
+
const aliasId = stableAliasId(input, normalized.normalized);
|
|
214
|
+
const createdAt = Date.now();
|
|
215
|
+
return adapter.transaction(() => {
|
|
216
|
+
adapter
|
|
217
|
+
.prepare(`
|
|
218
|
+
INSERT OR IGNORE INTO entity_aliases (
|
|
219
|
+
id, entity_id, label, normalized_label, lang, script, label_type,
|
|
220
|
+
source_type, source_ref, confidence, status, created_at
|
|
221
|
+
)
|
|
222
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', ?)
|
|
223
|
+
`)
|
|
224
|
+
.run(aliasId, input.entity_id, input.label, normalized.normalized, input.lang ?? null, input.script ?? normalized.script, input.label_type ?? 'alt', input.source_type, input.source_ref ?? null, confidence, createdAt);
|
|
225
|
+
const alias = loadAlias(adapter, aliasId);
|
|
226
|
+
assertAliasReplayMatches(alias, input, normalized, confidence);
|
|
227
|
+
const edge = insertAliasEdge(adapter, input, alias, confidence, sourceRefs);
|
|
228
|
+
return { alias, edge };
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=alias-write.js.map
|