coaiajs 0.4.3 → 0.5.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.
Files changed (55) hide show
  1. package/README.md +2 -2
  2. package/dist/mcp/config.d.ts.map +1 -1
  3. package/dist/mcp/config.js +13 -1
  4. package/dist/mcp/config.js.map +1 -1
  5. package/dist/mcp/server.js +11 -1
  6. package/dist/mcp/server.js.map +1 -1
  7. package/dist/src/cli.js +15 -2
  8. package/dist/src/cli.js.map +1 -1
  9. package/dist/src/narrative/argument-hygiene.d.ts +46 -0
  10. package/dist/src/narrative/argument-hygiene.d.ts.map +1 -0
  11. package/dist/src/narrative/argument-hygiene.js +155 -0
  12. package/dist/src/narrative/argument-hygiene.js.map +1 -0
  13. package/dist/src/narrative/contract.d.ts +200 -0
  14. package/dist/src/narrative/contract.d.ts.map +1 -0
  15. package/dist/src/narrative/contract.js +274 -0
  16. package/dist/src/narrative/contract.js.map +1 -0
  17. package/dist/src/narrative/github-bridge.d.ts +23 -0
  18. package/dist/src/narrative/github-bridge.d.ts.map +1 -0
  19. package/dist/src/narrative/github-bridge.js +320 -0
  20. package/dist/src/narrative/github-bridge.js.map +1 -0
  21. package/dist/src/narrative/graph-manager.d.ts +83 -2
  22. package/dist/src/narrative/graph-manager.d.ts.map +1 -1
  23. package/dist/src/narrative/graph-manager.js +444 -54
  24. package/dist/src/narrative/graph-manager.js.map +1 -1
  25. package/dist/src/narrative/index.d.ts +24 -3
  26. package/dist/src/narrative/index.d.ts.map +1 -1
  27. package/dist/src/narrative/index.js +36 -15
  28. package/dist/src/narrative/index.js.map +1 -1
  29. package/dist/src/narrative/jsonl-preservation.d.ts +26 -0
  30. package/dist/src/narrative/jsonl-preservation.d.ts.map +1 -0
  31. package/dist/src/narrative/jsonl-preservation.js +293 -0
  32. package/dist/src/narrative/jsonl-preservation.js.map +1 -0
  33. package/dist/src/narrative/jsonl-records.d.ts +31 -0
  34. package/dist/src/narrative/jsonl-records.d.ts.map +1 -0
  35. package/dist/src/narrative/jsonl-records.js +69 -0
  36. package/dist/src/narrative/jsonl-records.js.map +1 -0
  37. package/dist/src/narrative/tool-definitions.d.ts +2 -1
  38. package/dist/src/narrative/tool-definitions.d.ts.map +1 -1
  39. package/dist/src/narrative/tool-definitions.js +134 -1
  40. package/dist/src/narrative/tool-definitions.js.map +1 -1
  41. package/dist/src/narrative/tool-handlers.d.ts.map +1 -1
  42. package/dist/src/narrative/tool-handlers.js +203 -20
  43. package/dist/src/narrative/tool-handlers.js.map +1 -1
  44. package/dist/src/narrative/types.d.ts +1 -1
  45. package/dist/src/narrative/types.d.ts.map +1 -1
  46. package/dist/src/narrative/validation.d.ts +13 -2
  47. package/dist/src/narrative/validation.d.ts.map +1 -1
  48. package/dist/src/narrative/validation.js +19 -4
  49. package/dist/src/narrative/validation.js.map +1 -1
  50. package/dist/src/types.d.ts +189 -0
  51. package/dist/src/types.d.ts.map +1 -1
  52. package/dist/src/types.js.map +1 -1
  53. package/llms-full.txt +64 -6
  54. package/llms.txt +4 -3
  55. package/package.json +5 -1
