@miadi/inquiry-weave 0.8.2 → 0.9.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 +14 -0
- package/dist/attention.d.ts +75 -0
- package/dist/attention.d.ts.map +1 -0
- package/dist/attention.js +356 -0
- package/dist/attention.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -2
- package/dist/cli.js.map +1 -1
- package/dist/closing.d.ts +60 -0
- package/dist/closing.d.ts.map +1 -0
- package/dist/closing.js +199 -0
- package/dist/closing.js.map +1 -0
- package/dist/episode.d.ts +2 -0
- package/dist/episode.d.ts.map +1 -1
- package/dist/episode.js +19 -2
- package/dist/episode.js.map +1 -1
- package/dist/git-landing.d.ts +119 -0
- package/dist/git-landing.d.ts.map +1 -0
- package/dist/git-landing.js +280 -0
- package/dist/git-landing.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.d.ts +13 -3
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +326 -4
- package/dist/mcp-server.js.map +1 -1
- package/dist/mint.d.ts +243 -0
- package/dist/mint.d.ts.map +1 -0
- package/dist/mint.js +743 -0
- package/dist/mint.js.map +1 -0
- package/dist/skill.js +1 -1
- package/package.json +1 -1
- package/rispecs/README.rise.md +5 -0
- package/rispecs/RISPEC.md +9 -0
package/README.md
CHANGED
|
@@ -176,6 +176,20 @@ The artefact owns `.weave.yaml`; the episode owns `inquiry/weave.yaml`, which
|
|
|
176
176
|
also records sync hashes and counts. These files are authoritative. The
|
|
177
177
|
medicine wheel is their registered projection.
|
|
178
178
|
|
|
179
|
+
## Chronicle attention through MCP
|
|
180
|
+
|
|
181
|
+
`inquiry-weave-mcp` exposes the same ATTENTION read/answer service used by the
|
|
182
|
+
Miadi API and `passages attention answer`:
|
|
183
|
+
|
|
184
|
+
- `chronicle_attention_list` — open items across the Chronicle, optionally one episode;
|
|
185
|
+
- `chronicle_attention_get` — one item plus safe excerpts from episode-local `depth[]` references;
|
|
186
|
+
- `chronicle_attention_answer` — one explicit answer, atomically written and projected.
|
|
187
|
+
|
|
188
|
+
Set `MIADI_CHRONICLE_ROOT`. Set `MIADI_CHRONICLE_MW_URL` (or `MW_API_URL`) to
|
|
189
|
+
the one Chronicle wheel. With no wheel URL, an answer still lands in the
|
|
190
|
+
authoritative file and the tool returns `pending`; it never falls back to a
|
|
191
|
+
repo-local wheel. See [jgwill/Miadi#631](https://github.com/jgwill/Miadi/issues/631).
|
|
192
|
+
|
|
179
193
|
## Episode lineage
|
|
180
194
|
|
|
181
195
|
```bash
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The read/write half of Chronicle ATTENTION.
|
|
3
|
+
*
|
|
4
|
+
* `attention.json` remains authoritative. This module gives the CLI, app API,
|
|
5
|
+
* and MCP one transaction for answering an item: resolve the episode, refuse
|
|
6
|
+
* malformed or stale state, atomically write the answer, project that exact
|
|
7
|
+
* item to the Chronicle wheel, and leave a truthful receipt. The wheel leg is
|
|
8
|
+
* fail-open by design; a human answer is never rolled back because projection
|
|
9
|
+
* is temporarily unavailable.
|
|
10
|
+
*
|
|
11
|
+
* Ref: jgwill/Miadi#631
|
|
12
|
+
*/
|
|
13
|
+
import { ATTENTION_FILE, type AttentionItem } from "@miadi/episodic-memory-schema";
|
|
14
|
+
import { type AttentionNodeOutcome, type RegisterAttentionNodeOptions } from "./attention-node.js";
|
|
15
|
+
export { ATTENTION_FILE };
|
|
16
|
+
export declare const ATTENTION_RECEIPT_FILE = ".mw-attention.json";
|
|
17
|
+
export declare const ATTENTION_MAX_BYTES = 200000;
|
|
18
|
+
export declare const ATTENTION_ANSWER_MAX_CHARS = 10000;
|
|
19
|
+
export type AttentionRegistration = "registered" | "pending" | "unregistered";
|
|
20
|
+
export interface AttentionContext {
|
|
21
|
+
ref: string;
|
|
22
|
+
file: string;
|
|
23
|
+
fragment?: string;
|
|
24
|
+
exists: boolean;
|
|
25
|
+
excerpt?: string;
|
|
26
|
+
error?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface ChronicleAttentionItem extends AttentionItem {
|
|
29
|
+
episode: string;
|
|
30
|
+
registration: AttentionRegistration;
|
|
31
|
+
context?: AttentionContext[];
|
|
32
|
+
}
|
|
33
|
+
export interface EpisodeAttention {
|
|
34
|
+
episode: string;
|
|
35
|
+
items: ChronicleAttentionItem[];
|
|
36
|
+
parseError?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface ReadAttentionOptions {
|
|
39
|
+
includeContext?: boolean;
|
|
40
|
+
}
|
|
41
|
+
export interface ListAttentionOptions extends ReadAttentionOptions {
|
|
42
|
+
episode?: string;
|
|
43
|
+
state?: "open" | "answered" | "all";
|
|
44
|
+
}
|
|
45
|
+
export type AttentionServiceErrorCode = "invalid_answer" | "store_not_found" | "malformed_store" | "item_not_found" | "already_answered" | "stale_state" | "busy";
|
|
46
|
+
export declare class AttentionServiceError extends Error {
|
|
47
|
+
readonly code: AttentionServiceErrorCode;
|
|
48
|
+
constructor(code: AttentionServiceErrorCode, message: string);
|
|
49
|
+
}
|
|
50
|
+
/** Resolve one depth reference inside its episode. Never follows an escape. */
|
|
51
|
+
export declare function readAttentionContext(episodeDir: string, ref: string): Promise<AttentionContext>;
|
|
52
|
+
/** Read one episode's store and its truthful wheel registration state. */
|
|
53
|
+
export declare function readEpisodeAttention(chronicleRoot: string, episodeRef: string, options?: ReadAttentionOptions): Promise<EpisodeAttention | null>;
|
|
54
|
+
/** List attention across the Chronicle, newest-asked first. */
|
|
55
|
+
export declare function listChronicleAttention(chronicleRoot: string, options?: ListAttentionOptions): Promise<ChronicleAttentionItem[]>;
|
|
56
|
+
/** Get one stable item identity, optionally with episode-local context. */
|
|
57
|
+
export declare function getChronicleAttention(chronicleRoot: string, episodeRef: string, itemId: string, options?: ReadAttentionOptions): Promise<ChronicleAttentionItem>;
|
|
58
|
+
export interface AnswerAttentionOptions extends RegisterAttentionNodeOptions {
|
|
59
|
+
mwUrl?: string;
|
|
60
|
+
now?: Date;
|
|
61
|
+
expectedState?: "open" | "answered";
|
|
62
|
+
}
|
|
63
|
+
export interface AnswerAttentionResult {
|
|
64
|
+
episode: string;
|
|
65
|
+
item: ChronicleAttentionItem;
|
|
66
|
+
projection: AttentionNodeOutcome;
|
|
67
|
+
synchronized: boolean;
|
|
68
|
+
receipt: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Record one explicit answer. The lock makes the open→answered guard real for
|
|
72
|
+
* concurrent app/MCP/CLI callers; no caller can silently overwrite another.
|
|
73
|
+
*/
|
|
74
|
+
export declare function answerChronicleAttention(chronicleRoot: string, episodeRef: string, itemId: string, answerText: string, options?: AnswerAttentionOptions): Promise<AnswerAttentionResult>;
|
|
75
|
+
//# sourceMappingURL=attention.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attention.d.ts","sourceRoot":"","sources":["../src/attention.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EACL,cAAc,EAGd,KAAK,aAAa,EACnB,MAAM,+BAA+B,CAAA;AAItC,OAAO,EAGL,KAAK,oBAAoB,EACzB,KAAK,4BAA4B,EAClC,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAAE,cAAc,EAAE,CAAA;AAEzB,eAAO,MAAM,sBAAsB,uBAAuB,CAAA;AAC1D,eAAO,MAAM,mBAAmB,SAAU,CAAA;AAC1C,eAAO,MAAM,0BAA0B,QAAS,CAAA;AAEhD,MAAM,MAAM,qBAAqB,GAAG,YAAY,GAAG,SAAS,GAAG,cAAc,CAAA;AAE7E,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,OAAO,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,sBAAuB,SAAQ,aAAa;IAC3D,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,EAAE,qBAAqB,CAAA;IACnC,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAA;CAC7B;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,sBAAsB,EAAE,CAAA;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;AAED,MAAM,WAAW,oBAAqB,SAAQ,oBAAoB;IAChE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,KAAK,CAAA;CACpC;AAED,MAAM,MAAM,yBAAyB,GACjC,gBAAgB,GAChB,iBAAiB,GACjB,iBAAiB,GACjB,gBAAgB,GAChB,kBAAkB,GAClB,aAAa,GACb,MAAM,CAAA;AAEV,qBAAa,qBAAsB,SAAQ,KAAK;aAE5B,IAAI,EAAE,yBAAyB;gBAA/B,IAAI,EAAE,yBAAyB,EAC/C,OAAO,EAAE,MAAM;CAKlB;AA0FD,+EAA+E;AAC/E,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,gBAAgB,CAAC,CAyC3B;AAED,0EAA0E;AAC1E,wBAAsB,oBAAoB,CACxC,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAiClC;AAED,+DAA+D;AAC/D,wBAAsB,sBAAsB,CAC1C,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAuBnC;AAED,2EAA2E;AAC3E,wBAAsB,qBAAqB,CACzC,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,sBAAsB,CAAC,CAajC;AAED,MAAM,WAAW,sBAAuB,SAAQ,4BAA4B;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,GAAG,CAAC,EAAE,IAAI,CAAA;IACV,aAAa,CAAC,EAAE,MAAM,GAAG,UAAU,CAAA;CACpC;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,sBAAsB,CAAA;IAC5B,UAAU,EAAE,oBAAoB,CAAA;IAChC,YAAY,EAAE,OAAO,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;CAChB;AAsCD;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,qBAAqB,CAAC,CA4GhC"}
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The read/write half of Chronicle ATTENTION.
|
|
3
|
+
*
|
|
4
|
+
* `attention.json` remains authoritative. This module gives the CLI, app API,
|
|
5
|
+
* and MCP one transaction for answering an item: resolve the episode, refuse
|
|
6
|
+
* malformed or stale state, atomically write the answer, project that exact
|
|
7
|
+
* item to the Chronicle wheel, and leave a truthful receipt. The wheel leg is
|
|
8
|
+
* fail-open by design; a human answer is never rolled back because projection
|
|
9
|
+
* is temporarily unavailable.
|
|
10
|
+
*
|
|
11
|
+
* Ref: jgwill/Miadi#631
|
|
12
|
+
*/
|
|
13
|
+
import { ATTENTION_FILE, parseAttentionStore, serializeAttentionStore, } from "@miadi/episodic-memory-schema";
|
|
14
|
+
import { open, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
15
|
+
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
16
|
+
import { resolveEpisode } from "./episode.js";
|
|
17
|
+
import { attentionNodeId, syncEpisodeAttention, } from "./attention-node.js";
|
|
18
|
+
export { ATTENTION_FILE };
|
|
19
|
+
export const ATTENTION_RECEIPT_FILE = ".mw-attention.json";
|
|
20
|
+
export const ATTENTION_MAX_BYTES = 200_000;
|
|
21
|
+
export const ATTENTION_ANSWER_MAX_CHARS = 10_000;
|
|
22
|
+
export class AttentionServiceError extends Error {
|
|
23
|
+
code;
|
|
24
|
+
constructor(code, message) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.code = code;
|
|
27
|
+
this.name = "AttentionServiceError";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function isNodeError(error, code) {
|
|
31
|
+
return Boolean(error && typeof error === "object" && "code" in error && error.code === code);
|
|
32
|
+
}
|
|
33
|
+
async function readReceipt(episodeDir) {
|
|
34
|
+
const file = join(episodeDir, ATTENTION_RECEIPT_FILE);
|
|
35
|
+
try {
|
|
36
|
+
const fileStat = await stat(file);
|
|
37
|
+
if (!fileStat.isFile() || fileStat.size > ATTENTION_MAX_BYTES)
|
|
38
|
+
return null;
|
|
39
|
+
return {
|
|
40
|
+
receipt: JSON.parse(await readFile(file, "utf8")),
|
|
41
|
+
mtimeMs: fileStat.mtimeMs,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function registrationFor(item, fileMtimeMs, receiptRead) {
|
|
49
|
+
if (!receiptRead)
|
|
50
|
+
return "unregistered";
|
|
51
|
+
const entry = receiptRead.receipt.items?.[item.id];
|
|
52
|
+
if (!entry)
|
|
53
|
+
return "unregistered";
|
|
54
|
+
if (entry.state !== "registered" || !entry.verified)
|
|
55
|
+
return "pending";
|
|
56
|
+
if (fileMtimeMs > receiptRead.mtimeMs + 2000)
|
|
57
|
+
return "pending";
|
|
58
|
+
return "registered";
|
|
59
|
+
}
|
|
60
|
+
function splitDepthRef(ref) {
|
|
61
|
+
const at = ref.indexOf("#");
|
|
62
|
+
if (at < 0)
|
|
63
|
+
return { file: ref };
|
|
64
|
+
const fragment = ref.slice(at + 1).trim();
|
|
65
|
+
return { file: ref.slice(0, at), fragment: fragment || undefined };
|
|
66
|
+
}
|
|
67
|
+
function normaliseAnchor(value) {
|
|
68
|
+
return value
|
|
69
|
+
.trim()
|
|
70
|
+
.toLowerCase()
|
|
71
|
+
.replace(/^#+\s*/, "")
|
|
72
|
+
.replace(/["'`]/g, "")
|
|
73
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
74
|
+
.replace(/^-+|-+$/g, "");
|
|
75
|
+
}
|
|
76
|
+
function contextExcerpt(content, fragment) {
|
|
77
|
+
const lines = content.split(/\r?\n/);
|
|
78
|
+
if (!fragment)
|
|
79
|
+
return lines.slice(0, 24).join("\n").slice(0, 6000);
|
|
80
|
+
let decoded = fragment;
|
|
81
|
+
try {
|
|
82
|
+
decoded = decodeURIComponent(fragment);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// A literal fragment is still useful search text.
|
|
86
|
+
}
|
|
87
|
+
const wanted = normaliseAnchor(decoded);
|
|
88
|
+
let found = lines.findIndex((line) => normaliseAnchor(line) === wanted);
|
|
89
|
+
if (found < 0) {
|
|
90
|
+
found = lines.findIndex((line) => normaliseAnchor(line).includes(wanted));
|
|
91
|
+
}
|
|
92
|
+
if (found < 0) {
|
|
93
|
+
found = lines.findIndex((line) => line.toLowerCase().includes(decoded.toLowerCase()));
|
|
94
|
+
}
|
|
95
|
+
if (found < 0)
|
|
96
|
+
return lines.slice(0, 24).join("\n").slice(0, 6000);
|
|
97
|
+
return lines.slice(Math.max(0, found - 3), Math.min(lines.length, found + 18)).join("\n").slice(0, 6000);
|
|
98
|
+
}
|
|
99
|
+
/** Resolve one depth reference inside its episode. Never follows an escape. */
|
|
100
|
+
export async function readAttentionContext(episodeDir, ref) {
|
|
101
|
+
const parsed = splitDepthRef(ref);
|
|
102
|
+
const filePart = parsed.file.trim();
|
|
103
|
+
if (!filePart || isAbsolute(filePart)) {
|
|
104
|
+
return { ref, file: filePart, fragment: parsed.fragment, exists: false, error: "depth path must be episode-relative" };
|
|
105
|
+
}
|
|
106
|
+
const target = resolve(episodeDir, filePart);
|
|
107
|
+
const rel = relative(episodeDir, target);
|
|
108
|
+
if (rel === ".." || rel.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || isAbsolute(rel)) {
|
|
109
|
+
return { ref, file: filePart, fragment: parsed.fragment, exists: false, error: "depth path leaves the episode" };
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
const fileStat = await stat(target);
|
|
113
|
+
if (!fileStat.isFile()) {
|
|
114
|
+
return { ref, file: filePart, fragment: parsed.fragment, exists: false, error: "depth target is not a file" };
|
|
115
|
+
}
|
|
116
|
+
if (fileStat.size > ATTENTION_MAX_BYTES) {
|
|
117
|
+
return { ref, file: filePart, fragment: parsed.fragment, exists: true, error: "depth target is too large to preview" };
|
|
118
|
+
}
|
|
119
|
+
const content = await readFile(target, "utf8");
|
|
120
|
+
return {
|
|
121
|
+
ref,
|
|
122
|
+
file: filePart,
|
|
123
|
+
fragment: parsed.fragment,
|
|
124
|
+
exists: true,
|
|
125
|
+
excerpt: contextExcerpt(content, parsed.fragment),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
if (isNodeError(error, "ENOENT")) {
|
|
130
|
+
return { ref, file: filePart, fragment: parsed.fragment, exists: false, error: "depth target does not exist" };
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
ref,
|
|
134
|
+
file: filePart,
|
|
135
|
+
fragment: parsed.fragment,
|
|
136
|
+
exists: false,
|
|
137
|
+
error: error instanceof Error ? error.message : String(error),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/** Read one episode's store and its truthful wheel registration state. */
|
|
142
|
+
export async function readEpisodeAttention(chronicleRoot, episodeRef, options = {}) {
|
|
143
|
+
const episode = await resolveEpisode(chronicleRoot, episodeRef);
|
|
144
|
+
const file = join(episode.dir, ATTENTION_FILE);
|
|
145
|
+
let raw;
|
|
146
|
+
let fileMtimeMs;
|
|
147
|
+
try {
|
|
148
|
+
const fileStat = await stat(file);
|
|
149
|
+
if (!fileStat.isFile())
|
|
150
|
+
return null;
|
|
151
|
+
if (fileStat.size > ATTENTION_MAX_BYTES) {
|
|
152
|
+
return { episode: episode.name, items: [], parseError: `${ATTENTION_FILE} exceeds ${ATTENTION_MAX_BYTES} bytes` };
|
|
153
|
+
}
|
|
154
|
+
fileMtimeMs = fileStat.mtimeMs;
|
|
155
|
+
raw = await readFile(file, "utf8");
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
if (isNodeError(error, "ENOENT"))
|
|
159
|
+
return null;
|
|
160
|
+
throw error;
|
|
161
|
+
}
|
|
162
|
+
const parsed = parseAttentionStore(raw);
|
|
163
|
+
if (parsed.parseError)
|
|
164
|
+
return { episode: episode.name, items: [], parseError: parsed.parseError };
|
|
165
|
+
const receipt = await readReceipt(episode.dir);
|
|
166
|
+
const items = await Promise.all(parsed.items.map(async (item) => ({
|
|
167
|
+
...item,
|
|
168
|
+
episode: episode.name,
|
|
169
|
+
registration: registrationFor(item, fileMtimeMs, receipt),
|
|
170
|
+
context: options.includeContext && item.depth
|
|
171
|
+
? await Promise.all(item.depth.map((depth) => readAttentionContext(episode.dir, depth)))
|
|
172
|
+
: undefined,
|
|
173
|
+
})));
|
|
174
|
+
return { episode: episode.name, items };
|
|
175
|
+
}
|
|
176
|
+
/** List attention across the Chronicle, newest-asked first. */
|
|
177
|
+
export async function listChronicleAttention(chronicleRoot, options = {}) {
|
|
178
|
+
const state = options.state ?? "open";
|
|
179
|
+
const episodes = options.episode
|
|
180
|
+
? [options.episode]
|
|
181
|
+
: (await readdir(chronicleRoot, { withFileTypes: true }))
|
|
182
|
+
.filter((entry) => entry.isDirectory() || entry.isSymbolicLink())
|
|
183
|
+
.map((entry) => entry.name);
|
|
184
|
+
const out = [];
|
|
185
|
+
for (const episode of episodes) {
|
|
186
|
+
let attention;
|
|
187
|
+
try {
|
|
188
|
+
attention = await readEpisodeAttention(chronicleRoot, episode, options);
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (!attention || attention.parseError)
|
|
194
|
+
continue;
|
|
195
|
+
for (const item of attention.items) {
|
|
196
|
+
if (state === "all" || item.state === state)
|
|
197
|
+
out.push(item);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
out.sort((a, b) => (b.asked ?? "").localeCompare(a.asked ?? ""));
|
|
201
|
+
return out;
|
|
202
|
+
}
|
|
203
|
+
/** Get one stable item identity, optionally with episode-local context. */
|
|
204
|
+
export async function getChronicleAttention(chronicleRoot, episodeRef, itemId, options = {}) {
|
|
205
|
+
const attention = await readEpisodeAttention(chronicleRoot, episodeRef, options);
|
|
206
|
+
if (!attention) {
|
|
207
|
+
throw new AttentionServiceError("store_not_found", `episode ${episodeRef} has no ${ATTENTION_FILE}`);
|
|
208
|
+
}
|
|
209
|
+
if (attention.parseError) {
|
|
210
|
+
throw new AttentionServiceError("malformed_store", `${ATTENTION_FILE} is malformed: ${attention.parseError}`);
|
|
211
|
+
}
|
|
212
|
+
const item = attention.items.find((candidate) => candidate.id === itemId);
|
|
213
|
+
if (!item) {
|
|
214
|
+
throw new AttentionServiceError("item_not_found", `attention item '${itemId}' was not found in ${attention.episode}`);
|
|
215
|
+
}
|
|
216
|
+
return item;
|
|
217
|
+
}
|
|
218
|
+
async function writeReceipt(episodeDir, mwUrl, itemId, outcome, now) {
|
|
219
|
+
const file = join(episodeDir, ATTENTION_RECEIPT_FILE);
|
|
220
|
+
let prior = {};
|
|
221
|
+
try {
|
|
222
|
+
prior = JSON.parse(await readFile(file, "utf8"));
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
// First receipt, or unreadable prior: replace it with a truthful one.
|
|
226
|
+
}
|
|
227
|
+
const synchronized = outcome.state !== "pending" && outcome.verified;
|
|
228
|
+
const receipt = {
|
|
229
|
+
...prior,
|
|
230
|
+
mw_url: mwUrl,
|
|
231
|
+
synced_at: now.toISOString(),
|
|
232
|
+
items: {
|
|
233
|
+
...(prior.items ?? {}),
|
|
234
|
+
[itemId]: {
|
|
235
|
+
node_id: outcome.node_id,
|
|
236
|
+
state: outcome.state === "pending" ? "pending" : "registered",
|
|
237
|
+
verified: outcome.verified,
|
|
238
|
+
verified_at: synchronized ? now.toISOString() : undefined,
|
|
239
|
+
error: outcome.error,
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
const tmp = `${file}.tmp-${process.pid}-${now.getTime()}`;
|
|
244
|
+
await writeFile(tmp, JSON.stringify(receipt, null, 2) + "\n", "utf8");
|
|
245
|
+
await rename(tmp, file);
|
|
246
|
+
return file;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Record one explicit answer. The lock makes the open→answered guard real for
|
|
250
|
+
* concurrent app/MCP/CLI callers; no caller can silently overwrite another.
|
|
251
|
+
*/
|
|
252
|
+
export async function answerChronicleAttention(chronicleRoot, episodeRef, itemId, answerText, options = {}) {
|
|
253
|
+
const answer = answerText.trim();
|
|
254
|
+
if (!answer)
|
|
255
|
+
throw new AttentionServiceError("invalid_answer", "answer must not be empty");
|
|
256
|
+
if (answer.length > ATTENTION_ANSWER_MAX_CHARS) {
|
|
257
|
+
throw new AttentionServiceError("invalid_answer", `answer exceeds ${ATTENTION_ANSWER_MAX_CHARS} characters`);
|
|
258
|
+
}
|
|
259
|
+
const episode = await resolveEpisode(chronicleRoot, episodeRef);
|
|
260
|
+
const file = join(episode.dir, ATTENTION_FILE);
|
|
261
|
+
const lockFile = `${file}.lock`;
|
|
262
|
+
let lock;
|
|
263
|
+
try {
|
|
264
|
+
lock = await open(lockFile, "wx");
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
if (isNodeError(error, "EEXIST")) {
|
|
268
|
+
throw new AttentionServiceError("busy", `${ATTENTION_FILE} is being updated; retry the answer`);
|
|
269
|
+
}
|
|
270
|
+
throw error;
|
|
271
|
+
}
|
|
272
|
+
let item;
|
|
273
|
+
const now = options.now ?? new Date();
|
|
274
|
+
try {
|
|
275
|
+
let raw;
|
|
276
|
+
let fileMode;
|
|
277
|
+
try {
|
|
278
|
+
const fileStat = await stat(file);
|
|
279
|
+
if (!fileStat.isFile())
|
|
280
|
+
throw new AttentionServiceError("store_not_found", `${file} is not a file`);
|
|
281
|
+
if (fileStat.size > ATTENTION_MAX_BYTES) {
|
|
282
|
+
throw new AttentionServiceError("malformed_store", `${ATTENTION_FILE} exceeds ${ATTENTION_MAX_BYTES} bytes`);
|
|
283
|
+
}
|
|
284
|
+
fileMode = fileStat.mode;
|
|
285
|
+
raw = await readFile(file, "utf8");
|
|
286
|
+
}
|
|
287
|
+
catch (error) {
|
|
288
|
+
if (isNodeError(error, "ENOENT")) {
|
|
289
|
+
throw new AttentionServiceError("store_not_found", `episode ${episode.name} has no ${ATTENTION_FILE}`);
|
|
290
|
+
}
|
|
291
|
+
throw error;
|
|
292
|
+
}
|
|
293
|
+
const parsed = parseAttentionStore(raw);
|
|
294
|
+
if (parsed.parseError) {
|
|
295
|
+
throw new AttentionServiceError("malformed_store", `${ATTENTION_FILE} is malformed: ${parsed.parseError}`);
|
|
296
|
+
}
|
|
297
|
+
const index = parsed.items.findIndex((candidate) => candidate.id === itemId);
|
|
298
|
+
if (index < 0) {
|
|
299
|
+
throw new AttentionServiceError("item_not_found", `attention item '${itemId}' was not found in ${episode.name}`);
|
|
300
|
+
}
|
|
301
|
+
const current = parsed.items[index];
|
|
302
|
+
const expected = options.expectedState ?? "open";
|
|
303
|
+
if (current.state !== expected) {
|
|
304
|
+
if (current.state === "answered") {
|
|
305
|
+
throw new AttentionServiceError("already_answered", `attention item '${itemId}' is already answered`);
|
|
306
|
+
}
|
|
307
|
+
throw new AttentionServiceError("stale_state", `attention item '${itemId}' is ${current.state}; caller expected ${expected}`);
|
|
308
|
+
}
|
|
309
|
+
if (current.state !== "open") {
|
|
310
|
+
throw new AttentionServiceError("already_answered", `attention item '${itemId}' is already answered`);
|
|
311
|
+
}
|
|
312
|
+
item = {
|
|
313
|
+
...current,
|
|
314
|
+
state: "answered",
|
|
315
|
+
answer,
|
|
316
|
+
answered_at: now.toISOString().slice(0, 10),
|
|
317
|
+
};
|
|
318
|
+
parsed.items[index] = item;
|
|
319
|
+
const tmp = `${file}.tmp-${process.pid}-${now.getTime()}`;
|
|
320
|
+
try {
|
|
321
|
+
await writeFile(tmp, serializeAttentionStore(parsed.items), { encoding: "utf8", mode: fileMode });
|
|
322
|
+
await rename(tmp, file);
|
|
323
|
+
}
|
|
324
|
+
catch (error) {
|
|
325
|
+
await rm(tmp, { force: true });
|
|
326
|
+
throw error;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
finally {
|
|
330
|
+
await lock.close();
|
|
331
|
+
await rm(lockFile, { force: true });
|
|
332
|
+
}
|
|
333
|
+
const mwUrl = (options.mwUrl ?? "").replace(/\/+$/, "");
|
|
334
|
+
const projection = mwUrl
|
|
335
|
+
? (await syncEpisodeAttention(mwUrl, episode.name, [item], { fetchImpl: options.fetchImpl })).outcomes[0]
|
|
336
|
+
: {
|
|
337
|
+
state: "pending",
|
|
338
|
+
node_id: attentionNodeId(episode.name, item.id),
|
|
339
|
+
verified: false,
|
|
340
|
+
error: "no Chronicle medicine-wheel URL configured",
|
|
341
|
+
};
|
|
342
|
+
const receipt = await writeReceipt(episode.dir, mwUrl, item.id, projection, now);
|
|
343
|
+
const synchronized = projection.state !== "pending" && projection.verified;
|
|
344
|
+
return {
|
|
345
|
+
episode: episode.name,
|
|
346
|
+
item: {
|
|
347
|
+
...item,
|
|
348
|
+
episode: episode.name,
|
|
349
|
+
registration: synchronized ? "registered" : "pending",
|
|
350
|
+
},
|
|
351
|
+
projection,
|
|
352
|
+
synchronized,
|
|
353
|
+
receipt: relative(chronicleRoot, receipt),
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
//# sourceMappingURL=attention.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attention.js","sourceRoot":"","sources":["../src/attention.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAaH,OAAO,
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAaH,OAAO,EAIL,gBAAgB,EAIjB,MAAM,cAAc,CAAA;AAuCrB,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE;QAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAA;IACzC,MAAM,EAAE;QAAE,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAA;CAC1C;AAED,MAAM,WAAW,aAAc,SAAQ,OAAO,CAAC,UAAU,CAAC;IACxD,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;IACvB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CACjB;AAq/BD,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAkEzF;AAED,OAAO,EAAE,gBAAgB,EAAE,CAAA"}
|
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import { readFile, writeFile } from "node:fs/promises";
|
|
|
7
7
|
import { isAbsolute, join, resolve } from "node:path";
|
|
8
8
|
import { resolveEnv } from "./env.js";
|
|
9
9
|
import { ArtefactResolutionError, buildArtefactName, ensureEpisodePrefix, resolveArtefact, scaffoldVessel, } from "./artefact.js";
|
|
10
|
-
import { AmbiguousEpisodeError, EpisodeResolutionError, parseEpisodeName, resolveEpisode, scaffoldEpisode, } from "./episode.js";
|
|
10
|
+
import { AmbiguousEpisodeError, anchorScaffoldIssue, EpisodeResolutionError, parseEpisodeName, resolveEpisode, scaffoldEpisode, } from "./episode.js";
|
|
11
11
|
import { createIssue, IssueCreationError } from "./issue.js";
|
|
12
12
|
import { issueBodyFromArtefact } from "./issue-content.js";
|
|
13
13
|
import { buildChronicleCatalog } from "./catalog.js";
|
|
@@ -316,6 +316,9 @@ async function commandInquire(context) {
|
|
|
316
316
|
now: context.now,
|
|
317
317
|
dryRun,
|
|
318
318
|
});
|
|
319
|
+
const issueAnchored = newEpisodeSlug !== undefined && !dryRun && result.issue !== undefined
|
|
320
|
+
? await anchorScaffoldIssue(episode.dir, result.issue)
|
|
321
|
+
: false;
|
|
319
322
|
const synced = newEpisodeSlug !== undefined
|
|
320
323
|
? await syncArtefact(artefact.dir, episode.dir, {
|
|
321
324
|
dryRun,
|
|
@@ -340,7 +343,7 @@ async function commandInquire(context) {
|
|
|
340
343
|
url: context.env.mwUrl,
|
|
341
344
|
}, null, 2)}\n`, "utf8");
|
|
342
345
|
}
|
|
343
|
-
emit(context, { vessel: artefact, related: result, synced, episodeRegistration }, [
|
|
346
|
+
emit(context, { vessel: artefact, related: result, issueAnchored, synced, episodeRegistration }, [
|
|
344
347
|
...(newEpisodeSlug !== undefined
|
|
345
348
|
? [`${dryRun ? "would scaffold" : "scaffolded"} episode: ${episode.dir}`]
|
|
346
349
|
: []),
|
|
@@ -352,6 +355,7 @@ async function commandInquire(context) {
|
|
|
352
355
|
: `artefact: ${artefact.dir}`,
|
|
353
356
|
...(createdVessel ? [" (empty by design — the inquiry's content is yours to write)"] : []),
|
|
354
357
|
...relateLines(result, "woven"),
|
|
358
|
+
...(issueAnchored ? [`episode issue anchor: ${result.issue}`] : []),
|
|
355
359
|
...(synced ? syncLines(synced) : []),
|
|
356
360
|
...(episodeRegistration
|
|
357
361
|
? [`episode registration: ${episodeRegistration.state} (${episodeRegistration.node_id})`]
|