@afu_context/afu-context 0.0.0-stage → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ /** Keep the host prefix unchanged; coverage instructions are appended at its end. */
2
+ export function summaryPrefix(entries, ids, prompt) {
3
+ if (!ids?.length)
4
+ return { messages: [], prompt }; // artifact-only consolidation
5
+ const start = entries.findIndex(entry => entry.id === ids[0]);
6
+ if (start < 0 || ids.some((id, i) => entries[start + i]?.id !== id))
7
+ throw new Error("Summary source no longer matches host history");
8
+ const before = entries.slice(0, start).flatMap(e => [...e.messages]).length;
9
+ const messages = entries.slice(0, start + ids.length).flatMap(e => [...e.messages]);
10
+ return { messages, prompt: before === 0 ? prompt : `${prompt}\nSource scope: use only messages ${before + 1} through ${messages.length} (one-based) as new source, together with any previous summary supplied in this request. Earlier messages are context only; do not import additional facts from them. Return one replacement summary.` };
11
+ }
12
+ //# sourceMappingURL=prefix.js.map
@@ -0,0 +1,79 @@
1
+ export function findAppliedArtifact(result) {
2
+ for (const item of result.items) {
3
+ const replaced = item.metadata?.replaced_item_ids;
4
+ if (item.kind !== "artifact" || !Array.isArray(replaced))
5
+ continue;
6
+ const replacedItemIds = replaced.filter((value) => typeof value === "string");
7
+ if (replacedItemIds.length === 0)
8
+ continue;
9
+ const artifactId = item.metadata?.artifact_id;
10
+ return {
11
+ item,
12
+ replacedItemIds,
13
+ ...(typeof artifactId === "string" ? { artifactId } : {}),
14
+ };
15
+ }
16
+ return undefined;
17
+ }
18
+ export function normalizeContent(value) {
19
+ if (typeof value === "string")
20
+ return value;
21
+ if (Array.isArray(value) && value.every(isJsonObject)) {
22
+ return structuredClone(value);
23
+ }
24
+ return safeStringify(value);
25
+ }
26
+ /** Same weights as the Python HeuristicTokenEstimator. */
27
+ function characterWeight(code) {
28
+ if ((code >= 0x4e00 && code <= 0x9fff) || (code >= 0x3400 && code <= 0x4dbf))
29
+ return 1;
30
+ if ((code >= 0xac00 && code <= 0xd7af) || (code >= 0x1100 && code <= 0x11ff))
31
+ return 1;
32
+ if (code >= 0x3040 && code <= 0x30ff)
33
+ return 1;
34
+ if (code >= 0x1f000)
35
+ return 1;
36
+ if (code >= 0x0370)
37
+ return 0.5;
38
+ return 0.25;
39
+ }
40
+ export function estimateContentTokens(content) {
41
+ const text = typeof content === "string" ? content : JSON.stringify(content);
42
+ let units = 0;
43
+ for (const character of text)
44
+ units += characterWeight(character.codePointAt(0) ?? 0);
45
+ return text.length === 0 ? 0 : Math.max(1, Math.ceil(units));
46
+ }
47
+ function isJsonObject(value) {
48
+ if (typeof value !== "object" || value === null || Array.isArray(value))
49
+ return false;
50
+ return Object.values(value).every(isJsonValue);
51
+ }
52
+ function isJsonValue(value) {
53
+ if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
54
+ return true;
55
+ }
56
+ if (Array.isArray(value))
57
+ return value.every(isJsonValue);
58
+ return isJsonObject(value);
59
+ }
60
+ function safeStringify(value) {
61
+ try {
62
+ const encoded = JSON.stringify(value);
63
+ return encoded ?? String(value);
64
+ }
65
+ catch {
66
+ return String(value);
67
+ }
68
+ }
69
+ /** An opaque observation ID travels with the exact replacement artifact. */
70
+ export function observationId(result) {
71
+ const applied = findAppliedArtifact(result);
72
+ if (!applied?.artifactId)
73
+ return undefined;
74
+ const artifact = result.artifacts.find(a => a.id === applied.artifactId);
75
+ const metadata = artifact?.metadata;
76
+ const id = metadata && typeof metadata === "object" && !Array.isArray(metadata) ? metadata.observation_id : undefined;
77
+ return typeof id === "string" && /^[a-f0-9]{24}$/.test(id) ? id : undefined;
78
+ }
79
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1,58 @@
1
+ import { appendFileSync, openSync, closeSync, mkdirSync, statSync, renameSync, constants, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ import { randomUUID, createHash } from 'node:crypto';
5
+ export const sessionHash = (value) => createHash('sha256').update(value).digest('hex').slice(0, 16);
6
+ const journals = new Map();
7
+ export function journal(host) {
8
+ let log = journals.get(host);
9
+ if (!log) {
10
+ log = createJournal(host);
11
+ journals.set(host, log);
12
+ log('runtime_started');
13
+ }
14
+ return log;
15
+ }
16
+ function createJournal(host) {
17
+ const root = join(process.env.AFU_STATE_DIR ?? join(homedir(), '.local/share/afu-context'), 'logs');
18
+ const id = randomUUID().replaceAll('-', '');
19
+ const file = join(root, `events-${process.pid}-${id}.jsonl`);
20
+ let version = '0.1.0', build = 'development';
21
+ try {
22
+ const m = JSON.parse(readFileSync(join(process.env.AFU_INSTALL_ROOT ?? process.cwd(), 'release.json'), 'utf8'));
23
+ if (/^[a-f0-9]{12,64}$/.test(m.build))
24
+ build = m.build;
25
+ if (/^[0-9.]+$/.test(m.version))
26
+ version = m.version;
27
+ }
28
+ catch { }
29
+ return (event, fields = {}) => {
30
+ try {
31
+ const row = { event, host, version, build, runtime_id: id, pid: process.pid, time: new Date().toISOString() };
32
+ // COSEC: diagnostics never include raw model IDs, credentials, prompts or errors.
33
+ for (const [k, v] of Object.entries(fields)) {
34
+ if (['session', 'artifact_id', 'phase', 'reason'].includes(k) && typeof v === 'string' && /^[A-Za-z0-9_.:+-]{1,100}$/.test(v))
35
+ row[k] = v;
36
+ if (['input_tokens', 'output_tokens', 'cache_read_input_tokens', 'cache_creation_input_tokens', 'context_window', 'threshold', 'duration_ms', 'host_compaction_ms'].includes(k) && typeof v === 'number' && Number.isFinite(v) && v >= 0)
37
+ row[k] = v;
38
+ if (['cache_usage_known', 'applied', 'upstream_success'].includes(k) && typeof v === 'boolean')
39
+ row[k] = v;
40
+ }
41
+ mkdirSync(root, { recursive: true, mode: 0o700 });
42
+ try {
43
+ if (statSync(file).size > 2 * 1024 * 1024)
44
+ renameSync(file, file.replace('.jsonl', '.previous.jsonl'));
45
+ }
46
+ catch { }
47
+ const fd = openSync(file, constants.O_WRONLY | constants.O_CREAT | constants.O_APPEND | (constants.O_NOFOLLOW ?? 0), 0o600);
48
+ try {
49
+ appendFileSync(fd, JSON.stringify(row) + '\n');
50
+ }
51
+ finally {
52
+ closeSync(fd);
53
+ }
54
+ }
55
+ catch { } // Logging must never break a host request.
56
+ };
57
+ }
58
+ //# sourceMappingURL=telemetry.js.map