@@ -0,0 +1,155 @@
1
+ /**
2
+ * COAIA Narrative - Argument Hygiene
3
+ *
4
+ * A malformed tool call must not become chart content.
5
+ *
6
+ * On 2026-07-30 a live store was found carrying seven observations whose bodies
7
+ * ended in `</currentReality>` followed by a `<parameter name="dueDate">` block:
8
+ * the raw text of a call whose argument tags never parsed, persisted verbatim as
9
+ * if it were prose. The call did not fail — it wrote, and every consumer that
10
+ * renders current reality has shown that tag ever since.
11
+ *
12
+ * The check belongs here, at the write boundary. Anything that reaches the JSONL
13
+ * is already in every reader's render, so a read-side filter arrives too late.
14
+ * A body carrying plainly unparsed call syntax is refused with the offending
15
+ * fragment named, so the caller can retry with the text it meant to record.
16
+ */
17
+ import { ALL_TOOL_DEFINITIONS } from './tool-definitions.js';
18
+ /**
19
+ * Tool-call machinery that has no business inside a text body. These are the
20
+ * tags a call is *made of*, not tags a chart can legitimately talk about in the
21
+ * same breath as its own content.
22
+ */
23
+ const CALL_MACHINERY = [
24
+ {
25
+ reason: 'a tool-call parameter tag',
26
+ pattern: /<\s*\/?\s*(?:[A-Za-z][\w.-]*:)?parameter\b/i
27
+ },
28
+ {
29
+ reason: 'a tool-call invoke tag',
30
+ pattern: /<\s*\/?\s*(?:[A-Za-z][\w.-]*:)?invoke\b/i
31
+ },
32
+ {
33
+ reason: 'a tool-call function_calls tag',
34
+ pattern: /<\s*\/?\s*(?:[A-Za-z][\w.-]*:)?function_calls\b/i
35
+ }
36
+ ];
37
+ /**
38
+ * Every argument name this package declares, gathered from the tool schemas so
39
+ * the vocabulary cannot drift from the tools themselves. A bare closing tag for
40
+ * one of these names inside a text body is a parse that failed, not prose.
41
+ */
42
+ const ARGUMENT_NAMES = (() => {
43
+ const names = new Set();
44
+ const walk = (node) => {
45
+ if (!node || typeof node !== 'object')
46
+ return;
47
+ const record = node;
48
+ const properties = record.properties;
49
+ if (properties && typeof properties === 'object' && !Array.isArray(properties)) {
50
+ for (const [key, child] of Object.entries(properties)) {
51
+ if (/^[A-Za-z_][A-Za-z0-9_-]*$/.test(key))
52
+ names.add(key);
53
+ walk(child);
54
+ }
55
+ }
56
+ if (record.items)
57
+ walk(record.items);
58
+ };
59
+ for (const tool of ALL_TOOL_DEFINITIONS)
60
+ walk(tool.inputSchema);
61
+ // Longest first, so a report names `</currentReality>` rather than a shorter
62
+ // suffix that happens to match inside it.
63
+ return [...names].sort((a, b) => b.length - a.length);
64
+ })();
65
+ const ARGUMENT_CLOSING_TAG = new RegExp(`<\\s*/\\s*(${ARGUMENT_NAMES.join('|')})\\s*>`, 'i');
66
+ /** Quote back enough of the offending text to be recognisable, not the whole body. */
67
+ function quoteFragment(text, index, matchLength) {
68
+ const close = text.indexOf('>', index + matchLength - 1);
69
+ const end = close !== -1 && close - index < 80 ? close + 1 : Math.min(text.length, index + 60);
70
+ const fragment = text.slice(index, end).replace(/\s+/g, ' ').trim();
71
+ return end < text.length && close === -1 ? `${fragment}…` : fragment;
72
+ }
73
+ /**
74
+ * Return the first unparsed-call-syntax fragment in `text`, or null if the text
75
+ * is ordinary prose. Angle brackets on their own are fine — `<div>`, `a < b`,
76
+ * `<rootDir>` all pass. Only call machinery and argument closing tags are refused.
77
+ */
78
+ export function findUnparsedCallSyntax(text) {
79
+ if (typeof text !== 'string' || !text.includes('<'))
80
+ return null;
81
+ // Report the earliest fragment, not the first pattern that happens to match.
82
+ // The earliest one is where the prose ended and the call text began — the place
83
+ // the caller needs to look.
84
+ let earliest = null;
85
+ const consider = (index, length, reason) => {
86
+ if (!earliest || index < earliest.index)
87
+ earliest = { index, length, reason };
88
+ };
89
+ for (const { reason, pattern } of CALL_MACHINERY) {
90
+ const match = pattern.exec(text);
91
+ if (match)
92
+ consider(match.index, match[0].length, reason);
93
+ }
94
+ const closing = ARGUMENT_CLOSING_TAG.exec(text);
95
+ if (closing)
96
+ consider(closing.index, closing[0].length, `a closing tag for the '${closing[1]}' argument`);
97
+ if (!earliest)
98
+ return null;
99
+ const hit = earliest;
100
+ return {
101
+ fragment: quoteFragment(text, hit.index, hit.length),
102
+ reason: hit.reason,
103
+ index: hit.index
104
+ };
105
+ }
106
+ /**
107
+ * Walk any argument value — string, array, or object — and return the first
108
+ * location carrying unparsed call syntax.
109
+ */
110
+ export function findUnparsedCallSyntaxIn(value, path = '') {
111
+ if (typeof value === 'string') {
112
+ const hit = findUnparsedCallSyntax(value);
113
+ return hit ? { ...hit, path: path || 'value' } : null;
114
+ }
115
+ if (Array.isArray(value)) {
116
+ for (let index = 0; index < value.length; index++) {
117
+ const found = findUnparsedCallSyntaxIn(value[index], `${path}[${index}]`);
118
+ if (found)
119
+ return found;
120
+ }
121
+ return null;
122
+ }
123
+ if (value && typeof value === 'object') {
124
+ for (const [key, child] of Object.entries(value)) {
125
+ const found = findUnparsedCallSyntaxIn(child, path ? `${path}.${key}` : key);
126
+ if (found)
127
+ return found;
128
+ }
129
+ return null;
130
+ }
131
+ return null;
132
+ }
133
+ /** The message a caller sees: what was refused, where, and what to do next. */
134
+ export function describeUnparsedCallSyntax(location) {
135
+ return [
136
+ `Malformed call refused — nothing was written.`,
137
+ ``,
138
+ `\`${location.path}\` carries unparsed call syntax, not prose:`,
139
+ ` ${location.fragment}`,
140
+ `That is ${location.reason}.`,
141
+ ``,
142
+ `The argument tags of this call did not parse, so raw call text arrived as the`,
143
+ `value. Storing it would put a tag into the chart where a sentence belongs.`,
144
+ `Re-send the call with \`${location.path}\` holding only the text meant to be recorded.`
145
+ ].join('\n');
146
+ }
147
+ /** Throw unless `value` is free of unparsed call syntax. Used at the store's edge. */
148
+ export function assertNoUnparsedCallSyntax(value, path) {
149
+ const location = findUnparsedCallSyntaxIn(value, path);
150
+ if (location)
151
+ throw new Error(describeUnparsedCallSyntax(location));
152
+ }
153
+ /** The argument-name vocabulary, exported so the scrubber reports what the server refuses. */
154
+ export const KNOWN_ARGUMENT_NAMES = ARGUMENT_NAMES;
155
+ //# sourceMappingURL=argument-hygiene.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"argument-hygiene.js","sourceRoot":"","sources":["../../../src/narrative/argument-hygiene.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,200 @@
1
+ /**
2
+ * COAIA Narrative — the store read contract.
3
+ *
4
+ * WHAT THIS IS FOR
5
+ *
6
+ * This package owns the writes. Anything that renders a chart store was, until now,
7
+ * re-deriving the store's shape by hand — entity-kind strings, the `${chartId}_chart`
8
+ * naming scheme, which metadata key holds the MMOT trail. That knowledge living in two
9
+ * places is not a style problem: when it drifts, a renderer does not break, it quietly
10
+ * renders LESS. A chart holding real work looks identical to an empty one.
11
+ *
12
+ * HOW IT AVOIDS BEING THE THING IT WARNS ABOUT
13
+ *
14
+ * The first draft of this module re-implemented record classification by hand, and a
15
+ * review measured it returning 73 entities where the writer's own parser returned 76 —
16
+ * it dropped the legacy `type:"narrative_beat"` dialect and reported the losses as
17
+ * corruption. Duplication relocated rather than removed.
18
+ *
19
+ * So classification is NOT defined here. `./jsonl-records.js` holds the single
20
+ * definition, and both the writer (jsonl-preservation.ts) and this contract import it.
21
+ * What this module adds on top is the two things the writer's parser genuinely does not
22
+ * offer a renderer: per-line TOLERANCE (parseJsonlMemory throws on one bad line, which
23
+ * is correct for a writer and fatal for a surface), and the selectors/naming below.
24
+ *
25
+ * THREE RULES THIS MODULE KEEPS
26
+ *
27
+ * 1. Zero I/O. No `fs`, no `process`, no network. Callers own reading; this owns shape.
28
+ * 2. Never imports the server. The package's `main` IS the MCP bootstrap — importing the
29
+ * package root starts a stdio server — which is why this lives behind its own subpath.
30
+ * 3. Tolerant by construction, honest about what it skipped.
31
+ */
32
+ import type { Entity } from '../types.js';
33
+ /** Bumped when a shape changes in a way a reader must notice. */
34
+ export declare const CONTRACT_VERSION = 2;
35
+ /**
36
+ * Every entityType this package writes. `test-contract.js` scans this package's own
37
+ * source and fails if a kind is written that this list does not publish — so the
38
+ * contract cannot fall behind the writer without the suite going red.
39
+ */
40
+ export declare const ENTITY_TYPES: {
41
+ readonly chart: "structural_tension_chart";
42
+ readonly desiredOutcome: "desired_outcome";
43
+ readonly currentReality: "current_reality";
44
+ readonly actionStep: "action_step";
45
+ readonly narrativeBeat: "narrative_beat";
46
+ readonly wampumBelt: "wampum_belt";
47
+ };
48
+ export type EntityType = (typeof ENTITY_TYPES)[keyof typeof ENTITY_TYPES];
49
+ /**
50
+ * `EntityType | (string & {})` rather than `EntityType | string`: the latter collapses
51
+ * to plain `string`, which silently permits `e.entityType === 'strucutral_tension_chart'`
52
+ * — a typo that compiles clean and renders nothing. This form keeps autocomplete AND
53
+ * keeps the comparison checked, while still accepting kinds written by a newer writer.
54
+ */
55
+ export type EntityTypeLike = EntityType | (string & {});
56
+ /**
57
+ * MMOT phases, as the writer accepts them. `'full'` is included deliberately: it is the
58
+ * tool's DEFAULT (`phase: string = 'full'`) and appears in live stores. An earlier draft
59
+ * omitted it, which would have made a phase rail built from this list silently drop
60
+ * every default-phase evaluation.
61
+ */
62
+ export declare const MMOT_PHASES: readonly ["full", "acknowledge", "analyze", "update", "recommit"];
63
+ export type MmotPhase = (typeof MMOT_PHASES)[number];
64
+ export declare const isMmotPhase: (v: unknown) => v is MmotPhase;
65
+ /**
66
+ * Fritz's creating phases, carried on a chart as `metadata.phase`. Published alongside
67
+ * MMOT_PHASES because both occupy the key `phase` in different records, and a reader
68
+ * that assumes one vocabulary when it holds the other gets a confident wrong answer.
69
+ */
70
+ export declare const CREATING_PHASES: readonly ["germination", "assimilation", "completion"];
71
+ export type CreatingPhase = (typeof CREATING_PHASES)[number];
72
+ export type Direction = 'North' | 'South' | 'East' | 'West';
73
+ export declare const chartEntityName: (chartId: string) => string;
74
+ export declare const desiredOutcomeName: (chartId: string) => string;
75
+ export declare const currentRealityName: (chartId: string) => string;
76
+ /** MMOT narrative beats are named `${chartId}_mmot_${epochMs}`. */
77
+ export declare const mmotBeatPrefix: (chartId: string) => string;
78
+ export declare const isMmotBeatName: (name: string, chartId: string) => boolean;
79
+ /** One entry in a chart's MMOT trail (`chart.metadata.mmotEvaluations`). */
80
+ export interface MmotEvaluation {
81
+ phase: MmotPhase | (string & {});
82
+ assessment: string;
83
+ direction?: Direction | (string & {});
84
+ timestamp?: string;
85
+ }
86
+ export interface StoreEntity {
87
+ name: string;
88
+ entityType: EntityTypeLike;
89
+ observations?: string[];
90
+ metadata?: Record<string, unknown>;
91
+ type: string;
92
+ }
93
+ export interface StoreRelation {
94
+ from: string;
95
+ to: string;
96
+ relationType: string;
97
+ metadata?: Record<string, unknown>;
98
+ type: 'relation';
99
+ }
100
+ export interface ParsedStore {
101
+ /** Keyed by entity name; a later record for the same name supersedes an earlier one. */
102
+ entities: Map<string, StoreEntity>;
103
+ relations: StoreRelation[];
104
+ /**
105
+ * Lines that were not valid JSON, or were valid JSON that the writer's own predicates
106
+ * do not classify as any known record. NOT a corruption count on its own — see the
107
+ * warning on `parseStore` about what this can and cannot detect.
108
+ */
109
+ skipped: number;
110
+ }
111
+ /**
112
+ * Parse raw JSONL store content. Pure: give it a string, get records back.
113
+ *
114
+ * Classification is delegated to the writer's own predicates, so every dialect the
115
+ * writer round-trips — including the legacy top-level `type:"narrative_beat"` form — is
116
+ * read here identically. The only behaviour added is per-line tolerance: one unparseable
117
+ * line is skipped rather than throwing away the whole store, which is what a live
118
+ * surface needs and what `parseJsonlMemory` deliberately does not do.
119
+ *
120
+ * WHAT `skipped` CANNOT TELL YOU. Since writeJsonlMemoryFile became atomic (temp +
121
+ * rename) a reader should never see a torn file at all, and `skipped > 0` now genuinely
122
+ * suggests a foreign or damaged line. But a store truncated by something OTHER than this
123
+ * package can still be a syntactically perfect PREFIX — every whole line parses, and the
124
+ * result is simply a smaller store with `skipped === 0`. No reader can detect that from
125
+ * content alone. If that matters to a caller, compare entity counts across reads.
126
+ */
127
+ export declare function parseStore(raw: string): ParsedStore;
128
+ /** The writer's `Entity` shape, for callers already holding a KnowledgeGraph. */
129
+ export type { Entity };
130
+ /** Read a string-valued metadata key, or undefined when absent or the wrong type. */
131
+ export declare function metaString(e: StoreEntity | undefined, key: string): string | undefined;
132
+ /** A step or chart is complete when `metadata.completionStatus` is exactly true. */
133
+ export declare const isComplete: (e: StoreEntity | undefined) => boolean;
134
+ export declare const getChartEntity: (s: ParsedStore, chartId: string) => StoreEntity | undefined;
135
+ export declare const getDesiredOutcome: (s: ParsedStore, chartId: string) => StoreEntity | undefined;
136
+ export declare const getCurrentReality: (s: ParsedStore, chartId: string) => StoreEntity | undefined;
137
+ /**
138
+ * FLAT action steps only — `action_step` entities carrying this chart's id.
139
+ *
140
+ * Read the name literally: this is NOT "the chart's work". Only steps passed in the
141
+ * `actionSteps[]` array at chart creation become `action_step` entities. `add_action_step`
142
+ * creates a TELESCOPED CHILD CHART instead, and its step is that child's desired outcome.
143
+ * In the live seat store, 49 of 71 charts carry a parent — most work is telescoped and
144
+ * invisible here. Use `getWork` unless you specifically want the flat ones.
145
+ */
146
+ export declare function getFlatActionSteps(s: ParsedStore, chartId: string): StoreEntity[];
147
+ /**
148
+ * Charts telescoped beneath this one — full charts whose `parentChart` points back here.
149
+ */
150
+ export declare function getChildCharts(s: ParsedStore, chartId: string): StoreEntity[];
151
+ /** One unit of work on a chart, whether it is a flat step or a telescoped child. */
152
+ export interface WorkItem {
153
+ /** Entity name for a flat step; the CHILD CHART's id for a telescoped one. */
154
+ id: string;
155
+ title: string;
156
+ completed: boolean;
157
+ dueDate?: string;
158
+ updatedAt?: string;
159
+ telescoped: boolean;
160
+ /** The underlying record, for callers that need more than the summary. */
161
+ entity: StoreEntity;
162
+ }
163
+ /**
164
+ * Everything a chart is actually holding: flat action steps PLUS telescoped children,
165
+ * in one list. This is what a progress indicator or a chart view wants — counting only
166
+ * flat steps is precisely the bug that made get_chart_progress report 0/0 for charts
167
+ * full of telescoped work.
168
+ */
169
+ export declare function getWork(s: ParsedStore, chartId: string): WorkItem[];
170
+ export declare function getMmotBeats(s: ParsedStore, chartId: string): StoreEntity[];
171
+ /**
172
+ * The chart's MMOT trail, normalized. Lives on the CHART entity's metadata, not on
173
+ * current reality — the two are separate records, and a caller may write the trail
174
+ * while deliberately leaving current reality alone.
175
+ */
176
+ export declare function getMmotEvaluations(chart: StoreEntity | undefined): MmotEvaluation[];
177
+ /**
178
+ * A content-derived revision token for the WHOLE store — newest timestamp, plus the
179
+ * record counts.
180
+ *
181
+ * The counts are not decoration. Two mutations this package performs move no timestamp
182
+ * a chart-scoped scan would see: `updateActionProgress` stamps the step but not the
183
+ * chart, and `removeActionStep` stamps nothing at all. A token built only from
184
+ * `max(updatedAt)` therefore holds steady across a progress update AND across a
185
+ * deletion — so a renderer caching on it serves a removed action step forever. Folding
186
+ * in `entities.size` and `relations.length` makes a deletion move the token.
187
+ *
188
+ * Still derived from content, never from a file mtime: an mtime changes when the store
189
+ * is rewritten with identical bytes, which makes every reader refetch for nothing and
190
+ * makes a real change indistinguishable from a no-op rewrite.
191
+ */
192
+ export declare function storeRevision(s: ParsedStore): string;
193
+ /**
194
+ * Revision across a specific set of entities. Narrower than `storeRevision` and subject
195
+ * to the blind spots described there — a progress update or a deletion may not move it.
196
+ * Prefer `storeRevision` for cache keys; use this only when a per-chart token is
197
+ * genuinely required and its limits are understood.
198
+ */
199
+ export declare function revisionOf(entities: Array<StoreEntity | undefined>): string;
200
+ //# sourceMappingURL=contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../../src/narrative/contract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,274 @@
1
+ /**
2
+ * COAIA Narrative — the store read contract.
3
+ *
4
+ * WHAT THIS IS FOR
5
+ *
6
+ * This package owns the writes. Anything that renders a chart store was, until now,
7
+ * re-deriving the store's shape by hand — entity-kind strings, the `${chartId}_chart`
8
+ * naming scheme, which metadata key holds the MMOT trail. That knowledge living in two
9
+ * places is not a style problem: when it drifts, a renderer does not break, it quietly
10
+ * renders LESS. A chart holding real work looks identical to an empty one.
11
+ *
12
+ * HOW IT AVOIDS BEING THE THING IT WARNS ABOUT
13
+ *
14
+ * The first draft of this module re-implemented record classification by hand, and a
15
+ * review measured it returning 73 entities where the writer's own parser returned 76 —
16
+ * it dropped the legacy `type:"narrative_beat"` dialect and reported the losses as
17
+ * corruption. Duplication relocated rather than removed.
18
+ *
19
+ * So classification is NOT defined here. `./jsonl-records.js` holds the single
20
+ * definition, and both the writer (jsonl-preservation.ts) and this contract import it.
21
+ * What this module adds on top is the two things the writer's parser genuinely does not
22
+ * offer a renderer: per-line TOLERANCE (parseJsonlMemory throws on one bad line, which
23
+ * is correct for a writer and fatal for a surface), and the selectors/naming below.
24
+ *
25
+ * THREE RULES THIS MODULE KEEPS
26
+ *
27
+ * 1. Zero I/O. No `fs`, no `process`, no network. Callers own reading; this owns shape.
28
+ * 2. Never imports the server. The package's `main` IS the MCP bootstrap — importing the
29
+ * package root starts a stdio server — which is why this lives behind its own subpath.
30
+ * 3. Tolerant by construction, honest about what it skipped.
31
+ */
32
+ import { isEntityRecord, isLegacyNarrativeBeatRecord, isRelationRecord, normalizeLegacyNarrativeBeat, } from './jsonl-records.js';
33
+ /** Bumped when a shape changes in a way a reader must notice. */
34
+ export const CONTRACT_VERSION = 2;
35
+ // ---------------------------------------------------------------------------
36
+ // Entity kinds
37
+ // ---------------------------------------------------------------------------
38
+ /**
39
+ * Every entityType this package writes. `test-contract.js` scans this package's own
40
+ * source and fails if a kind is written that this list does not publish — so the
41
+ * contract cannot fall behind the writer without the suite going red.
42
+ */
43
+ export const ENTITY_TYPES = {
44
+ chart: 'structural_tension_chart',
45
+ desiredOutcome: 'desired_outcome',
46
+ currentReality: 'current_reality',
47
+ actionStep: 'action_step',
48
+ narrativeBeat: 'narrative_beat',
49
+ wampumBelt: 'wampum_belt',
50
+ };
51
+ /**
52
+ * MMOT phases, as the writer accepts them. `'full'` is included deliberately: it is the
53
+ * tool's DEFAULT (`phase: string = 'full'`) and appears in live stores. An earlier draft
54
+ * omitted it, which would have made a phase rail built from this list silently drop
55
+ * every default-phase evaluation.
56
+ */
57
+ export const MMOT_PHASES = ['full', 'acknowledge', 'analyze', 'update', 'recommit'];
58
+ export const isMmotPhase = (v) => typeof v === 'string' && MMOT_PHASES.includes(v);
59
+ /**
60
+ * Fritz's creating phases, carried on a chart as `metadata.phase`. Published alongside
61
+ * MMOT_PHASES because both occupy the key `phase` in different records, and a reader
62
+ * that assumes one vocabulary when it holds the other gets a confident wrong answer.
63
+ */
64
+ export const CREATING_PHASES = ['germination', 'assimilation', 'completion'];
65
+ // ---------------------------------------------------------------------------
66
+ // Naming scheme
67
+ // ---------------------------------------------------------------------------
68
+ export const chartEntityName = (chartId) => `${chartId}_chart`;
69
+ export const desiredOutcomeName = (chartId) => `${chartId}_desired_outcome`;
70
+ export const currentRealityName = (chartId) => `${chartId}_current_reality`;
71
+ /** MMOT narrative beats are named `${chartId}_mmot_${epochMs}`. */
72
+ export const mmotBeatPrefix = (chartId) => `${chartId}_mmot_`;
73
+ export const isMmotBeatName = (name, chartId) => name.startsWith(mmotBeatPrefix(chartId));
74
+ // ---------------------------------------------------------------------------
75
+ // Parsing
76
+ // ---------------------------------------------------------------------------
77
+ /**
78
+ * Parse raw JSONL store content. Pure: give it a string, get records back.
79
+ *
80
+ * Classification is delegated to the writer's own predicates, so every dialect the
81
+ * writer round-trips — including the legacy top-level `type:"narrative_beat"` form — is
82
+ * read here identically. The only behaviour added is per-line tolerance: one unparseable
83
+ * line is skipped rather than throwing away the whole store, which is what a live
84
+ * surface needs and what `parseJsonlMemory` deliberately does not do.
85
+ *
86
+ * WHAT `skipped` CANNOT TELL YOU. Since writeJsonlMemoryFile became atomic (temp +
87
+ * rename) a reader should never see a torn file at all, and `skipped > 0` now genuinely
88
+ * suggests a foreign or damaged line. But a store truncated by something OTHER than this
89
+ * package can still be a syntactically perfect PREFIX — every whole line parses, and the
90
+ * result is simply a smaller store with `skipped === 0`. No reader can detect that from
91
+ * content alone. If that matters to a caller, compare entity counts across reads.
92
+ */
93
+ export function parseStore(raw) {
94
+ const entities = new Map();
95
+ const relations = [];
96
+ let skipped = 0;
97
+ for (const line of raw.split('\n')) {
98
+ if (line.trim() === '')
99
+ continue;
100
+ let record;
101
+ try {
102
+ const parsed = JSON.parse(line);
103
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
104
+ skipped++;
105
+ continue;
106
+ }
107
+ record = parsed;
108
+ }
109
+ catch {
110
+ skipped++;
111
+ continue;
112
+ }
113
+ if (isEntityRecord(record)) {
114
+ entities.set(record.name, record);
115
+ }
116
+ else if (isRelationRecord(record)) {
117
+ relations.push(record);
118
+ }
119
+ else if (isLegacyNarrativeBeatRecord(record)) {
120
+ const normalized = normalizeLegacyNarrativeBeat(record);
121
+ entities.set(normalized.name, normalized);
122
+ }
123
+ else {
124
+ skipped++;
125
+ }
126
+ }
127
+ return { entities, relations, skipped };
128
+ }
129
+ // ---------------------------------------------------------------------------
130
+ // Selectors
131
+ // ---------------------------------------------------------------------------
132
+ const meta = (e) => (e && e.metadata) || {};
133
+ /** Read a string-valued metadata key, or undefined when absent or the wrong type. */
134
+ export function metaString(e, key) {
135
+ const v = meta(e)[key];
136
+ return typeof v === 'string' ? v : undefined;
137
+ }
138
+ /** A step or chart is complete when `metadata.completionStatus` is exactly true. */
139
+ export const isComplete = (e) => meta(e)['completionStatus'] === true;
140
+ export const getChartEntity = (s, chartId) => s.entities.get(chartEntityName(chartId));
141
+ export const getDesiredOutcome = (s, chartId) => s.entities.get(desiredOutcomeName(chartId));
142
+ export const getCurrentReality = (s, chartId) => s.entities.get(currentRealityName(chartId));
143
+ /**
144
+ * FLAT action steps only — `action_step` entities carrying this chart's id.
145
+ *
146
+ * Read the name literally: this is NOT "the chart's work". Only steps passed in the
147
+ * `actionSteps[]` array at chart creation become `action_step` entities. `add_action_step`
148
+ * creates a TELESCOPED CHILD CHART instead, and its step is that child's desired outcome.
149
+ * In the live seat store, 49 of 71 charts carry a parent — most work is telescoped and
150
+ * invisible here. Use `getWork` unless you specifically want the flat ones.
151
+ */
152
+ export function getFlatActionSteps(s, chartId) {
153
+ const out = [];
154
+ for (const e of s.entities.values()) {
155
+ if (e.entityType === ENTITY_TYPES.actionStep && meta(e)['chartId'] === chartId) {
156
+ out.push(e);
157
+ }
158
+ }
159
+ return out;
160
+ }
161
+ /**
162
+ * Charts telescoped beneath this one — full charts whose `parentChart` points back here.
163
+ */
164
+ export function getChildCharts(s, chartId) {
165
+ const out = [];
166
+ for (const e of s.entities.values()) {
167
+ if (e.entityType === ENTITY_TYPES.chart && meta(e)['parentChart'] === chartId) {
168
+ out.push(e);
169
+ }
170
+ }
171
+ return out;
172
+ }
173
+ /**
174
+ * Everything a chart is actually holding: flat action steps PLUS telescoped children,
175
+ * in one list. This is what a progress indicator or a chart view wants — counting only
176
+ * flat steps is precisely the bug that made get_chart_progress report 0/0 for charts
177
+ * full of telescoped work.
178
+ */
179
+ export function getWork(s, chartId) {
180
+ const out = [];
181
+ for (const e of getFlatActionSteps(s, chartId)) {
182
+ out.push({
183
+ id: e.name,
184
+ title: e.observations?.[0] ?? e.name,
185
+ completed: isComplete(e),
186
+ dueDate: metaString(e, 'dueDate'),
187
+ updatedAt: metaString(e, 'updatedAt'),
188
+ telescoped: false,
189
+ entity: e,
190
+ });
191
+ }
192
+ for (const e of getChildCharts(s, chartId)) {
193
+ const childId = metaString(e, 'chartId') ?? e.name.replace(/_chart$/, '');
194
+ const childOutcome = getDesiredOutcome(s, childId);
195
+ out.push({
196
+ id: childId,
197
+ title: childOutcome?.observations?.[0] ?? childId,
198
+ completed: isComplete(e),
199
+ dueDate: metaString(e, 'dueDate'),
200
+ updatedAt: metaString(e, 'updatedAt'),
201
+ telescoped: true,
202
+ entity: e,
203
+ });
204
+ }
205
+ return out;
206
+ }
207
+ export function getMmotBeats(s, chartId) {
208
+ const out = [];
209
+ for (const e of s.entities.values()) {
210
+ if (e.entityType === ENTITY_TYPES.narrativeBeat && isMmotBeatName(e.name, chartId)) {
211
+ out.push(e);
212
+ }
213
+ }
214
+ return out;
215
+ }
216
+ /**
217
+ * The chart's MMOT trail, normalized. Lives on the CHART entity's metadata, not on
218
+ * current reality — the two are separate records, and a caller may write the trail
219
+ * while deliberately leaving current reality alone.
220
+ */
221
+ export function getMmotEvaluations(chart) {
222
+ const raw = meta(chart)['mmotEvaluations'];
223
+ if (!Array.isArray(raw))
224
+ return [];
225
+ return raw.map((ev) => {
226
+ const r = (ev || {});
227
+ return {
228
+ phase: String(r['phase'] ?? ''),
229
+ assessment: String(r['assessment'] ?? ''),
230
+ direction: typeof r['direction'] === 'string' ? r['direction'] : undefined,
231
+ timestamp: typeof r['timestamp'] === 'string' ? r['timestamp'] : undefined,
232
+ };
233
+ });
234
+ }
235
+ /**
236
+ * A content-derived revision token for the WHOLE store — newest timestamp, plus the
237
+ * record counts.
238
+ *
239
+ * The counts are not decoration. Two mutations this package performs move no timestamp
240
+ * a chart-scoped scan would see: `updateActionProgress` stamps the step but not the
241
+ * chart, and `removeActionStep` stamps nothing at all. A token built only from
242
+ * `max(updatedAt)` therefore holds steady across a progress update AND across a
243
+ * deletion — so a renderer caching on it serves a removed action step forever. Folding
244
+ * in `entities.size` and `relations.length` makes a deletion move the token.
245
+ *
246
+ * Still derived from content, never from a file mtime: an mtime changes when the store
247
+ * is rewritten with identical bytes, which makes every reader refetch for nothing and
248
+ * makes a real change indistinguishable from a no-op rewrite.
249
+ */
250
+ export function storeRevision(s) {
251
+ let newest = '';
252
+ for (const e of s.entities.values()) {
253
+ const u = metaString(e, 'updatedAt') ?? metaString(e, 'createdAt') ?? '';
254
+ if (u > newest)
255
+ newest = u;
256
+ }
257
+ return `${newest}|${s.entities.size}|${s.relations.length}`;
258
+ }
259
+ /**
260
+ * Revision across a specific set of entities. Narrower than `storeRevision` and subject
261
+ * to the blind spots described there — a progress update or a deletion may not move it.
262
+ * Prefer `storeRevision` for cache keys; use this only when a per-chart token is
263
+ * genuinely required and its limits are understood.
264
+ */
265
+ export function revisionOf(entities) {
266
+ let revision = '';
267
+ for (const e of entities) {
268
+ const u = metaString(e, 'updatedAt') ?? metaString(e, 'createdAt') ?? '';
269
+ if (u > revision)
270
+ revision = u;
271
+ }
272
+ return revision;
273
+ }
274
+ //# sourceMappingURL=contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.js","sourceRoot":"","sources":["../../../src/narrative/contract.ts"],"names":[],"mappings":"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"}