@jungjaehoon/mama-core 4.1.1 → 5.1.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 +10 -10
- package/db/migrations/099-commitment-revision-graph.sql +3 -0
- package/dist/api/catalog.js +4 -7
- package/dist/db-adapter/node-sqlite-adapter.d.ts +5 -1
- package/dist/db-adapter/node-sqlite-adapter.js +27 -24
- package/dist/db-manager.d.ts +2 -13
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -6
- package/dist/knowledge/access.js +1 -1
- package/dist/knowledge/commitments.d.ts +2 -0
- package/dist/knowledge/commitments.js +1 -0
- package/dist/knowledge/graph-query.d.ts +10 -1
- package/dist/knowledge/graph-query.js +118 -25
- package/dist/knowledge/index.d.ts +12 -4
- package/dist/knowledge/index.js +10 -10
- package/dist/knowledge/judgments.d.ts +3 -1
- package/dist/knowledge/judgments.js +46 -26
- package/dist/knowledge/links.d.ts +50 -0
- package/dist/knowledge/links.js +152 -0
- package/dist/knowledge/search.d.ts +6 -1
- package/dist/knowledge/search.js +17 -11
- package/dist/mama-api.d.ts +22 -7
- package/dist/mama-api.js +64 -15
- package/dist/memory/api.d.ts +17 -12
- package/dist/memory/api.js +436 -414
- package/dist/memory/decision-links.d.ts +54 -0
- package/dist/memory/decision-links.js +195 -0
- package/dist/memory/graph-read.d.ts +0 -16
- package/dist/memory/graph-read.js +10 -51
- package/dist/memory/judgment-types.d.ts +1 -20
- package/dist/memory/provenance-live.js +1 -1
- package/dist/memory/types.d.ts +14 -1
- package/dist/memory/types.js +1 -7
- package/dist/memory/write-adapters.d.ts +1 -39
- package/dist/memory/write-adapters.js +0 -98
- package/package.json +1 -1
- package/dist/knowledge/commitment-revision-migration.d.ts +0 -4
- package/dist/knowledge/commitment-revision-migration.js +0 -50
- package/dist/knowledge/decision-edges.d.ts +0 -70
- package/dist/knowledge/decision-edges.js +0 -123
- package/dist/memory/evolution-engine.d.ts +0 -22
- package/dist/memory/evolution-engine.js +0 -133
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type LinkCommand, type LinkReceipt, type StoredLink } from './links.js';
|
|
1
2
|
import type { DatabaseInstance } from '../db-manager.js';
|
|
2
3
|
import { type JudgmentAccess, type JudgmentKnowledgeOptions } from './judgments.js';
|
|
3
4
|
import type { CommitmentPage, CreateWorkCommand, ReviseWorkCommand, WithdrawWorkCommand, WorkRead, WorkWriteResult } from './commitments.js';
|
|
@@ -7,7 +8,8 @@ export type { JudgmentAccess } from './judgments.js';
|
|
|
7
8
|
export { judgmentRecordId } from './judgments.js';
|
|
8
9
|
export type { IdentityCorrection, IdentityCorrectionReceipt, JudgmentAmendment, JudgmentCommand, JudgmentEventMeta, JudgmentProjections, JudgmentReceipt, JudgmentRecordFields, JsonValue, OwnerWorkPatch, RecordLink, WorkGraphPage, WorkGraphQuery, WorkReclassifyDisposition, WorkReference, } from '../memory/judgment-types.js';
|
|
9
10
|
export { WORK_RECLASSIFY_DISPOSITIONS } from '../memory/judgment-types.js';
|
|
10
|
-
export { JudgmentError, appendJudgment,
|
|
11
|
+
export { JudgmentError, appendJudgment, getTwinEdge, listTwinEdgesForRefs, mapTwinEdgeRow, } from './judgments.js';
|
|
12
|
+
export { appendLink, findLink, linkEdgeId, type LinkCommand, type LinkReceipt, type StoredLink, } from './links.js';
|
|
11
13
|
export { assertTwinRefsVisible, channelGrantClause, isChannelGranted, listVisibleTwinEdgesForRefs, visibleTwinRefKeys, visibleTwinRefKeysRecursive, TwinRefNotVisibleError, } from './access.js';
|
|
12
14
|
export type { ChannelGrant, ChannelGrantClause } from './access.js';
|
|
13
15
|
export { TWIN_EDGE_SOURCES, TWIN_EDGE_TYPES, TWIN_REF_KINDS, type InsertTwinEdgeInput, type ListVisibleTwinEdgesOptions, type TwinEdgeInsert, type TwinEdgeRecord, type TwinEdgeSource, type TwinEdgeSubjectRef, type TwinEdgeType, type TwinProjectRef, type TwinRef, type TwinRefKind, type TwinScopeRef, type TwinVisibility, } from './twin-edge-types.js';
|
|
@@ -17,10 +19,9 @@ export { observationVersionId, appendObservationVersion, getObservationVersion,
|
|
|
17
19
|
export { createWork, readWork, reviseWork, withdrawWork } from './commitments.js';
|
|
18
20
|
export { parseExactDueAt, assertIsoDate, assertWorkPatchValues, WORK_PATCH_DEADLINE_PATTERN, WORK_PATCH_DUE_AT_PATTERN, type ParsedExactDueAt, } from './work-dates.js';
|
|
19
21
|
export type { CommitmentPage, CommitmentChainEntry, CommitmentRevision, CommitmentView, CreateWorkCommand, ReviseWorkCommand, WithdrawWorkCommand, WorkRead, WorkWriteResult, } from './commitments.js';
|
|
20
|
-
export { vectorSearch, fts5Search } from './search.js';
|
|
21
|
-
export { queryDecisionGraph, querySemanticEdges, getGraphNeighborhood, getGraphPaths, getGraphTimeline, queryGraph, AgentGraphValidationError, type AgentGraphAdapter, type AgentGraphEdgeFilters, type AgentGraphResult, type AgentGraphCurrentProjection, type AgentGraphPath, type AgentGraphTimelineEvent, type AgentGraphTimelineMemoryEvent, type AgentGraphTimelineCaseEvent, type AgentGraphTimelineRawEvent, type AgentGraphTimelineEdgeEvent, type GraphNeighborhoodInput, type GraphPathsInput, type GraphPathsResult, type GraphTimelineInput, type GraphTimelineResult, } from './graph-query.js';
|
|
22
|
+
export { vectorSearch, fts5Search, RECALL_EXCLUDED_STATUSES } from './search.js';
|
|
23
|
+
export { queryDecisionGraph, querySemanticEdges, STATED_DECISION_EDGES, getGraphNeighborhood, getGraphPaths, getGraphTimeline, queryGraph, AgentGraphValidationError, type AgentGraphAdapter, type AgentGraphEdgeFilters, type AgentGraphResult, type AgentGraphCurrentProjection, type AgentGraphPath, type AgentGraphTimelineEvent, type AgentGraphTimelineMemoryEvent, type AgentGraphTimelineCaseEvent, type AgentGraphTimelineRawEvent, type AgentGraphTimelineEdgeEvent, type GraphNeighborhoodInput, type GraphPathsInput, type GraphPathsResult, type GraphTimelineInput, type GraphTimelineResult, } from './graph-query.js';
|
|
22
24
|
export type { SourceIngestCommand, SourceIngestReceipt } from './source-ingest.js';
|
|
23
|
-
export { upsertDecisionEdge, proposeDecisionEdge, approveDecisionEdge, rejectDecisionEdge, deprecateAutoDecisionEdges, deleteDecisionEdgesWithAudit, type DecisionEdgeKey, type DecisionEdgeRow, type DecisionEdgeDeleteFailure, } from './decision-edges.js';
|
|
24
25
|
export interface KnowledgeOptions extends JudgmentKnowledgeOptions {
|
|
25
26
|
adapter: DatabaseInstance;
|
|
26
27
|
}
|
|
@@ -42,6 +43,13 @@ export interface Knowledge {
|
|
|
42
43
|
* paths, timelines, and hydrated details — all under the caller's authority.
|
|
43
44
|
*/
|
|
44
45
|
queryGraph(query: WorkGraphQuery, access: JudgmentAccess): WorkGraphPage;
|
|
46
|
+
/**
|
|
47
|
+
* Append one edge between existing records, with its reason; nothing is edited. A link to an
|
|
48
|
+
* edge contradicts it.
|
|
49
|
+
*/
|
|
50
|
+
appendLink(command: LinkCommand, access: JudgmentAccess): LinkReceipt;
|
|
51
|
+
/** The link this principal already wrote under a command id, if any. */
|
|
52
|
+
findLink(commandId: string, access: JudgmentAccess): StoredLink | null;
|
|
45
53
|
}
|
|
46
54
|
export declare function createKnowledge(options: KnowledgeOptions): Knowledge;
|
|
47
55
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/knowledge/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.deleteDecisionEdgesWithAudit = void 0;
|
|
3
|
+
exports.AgentGraphValidationError = exports.queryGraph = exports.getGraphTimeline = exports.getGraphPaths = exports.getGraphNeighborhood = exports.STATED_DECISION_EDGES = exports.querySemanticEdges = exports.queryDecisionGraph = exports.RECALL_EXCLUDED_STATUSES = exports.fts5Search = exports.vectorSearch = exports.WORK_PATCH_DUE_AT_PATTERN = exports.WORK_PATCH_DEADLINE_PATTERN = exports.assertWorkPatchValues = exports.assertIsoDate = exports.parseExactDueAt = exports.withdrawWork = exports.reviseWork = exports.readWork = exports.createWork = exports.isObservationVisibilityRowVisible = exports.isObservationVersionVisible = exports.readObservationVersion = exports.searchOwnerObservationVersions = exports.getObservationVersion = exports.appendObservationVersion = exports.observationVersionId = exports.correctIdentity = exports.ingestSource = exports.TWIN_REF_KINDS = exports.TWIN_EDGE_TYPES = exports.TWIN_EDGE_SOURCES = exports.TwinRefNotVisibleError = exports.visibleTwinRefKeysRecursive = exports.visibleTwinRefKeys = exports.listVisibleTwinEdgesForRefs = exports.isChannelGranted = exports.channelGrantClause = exports.assertTwinRefsVisible = exports.linkEdgeId = exports.findLink = exports.appendLink = exports.mapTwinEdgeRow = exports.listTwinEdgesForRefs = exports.getTwinEdge = exports.appendJudgment = exports.JudgmentError = exports.WORK_RECLASSIFY_DISPOSITIONS = exports.judgmentRecordId = void 0;
|
|
5
4
|
exports.createKnowledge = createKnowledge;
|
|
5
|
+
const links_js_1 = require("./links.js");
|
|
6
6
|
const judgments_js_1 = require("./judgments.js");
|
|
7
7
|
const source_ingest_js_1 = require("./source-ingest.js");
|
|
8
8
|
const commitments_js_1 = require("./commitments.js");
|
|
@@ -15,10 +15,13 @@ Object.defineProperty(exports, "WORK_RECLASSIFY_DISPOSITIONS", { enumerable: tru
|
|
|
15
15
|
var judgments_js_3 = require("./judgments.js");
|
|
16
16
|
Object.defineProperty(exports, "JudgmentError", { enumerable: true, get: function () { return judgments_js_3.JudgmentError; } });
|
|
17
17
|
Object.defineProperty(exports, "appendJudgment", { enumerable: true, get: function () { return judgments_js_3.appendJudgment; } });
|
|
18
|
-
Object.defineProperty(exports, "insertTwinEdge", { enumerable: true, get: function () { return judgments_js_3.insertTwinEdge; } });
|
|
19
18
|
Object.defineProperty(exports, "getTwinEdge", { enumerable: true, get: function () { return judgments_js_3.getTwinEdge; } });
|
|
20
19
|
Object.defineProperty(exports, "listTwinEdgesForRefs", { enumerable: true, get: function () { return judgments_js_3.listTwinEdgesForRefs; } });
|
|
21
20
|
Object.defineProperty(exports, "mapTwinEdgeRow", { enumerable: true, get: function () { return judgments_js_3.mapTwinEdgeRow; } });
|
|
21
|
+
var links_js_2 = require("./links.js");
|
|
22
|
+
Object.defineProperty(exports, "appendLink", { enumerable: true, get: function () { return links_js_2.appendLink; } });
|
|
23
|
+
Object.defineProperty(exports, "findLink", { enumerable: true, get: function () { return links_js_2.findLink; } });
|
|
24
|
+
Object.defineProperty(exports, "linkEdgeId", { enumerable: true, get: function () { return links_js_2.linkEdgeId; } });
|
|
22
25
|
var access_js_1 = require("./access.js");
|
|
23
26
|
Object.defineProperty(exports, "assertTwinRefsVisible", { enumerable: true, get: function () { return access_js_1.assertTwinRefsVisible; } });
|
|
24
27
|
Object.defineProperty(exports, "channelGrantClause", { enumerable: true, get: function () { return access_js_1.channelGrantClause; } });
|
|
@@ -59,21 +62,16 @@ Object.defineProperty(exports, "WORK_PATCH_DUE_AT_PATTERN", { enumerable: true,
|
|
|
59
62
|
var search_js_1 = require("./search.js");
|
|
60
63
|
Object.defineProperty(exports, "vectorSearch", { enumerable: true, get: function () { return search_js_1.vectorSearch; } });
|
|
61
64
|
Object.defineProperty(exports, "fts5Search", { enumerable: true, get: function () { return search_js_1.fts5Search; } });
|
|
65
|
+
Object.defineProperty(exports, "RECALL_EXCLUDED_STATUSES", { enumerable: true, get: function () { return search_js_1.RECALL_EXCLUDED_STATUSES; } });
|
|
62
66
|
var graph_query_js_2 = require("./graph-query.js");
|
|
63
67
|
Object.defineProperty(exports, "queryDecisionGraph", { enumerable: true, get: function () { return graph_query_js_2.queryDecisionGraph; } });
|
|
64
68
|
Object.defineProperty(exports, "querySemanticEdges", { enumerable: true, get: function () { return graph_query_js_2.querySemanticEdges; } });
|
|
69
|
+
Object.defineProperty(exports, "STATED_DECISION_EDGES", { enumerable: true, get: function () { return graph_query_js_2.STATED_DECISION_EDGES; } });
|
|
65
70
|
Object.defineProperty(exports, "getGraphNeighborhood", { enumerable: true, get: function () { return graph_query_js_2.getGraphNeighborhood; } });
|
|
66
71
|
Object.defineProperty(exports, "getGraphPaths", { enumerable: true, get: function () { return graph_query_js_2.getGraphPaths; } });
|
|
67
72
|
Object.defineProperty(exports, "getGraphTimeline", { enumerable: true, get: function () { return graph_query_js_2.getGraphTimeline; } });
|
|
68
73
|
Object.defineProperty(exports, "queryGraph", { enumerable: true, get: function () { return graph_query_js_2.queryGraph; } });
|
|
69
74
|
Object.defineProperty(exports, "AgentGraphValidationError", { enumerable: true, get: function () { return graph_query_js_2.AgentGraphValidationError; } });
|
|
70
|
-
var decision_edges_js_1 = require("./decision-edges.js");
|
|
71
|
-
Object.defineProperty(exports, "upsertDecisionEdge", { enumerable: true, get: function () { return decision_edges_js_1.upsertDecisionEdge; } });
|
|
72
|
-
Object.defineProperty(exports, "proposeDecisionEdge", { enumerable: true, get: function () { return decision_edges_js_1.proposeDecisionEdge; } });
|
|
73
|
-
Object.defineProperty(exports, "approveDecisionEdge", { enumerable: true, get: function () { return decision_edges_js_1.approveDecisionEdge; } });
|
|
74
|
-
Object.defineProperty(exports, "rejectDecisionEdge", { enumerable: true, get: function () { return decision_edges_js_1.rejectDecisionEdge; } });
|
|
75
|
-
Object.defineProperty(exports, "deprecateAutoDecisionEdges", { enumerable: true, get: function () { return decision_edges_js_1.deprecateAutoDecisionEdges; } });
|
|
76
|
-
Object.defineProperty(exports, "deleteDecisionEdgesWithAudit", { enumerable: true, get: function () { return decision_edges_js_1.deleteDecisionEdgesWithAudit; } });
|
|
77
75
|
function createKnowledge(options) {
|
|
78
76
|
const writer = (0, judgments_js_1.createJudgmentWriter)(options);
|
|
79
77
|
return {
|
|
@@ -85,6 +83,8 @@ function createKnowledge(options) {
|
|
|
85
83
|
withdrawWork: (command, access) => (0, commitments_js_1.withdrawWork)(command, access, options),
|
|
86
84
|
readWork: (query, access) => (0, commitments_js_1.readWork)(options.adapter, query, access),
|
|
87
85
|
queryGraph: (query, access) => (0, graph_query_js_1.queryGraph)(options.adapter, query, access),
|
|
86
|
+
appendLink: (command, access) => (0, links_js_1.appendLink)(options.adapter, command, access),
|
|
87
|
+
findLink: (commandId, access) => (0, links_js_1.findLink)(options.adapter, commandId, access),
|
|
88
88
|
};
|
|
89
89
|
}
|
|
90
90
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DatabaseAdapter, DatabaseInstance } from '../db-manager.js';
|
|
2
2
|
import { type TwinEdgeInsert, type TwinEdgeRecord, type TwinEdgeSource, type TwinEdgeType, type TwinProjectRef, type TwinRef } from './twin-edge-types.js';
|
|
3
|
-
import type { JudgmentCommand, JudgmentReceipt } from '../memory/judgment-types.js';
|
|
3
|
+
import type { JudgmentCommand, JudgmentReceipt, WorkReference } from '../memory/judgment-types.js';
|
|
4
4
|
import type { MemoryScopeRef } from '../memory/types.js';
|
|
5
5
|
export interface JudgmentAccess {
|
|
6
6
|
principalId: string;
|
|
@@ -88,6 +88,8 @@ export declare function admittedScopeIds(access: JudgmentAccess): string[];
|
|
|
88
88
|
export declare function boundScopeIdsFor(access: JudgmentAccess, command: {
|
|
89
89
|
scopes?: MemoryScopeRef[];
|
|
90
90
|
}): string[];
|
|
91
|
+
/** Whether a link may name this target under the caller's admitted write scopes. */
|
|
92
|
+
export declare function referenceExists(adapter: Pick<DatabaseAdapter, 'prepare'>, reference: WorkReference, admittedScopeIds: readonly string[]): boolean;
|
|
91
93
|
export declare function appendJudgment(command: JudgmentCommand, access: JudgmentAccess, options: JudgmentKnowledgeOptions): Promise<JudgmentReceipt>;
|
|
92
94
|
export declare function createJudgmentWriter(options: JudgmentKnowledgeOptions): {
|
|
93
95
|
appendJudgment: (command: JudgmentCommand, access: JudgmentAccess) => Promise<JudgmentReceipt>;
|
|
@@ -7,6 +7,7 @@ exports.JudgmentError = void 0;
|
|
|
7
7
|
exports.judgmentRecordId = judgmentRecordId;
|
|
8
8
|
exports.admittedScopeIds = admittedScopeIds;
|
|
9
9
|
exports.boundScopeIdsFor = boundScopeIdsFor;
|
|
10
|
+
exports.referenceExists = referenceExists;
|
|
10
11
|
exports.appendJudgment = appendJudgment;
|
|
11
12
|
exports.createJudgmentWriter = createJudgmentWriter;
|
|
12
13
|
exports.mapTwinEdgeRow = mapTwinEdgeRow;
|
|
@@ -130,6 +131,12 @@ const AMEND_FIELDS = [
|
|
|
130
131
|
/** Public-save parity fields carried by the command are validated up front so a
|
|
131
132
|
* malformed command fails before any write or embedder call. */
|
|
132
133
|
function validateCommandFields(command) {
|
|
134
|
+
// A replacement is a stated act: without its own reason the supersedes link would carry the
|
|
135
|
+
// whole record's reasoning instead.
|
|
136
|
+
for (const replacement of command.replaces ?? []) {
|
|
137
|
+
requireText(replacement.id, 'replaces id');
|
|
138
|
+
requireText(replacement.reason, 'replaces reason');
|
|
139
|
+
}
|
|
133
140
|
if (command.confidence !== undefined &&
|
|
134
141
|
(!Number.isFinite(command.confidence) || command.confidence < 0 || command.confidence > 1)) {
|
|
135
142
|
throw new JudgmentError('INVALID_COMMAND', 'confidence must be a number between 0 and 1');
|
|
@@ -163,14 +170,8 @@ function validateCommandFields(command) {
|
|
|
163
170
|
throw new JudgmentError('INVALID_COMMAND', 'amends requires at least one projection field to set');
|
|
164
171
|
}
|
|
165
172
|
}
|
|
166
|
-
for (const edge of command.projections?.decisionEdges ?? []) {
|
|
167
|
-
if (edge.fromId !== undefined) {
|
|
168
|
-
requireText(edge.fromId, 'projection edge source');
|
|
169
|
-
}
|
|
170
|
-
requireText(edge.targetId, 'projection edge target');
|
|
171
|
-
requireText(edge.relationship, 'projection edge relationship');
|
|
172
|
-
}
|
|
173
173
|
}
|
|
174
|
+
/** Whether a link may name this target under the caller's admitted write scopes. */
|
|
174
175
|
function referenceExists(adapter, reference, admittedScopeIds) {
|
|
175
176
|
if (reference.kind === 'memory') {
|
|
176
177
|
const row = adapter
|
|
@@ -207,7 +208,13 @@ function referenceExists(adapter, reference, admittedScopeIds) {
|
|
|
207
208
|
.get(reference.id) !== undefined);
|
|
208
209
|
}
|
|
209
210
|
if (reference.kind === 'edge') {
|
|
210
|
-
|
|
211
|
+
// A correction reads the link it corrects, so both of that link's ends must be reachable.
|
|
212
|
+
const edge = adapter
|
|
213
|
+
.prepare('SELECT subject_kind, subject_id, object_kind, object_id FROM twin_edges WHERE edge_id = ?')
|
|
214
|
+
.get(reference.id);
|
|
215
|
+
return (edge !== undefined &&
|
|
216
|
+
referenceExists(adapter, { kind: edge.subject_kind, id: edge.subject_id }, admittedScopeIds) &&
|
|
217
|
+
referenceExists(adapter, { kind: edge.object_kind, id: edge.object_id }, admittedScopeIds));
|
|
211
218
|
}
|
|
212
219
|
return false;
|
|
213
220
|
}
|
|
@@ -255,6 +262,26 @@ const AMEND_COLUMN_MAP = {
|
|
|
255
262
|
supersedes: 'supersedes',
|
|
256
263
|
supersededBy: 'superseded_by',
|
|
257
264
|
};
|
|
265
|
+
/** The column values an amendment or a replacement is about to overwrite, per target. */
|
|
266
|
+
function priorValuesFor(adapter, command) {
|
|
267
|
+
const read = (id, columns) => adapter.prepare(`SELECT ${columns.join(', ')} FROM decisions WHERE id = ?`).get(id);
|
|
268
|
+
const replaced = (command.replaces ?? []).map((replacement) => {
|
|
269
|
+
const prior = read(replacement.id, ['status', 'superseded_by']);
|
|
270
|
+
return {
|
|
271
|
+
target: replacement.id,
|
|
272
|
+
values: { status: prior?.status ?? null, supersededBy: prior?.superseded_by ?? null },
|
|
273
|
+
};
|
|
274
|
+
});
|
|
275
|
+
const amended = (command.amends ?? []).map((amendment) => {
|
|
276
|
+
const fields = AMEND_FIELDS.filter((field) => field in amendment);
|
|
277
|
+
const prior = read(amendment.target.id, fields.map((field) => AMEND_COLUMN_MAP[field]));
|
|
278
|
+
return {
|
|
279
|
+
target: amendment.target.id,
|
|
280
|
+
values: Object.fromEntries(fields.map((field) => [field, prior?.[AMEND_COLUMN_MAP[field]] ?? null])),
|
|
281
|
+
};
|
|
282
|
+
});
|
|
283
|
+
return [...replaced, ...amended];
|
|
284
|
+
}
|
|
258
285
|
function applyAmendment(adapter, amendment, now) {
|
|
259
286
|
const sets = ['updated_at = ?'];
|
|
260
287
|
const params = [now];
|
|
@@ -268,7 +295,7 @@ function applyAmendment(adapter, amendment, now) {
|
|
|
268
295
|
.prepare(`UPDATE decisions SET ${sets.join(', ')} WHERE id = ?`)
|
|
269
296
|
.run(...params, amendment.target.id);
|
|
270
297
|
}
|
|
271
|
-
function applyProjections(adapter, command, recordId, effectiveScopes
|
|
298
|
+
function applyProjections(adapter, command, recordId, effectiveScopes) {
|
|
272
299
|
const projections = command.projections;
|
|
273
300
|
if (!projections)
|
|
274
301
|
return;
|
|
@@ -280,17 +307,6 @@ function applyProjections(adapter, command, recordId, effectiveScopes, now) {
|
|
|
280
307
|
scopes: effectiveScopes,
|
|
281
308
|
});
|
|
282
309
|
}
|
|
283
|
-
for (const targetId of projections.supersedeTargets ?? []) {
|
|
284
|
-
adapter
|
|
285
|
-
.prepare("UPDATE decisions SET superseded_by = ?, status = 'superseded', updated_at = ? WHERE id = ?")
|
|
286
|
-
.run(recordId, now, targetId);
|
|
287
|
-
}
|
|
288
|
-
const edgeInsert = adapter.prepare(`INSERT INTO decision_edges
|
|
289
|
-
(from_id, to_id, relationship, reason, weight, created_at, created_by, approved_by_user)
|
|
290
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
291
|
-
for (const edge of projections.decisionEdges ?? []) {
|
|
292
|
-
edgeInsert.run(edge.fromId ?? recordId, edge.targetId, edge.relationship, edge.reason ?? null, edge.weight ?? 1, now, edge.createdBy ?? 'user', edge.approvedByUser ?? 1);
|
|
293
|
-
}
|
|
294
310
|
}
|
|
295
311
|
function parseReceipt(value) {
|
|
296
312
|
let parsed;
|
|
@@ -339,7 +355,6 @@ function insertLink(adapter, command, recordId, link, index, access, now) {
|
|
|
339
355
|
agent_id: Object.hasOwn(command, 'agentId') ? (command.agentId ?? undefined) : access.agentId,
|
|
340
356
|
model_run_id: command.modelRunId ?? undefined,
|
|
341
357
|
reason_text: typeof link.attrs?.reason === 'string' ? link.attrs.reason : (command.reasoning ?? undefined),
|
|
342
|
-
evidence_refs: command.replaces?.length ? command.replaces : undefined,
|
|
343
358
|
content_hash: contentHash,
|
|
344
359
|
created_at: now,
|
|
345
360
|
});
|
|
@@ -405,6 +420,9 @@ async function appendJudgmentOnAdapter(adapter, command, access, embedder) {
|
|
|
405
420
|
}
|
|
406
421
|
return;
|
|
407
422
|
}
|
|
423
|
+
// What the amendments and replacements will overwrite, kept in this record: the target
|
|
424
|
+
// columns are a projection, and the appended records are the history.
|
|
425
|
+
const replacedValues = priorValuesFor(adapter, command);
|
|
408
426
|
const decisionRowId = (0, db_manager_js_1.insertPreparedDecision)(adapter, {
|
|
409
427
|
id: recordId,
|
|
410
428
|
topic: command.topic,
|
|
@@ -443,7 +461,9 @@ async function appendJudgmentOnAdapter(adapter, command, access, embedder) {
|
|
|
443
461
|
kind = COALESCE(?, kind), status = COALESCE(?, status),
|
|
444
462
|
summary = COALESCE(?, summary), is_static = COALESCE(?, is_static)
|
|
445
463
|
WHERE id = ?`)
|
|
446
|
-
.run(command.recordKind, (0, canonicalize_js_1.canonicalizeJSON)(
|
|
464
|
+
.run(command.recordKind, (0, canonicalize_js_1.canonicalizeJSON)(replacedValues.length > 0
|
|
465
|
+
? { ...(command.payload ?? {}), replacedValues }
|
|
466
|
+
: (command.payload ?? {})), command.appliesFrom ?? null, command.appliesUntil ?? null, command.record?.kind ?? null, command.record?.status ?? null,
|
|
447
467
|
// Never leave summary NULL: legacy readers (evolution candidates,
|
|
448
468
|
// recall) assume it is text. The command summary is the authored
|
|
449
469
|
// summary when the record does not carry a distinct one.
|
|
@@ -477,7 +497,7 @@ async function appendJudgmentOnAdapter(adapter, command, access, embedder) {
|
|
|
477
497
|
}
|
|
478
498
|
applyAmendment(adapter, amendment, domainNow);
|
|
479
499
|
}
|
|
480
|
-
applyProjections(adapter, command, recordId, effectiveScopes
|
|
500
|
+
applyProjections(adapter, command, recordId, effectiveScopes);
|
|
481
501
|
// A commitment's own clock is domain time, like the record's: `updated_at`
|
|
482
502
|
// is what the board sorts and filters on, so a caller that states when a
|
|
483
503
|
// revision happened must see that time on the row, not the wall clock the
|
|
@@ -547,9 +567,9 @@ async function appendJudgmentOnAdapter(adapter, command, access, embedder) {
|
|
|
547
567
|
(commitment_id, revision, record_id, operation, set_json, clear_json, applies_from, applies_until, created_at, agent_id, model_run_id)
|
|
548
568
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
549
569
|
.run(work.commitmentId, revision, recordId, work.operation, (0, canonicalize_js_1.canonicalizeJSON)(workPatch(work)), (0, canonicalize_js_1.canonicalizeJSON)(work.clear ?? []), command.eventDatetime ?? null, command.appliesUntil ?? null, domainNow, access.agentId, command.modelRunId ?? null);
|
|
550
|
-
|
|
551
|
-
//
|
|
552
|
-
|
|
570
|
+
// No host edge to the previous revision: the order lives in commitment_assignments, and
|
|
571
|
+
// how a revision relates to an earlier record is the agent's link to state (owner,
|
|
572
|
+
// 2026-09-30).
|
|
553
573
|
adapter
|
|
554
574
|
.prepare('UPDATE commitments SET current_revision = ?, head_record_id = ?, withdrawn = ?, updated_at = ?, agent_id = ?, model_run_id = ? WHERE commitment_id = ?')
|
|
555
575
|
.run(revision, recordId, work.operation === 'withdraw' ? 1 : 0, domainNow, access.agentId, command.modelRunId ?? null, work.commitmentId);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { DatabaseAdapter } from '../db-manager.js';
|
|
2
|
+
import type { RecordLink } from '../memory/judgment-types.js';
|
|
3
|
+
import type { JudgmentAccess } from './judgments.js';
|
|
4
|
+
import type { TwinRef } from './twin-edge-types.js';
|
|
5
|
+
/**
|
|
6
|
+
* One edge the agent appends between records that already exist, with the sentence of why.
|
|
7
|
+
*
|
|
8
|
+
* Nothing is edited: a link is a new row, and a wrong link is answered by a newer one that
|
|
9
|
+
* contradicts it. The row is its own receipt, so no record or revision is written for it.
|
|
10
|
+
*/
|
|
11
|
+
export interface LinkCommand {
|
|
12
|
+
commandId: string;
|
|
13
|
+
/** The record the statement is made from; for a work item, its head revision. */
|
|
14
|
+
from: {
|
|
15
|
+
kind: 'memory';
|
|
16
|
+
id: string;
|
|
17
|
+
};
|
|
18
|
+
/** An edge target is a correction and takes `contradicts`. */
|
|
19
|
+
to: TwinRef;
|
|
20
|
+
relation: RecordLink['relation'];
|
|
21
|
+
reason: string;
|
|
22
|
+
/** Observations or records the reason rests on. */
|
|
23
|
+
evidenceRefs?: TwinRef[];
|
|
24
|
+
/** Extra relation attributes, such as a person's role. */
|
|
25
|
+
attrs?: Record<string, unknown>;
|
|
26
|
+
agentId?: string | null;
|
|
27
|
+
modelRunId?: string | null;
|
|
28
|
+
}
|
|
29
|
+
export interface LinkReceipt {
|
|
30
|
+
edgeId: string;
|
|
31
|
+
createdAt: number;
|
|
32
|
+
/** True when the command id was already written with this same link. */
|
|
33
|
+
replayed: boolean;
|
|
34
|
+
}
|
|
35
|
+
/** A stored link, found by the command id that wrote it. */
|
|
36
|
+
export interface StoredLink extends LinkReceipt {
|
|
37
|
+
from: TwinRef;
|
|
38
|
+
to: TwinRef;
|
|
39
|
+
relation: string;
|
|
40
|
+
reason: string | null;
|
|
41
|
+
}
|
|
42
|
+
/** A link's id is fixed by who asked and under which command id, so a retry finds its row. */
|
|
43
|
+
export declare function linkEdgeId(principalId: string, commandId: string): string;
|
|
44
|
+
/**
|
|
45
|
+
* The link a principal already wrote under this command id, if any: a retry finds it here, with
|
|
46
|
+
* what it states, so the caller can tell a retry from another link under the same id.
|
|
47
|
+
*/
|
|
48
|
+
export declare function findLink(adapter: Pick<DatabaseAdapter, 'prepare'>, commandId: string, access: JudgmentAccess): StoredLink | null;
|
|
49
|
+
export declare function appendLink(adapter: Pick<DatabaseAdapter, 'prepare' | 'transaction' | 'transactionImmediate'>, command: LinkCommand, access: JudgmentAccess): LinkReceipt;
|
|
50
|
+
//# sourceMappingURL=links.d.ts.map
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.linkEdgeId = linkEdgeId;
|
|
7
|
+
exports.findLink = findLink;
|
|
8
|
+
exports.appendLink = appendLink;
|
|
9
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
10
|
+
const canonicalize_js_1 = require("../canonicalize.js");
|
|
11
|
+
const judgments_js_1 = require("./judgments.js");
|
|
12
|
+
const TARGET_KINDS = new Set(['memory', 'registry', 'observation', 'edge']);
|
|
13
|
+
const EVIDENCE_KINDS = new Set(['memory', 'observation']);
|
|
14
|
+
/**
|
|
15
|
+
* Relations a link states. Replacing (supersedes) and amending change the target's state, so they
|
|
16
|
+
* are acts written with a record (`replaces`, an amendment), and a source citation (derived_from)
|
|
17
|
+
* is written with the record that rests on it.
|
|
18
|
+
*/
|
|
19
|
+
const RELATIONS = new Set([
|
|
20
|
+
'refines',
|
|
21
|
+
'contradicts',
|
|
22
|
+
'mentions',
|
|
23
|
+
'builds_on',
|
|
24
|
+
'debates',
|
|
25
|
+
'synthesizes',
|
|
26
|
+
'blocks',
|
|
27
|
+
'next_action_for',
|
|
28
|
+
]);
|
|
29
|
+
function nonblank(value, field) {
|
|
30
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
31
|
+
throw new judgments_js_1.JudgmentError('INVALID_COMMAND', `${field} must be nonblank`);
|
|
32
|
+
}
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
/** A link's id is fixed by who asked and under which command id, so a retry finds its row. */
|
|
36
|
+
function linkEdgeId(principalId, commandId) {
|
|
37
|
+
return `link_${node_crypto_1.default
|
|
38
|
+
.createHash('sha256')
|
|
39
|
+
.update((0, canonicalize_js_1.canonicalizeJSON)({ principalId, commandId }))
|
|
40
|
+
.digest('hex')
|
|
41
|
+
.slice(0, 24)}`;
|
|
42
|
+
}
|
|
43
|
+
function linkContentHash(edgeId, command) {
|
|
44
|
+
return node_crypto_1.default
|
|
45
|
+
.createHash('sha256')
|
|
46
|
+
.update((0, canonicalize_js_1.canonicalizeJSON)({
|
|
47
|
+
edgeId,
|
|
48
|
+
from: command.from,
|
|
49
|
+
to: command.to,
|
|
50
|
+
relation: command.relation,
|
|
51
|
+
reason: command.reason,
|
|
52
|
+
evidenceRefs: command.evidenceRefs ?? [],
|
|
53
|
+
attrs: command.attrs ?? {},
|
|
54
|
+
}))
|
|
55
|
+
.digest();
|
|
56
|
+
}
|
|
57
|
+
function assertReachable(adapter, refs, access) {
|
|
58
|
+
// The same rule a record's own links follow (judgments.ts): the ends sit in the caller's
|
|
59
|
+
// admitted scopes, and an edge target has both of its ends there.
|
|
60
|
+
const scopeIds = (0, judgments_js_1.admittedScopeIds)(access);
|
|
61
|
+
for (const ref of refs) {
|
|
62
|
+
if (!(0, judgments_js_1.referenceExists)(adapter, ref, scopeIds)) {
|
|
63
|
+
// Echo only the caller's own input: an unavailable id reads the same whether wrong or outside scope.
|
|
64
|
+
throw new judgments_js_1.JudgmentError('REFERENCE_NOT_FOUND', `A link end is unavailable: ${ref.kind} ${ref.id}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The link a principal already wrote under this command id, if any: a retry finds it here, with
|
|
70
|
+
* what it states, so the caller can tell a retry from another link under the same id.
|
|
71
|
+
*/
|
|
72
|
+
function findLink(adapter, commandId, access) {
|
|
73
|
+
const existing = (0, judgments_js_1.getTwinEdge)(adapter, linkEdgeId(access.principalId, commandId));
|
|
74
|
+
return existing
|
|
75
|
+
? {
|
|
76
|
+
edgeId: existing.edge_id,
|
|
77
|
+
createdAt: existing.created_at,
|
|
78
|
+
replayed: true,
|
|
79
|
+
from: existing.subject_ref,
|
|
80
|
+
to: existing.object_ref,
|
|
81
|
+
relation: existing.edge_type,
|
|
82
|
+
reason: existing.reason_text,
|
|
83
|
+
}
|
|
84
|
+
: null;
|
|
85
|
+
}
|
|
86
|
+
function appendLink(adapter, command, access) {
|
|
87
|
+
nonblank(command.commandId, 'commandId');
|
|
88
|
+
nonblank(access.principalId, 'principalId');
|
|
89
|
+
const reason = nonblank(command.reason, 'reason').trim();
|
|
90
|
+
if (command.from?.kind !== 'memory') {
|
|
91
|
+
throw new judgments_js_1.JudgmentError('INVALID_COMMAND', 'A link starts from a memory record');
|
|
92
|
+
}
|
|
93
|
+
nonblank(command.from.id, 'from.id');
|
|
94
|
+
if (!command.to || !TARGET_KINDS.has(command.to.kind)) {
|
|
95
|
+
throw new judgments_js_1.JudgmentError('INVALID_COMMAND', 'A link targets a memory record, a registry node, an observation or an edge');
|
|
96
|
+
}
|
|
97
|
+
nonblank(command.to.id, 'to.id');
|
|
98
|
+
if (!RELATIONS.has(command.relation)) {
|
|
99
|
+
throw new judgments_js_1.JudgmentError('INVALID_COMMAND', `Unknown link relation: ${command.relation}`);
|
|
100
|
+
}
|
|
101
|
+
if (command.to.kind === 'edge' && command.relation !== 'contradicts') {
|
|
102
|
+
throw new judgments_js_1.JudgmentError('INVALID_COMMAND', 'A link to an edge is a correction and takes the relation contradicts');
|
|
103
|
+
}
|
|
104
|
+
for (const ref of command.evidenceRefs ?? []) {
|
|
105
|
+
if (!EVIDENCE_KINDS.has(ref.kind)) {
|
|
106
|
+
throw new judgments_js_1.JudgmentError('INVALID_COMMAND', 'Link evidence is an observation or a record');
|
|
107
|
+
}
|
|
108
|
+
nonblank(ref.id, 'evidence id');
|
|
109
|
+
}
|
|
110
|
+
const edgeId = linkEdgeId(access.principalId, command.commandId);
|
|
111
|
+
const contentHash = linkContentHash(edgeId, { ...command, reason });
|
|
112
|
+
const replay = () => {
|
|
113
|
+
const existing = (0, judgments_js_1.getTwinEdge)(adapter, edgeId);
|
|
114
|
+
if (!existing)
|
|
115
|
+
return null;
|
|
116
|
+
if (!existing.content_hash.equals(contentHash)) {
|
|
117
|
+
throw new judgments_js_1.JudgmentError('COMMAND_CONFLICT', 'The command id is already bound to another link');
|
|
118
|
+
}
|
|
119
|
+
return { edgeId, createdAt: existing.created_at, replayed: true };
|
|
120
|
+
};
|
|
121
|
+
assertReachable(adapter, [command.from, command.to, ...(command.evidenceRefs ?? [])], access);
|
|
122
|
+
const earlier = replay();
|
|
123
|
+
if (earlier)
|
|
124
|
+
return earlier;
|
|
125
|
+
const now = Date.now();
|
|
126
|
+
const transaction = adapter.transactionImmediate
|
|
127
|
+
? adapter.transactionImmediate.bind(adapter)
|
|
128
|
+
: adapter.transaction.bind(adapter);
|
|
129
|
+
return transaction(() => {
|
|
130
|
+
const raced = replay();
|
|
131
|
+
if (raced)
|
|
132
|
+
return raced;
|
|
133
|
+
(0, judgments_js_1.insertTwinEdge)(adapter, {
|
|
134
|
+
edge_id: edgeId,
|
|
135
|
+
edge_type: command.relation,
|
|
136
|
+
subject_ref: command.from,
|
|
137
|
+
object_ref: command.to,
|
|
138
|
+
relation_attrs: { ...(command.attrs ?? {}), reason },
|
|
139
|
+
confidence: 1.0,
|
|
140
|
+
source: access.edgeSource ?? 'agent',
|
|
141
|
+
agent_id: Object.hasOwn(command, 'agentId') ? (command.agentId ?? undefined) : access.agentId,
|
|
142
|
+
model_run_id: command.modelRunId ?? undefined,
|
|
143
|
+
request_idempotency_key: command.commandId,
|
|
144
|
+
reason_text: reason,
|
|
145
|
+
evidence_refs: command.evidenceRefs?.length ? command.evidenceRefs : undefined,
|
|
146
|
+
content_hash: contentHash,
|
|
147
|
+
created_at: now,
|
|
148
|
+
});
|
|
149
|
+
return { edgeId, createdAt: now, replayed: false };
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=links.js.map
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { DatabaseInstance, DecisionRecord } from '../db-manager.js';
|
|
10
10
|
import type { MemoryKindFilter } from '../memory/types.js';
|
|
11
|
+
/** Statuses default recall leaves out; history (`includeHistory`) shows them. */
|
|
12
|
+
export declare const RECALL_EXCLUDED_STATUSES: readonly ["superseded", "contradicted", "stale"];
|
|
11
13
|
/**
|
|
12
14
|
* Brute-force cosine similarity search over stored embeddings.
|
|
13
15
|
*
|
|
@@ -31,7 +33,10 @@ export declare function vectorSearch(adapter: DatabaseInstance, queryEmbedding:
|
|
|
31
33
|
* @param limit - Max rows to return
|
|
32
34
|
* @returns Matching decision IDs with BM25 rank scores
|
|
33
35
|
*/
|
|
34
|
-
export declare function fts5Search(adapter: DatabaseInstance, query: string, limit?: number, kind?: MemoryKindFilter
|
|
36
|
+
export declare function fts5Search(adapter: DatabaseInstance, query: string, limit?: number, kind?: MemoryKindFilter, exclude?: {
|
|
37
|
+
statuses?: readonly string[];
|
|
38
|
+
amendments?: boolean;
|
|
39
|
+
}): Promise<{
|
|
35
40
|
id: string;
|
|
36
41
|
rank: number;
|
|
37
42
|
}[]>;
|
package/dist/knowledge/search.js
CHANGED
|
@@ -8,8 +8,11 @@
|
|
|
8
8
|
* @module knowledge/search
|
|
9
9
|
*/
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.RECALL_EXCLUDED_STATUSES = void 0;
|
|
11
12
|
exports.vectorSearch = vectorSearch;
|
|
12
13
|
exports.fts5Search = fts5Search;
|
|
14
|
+
/** Statuses default recall leaves out; history (`includeHistory`) shows them. */
|
|
15
|
+
exports.RECALL_EXCLUDED_STATUSES = ['superseded', 'contradicted', 'stale'];
|
|
13
16
|
/**
|
|
14
17
|
* Brute-force cosine similarity search over stored embeddings.
|
|
15
18
|
*
|
|
@@ -59,7 +62,7 @@ async function vectorSearch(adapter, queryEmbedding, limit = 5, threshold = 0.7,
|
|
|
59
62
|
* @param limit - Max rows to return
|
|
60
63
|
* @returns Matching decision IDs with BM25 rank scores
|
|
61
64
|
*/
|
|
62
|
-
async function fts5Search(adapter, query, limit = 10, kind) {
|
|
65
|
+
async function fts5Search(adapter, query, limit = 10, kind, exclude) {
|
|
63
66
|
// An absent FTS table is a real answer - no rows. A failing adapter is not,
|
|
64
67
|
// so this lookup is left unguarded and its errors reach the caller.
|
|
65
68
|
const tableCheck = adapter
|
|
@@ -68,24 +71,27 @@ async function fts5Search(adapter, query, limit = 10, kind) {
|
|
|
68
71
|
if (!tableCheck)
|
|
69
72
|
return [];
|
|
70
73
|
// Query execution - let errors propagate to the caller
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
const kinds = Array.isArray(kind) ? kind : kind === undefined ? [] : [kind];
|
|
75
|
+
const kindClause = kinds.length === 0 ? '' : `AND d.kind IN (${kinds.map(() => '?').join(', ')})`;
|
|
76
|
+
// Excluded rows are left out before LIMIT, so they cannot fill the pool ahead of rows that stay.
|
|
77
|
+
const statuses = exclude?.statuses ?? [];
|
|
78
|
+
const statusClause = statuses.length === 0
|
|
79
|
+
? ''
|
|
80
|
+
: `AND (d.status IS NULL OR d.status NOT IN (${statuses.map(() => '?').join(', ')}))`;
|
|
81
|
+
const amendmentClause = exclude?.amendments
|
|
82
|
+
? "AND json_extract(d.payload_json, '$.amended') IS NULL"
|
|
83
|
+
: '';
|
|
76
84
|
const stmt = adapter.prepare(`
|
|
77
85
|
SELECT d.id, rank
|
|
78
86
|
FROM decisions_fts
|
|
79
87
|
JOIN decisions d ON decisions_fts.rowid = d.rowid
|
|
80
88
|
WHERE decisions_fts MATCH ?
|
|
81
89
|
${kindClause}
|
|
90
|
+
${statusClause}
|
|
91
|
+
${amendmentClause}
|
|
82
92
|
ORDER BY rank
|
|
83
93
|
LIMIT ?
|
|
84
94
|
`);
|
|
85
|
-
return
|
|
86
|
-
? stmt.all(query, ...kind, limit)
|
|
87
|
-
: kind === undefined
|
|
88
|
-
? stmt.all(query, limit)
|
|
89
|
-
: stmt.all(query, kind, limit));
|
|
95
|
+
return stmt.all(query, ...kinds, ...statuses, limit);
|
|
90
96
|
}
|
|
91
97
|
//# sourceMappingURL=search.js.map
|