elepha 0.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/LICENSE.md +375 -0
- package/README.md +135 -0
- package/bin/elepha.js +2 -0
- package/dist/adapters/base.js +600 -0
- package/dist/adapters/base.js.map +1 -0
- package/dist/adapters/claude-code.js +260 -0
- package/dist/adapters/claude-code.js.map +1 -0
- package/dist/adapters/codex.js +499 -0
- package/dist/adapters/codex.js.map +1 -0
- package/dist/adapters/discriminators.js +39 -0
- package/dist/adapters/discriminators.js.map +1 -0
- package/dist/adapters/index.js +6 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/internal-command.js +3 -0
- package/dist/adapters/internal-command.js.map +1 -0
- package/dist/cli/backup-wizard.js +97 -0
- package/dist/cli/backup-wizard.js.map +1 -0
- package/dist/cli/capture-service.js +80 -0
- package/dist/cli/capture-service.js.map +1 -0
- package/dist/cli/commands/backfills.js +336 -0
- package/dist/cli/commands/backfills.js.map +1 -0
- package/dist/cli/commands/backup.js +234 -0
- package/dist/cli/commands/backup.js.map +1 -0
- package/dist/cli/commands/config.js +66 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/consent.js +237 -0
- package/dist/cli/commands/consent.js.map +1 -0
- package/dist/cli/commands/daemon-control.js +76 -0
- package/dist/cli/commands/daemon-control.js.map +1 -0
- package/dist/cli/commands/doctor.js +31 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/hook.js +24 -0
- package/dist/cli/commands/hook.js.map +1 -0
- package/dist/cli/commands/import.js +611 -0
- package/dist/cli/commands/import.js.map +1 -0
- package/dist/cli/commands/init.js +19 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/inspect.js +54 -0
- package/dist/cli/commands/inspect.js.map +1 -0
- package/dist/cli/commands/install.js +49 -0
- package/dist/cli/commands/install.js.map +1 -0
- package/dist/cli/commands/internal.js +16 -0
- package/dist/cli/commands/internal.js.map +1 -0
- package/dist/cli/commands/mcp.js +12 -0
- package/dist/cli/commands/mcp.js.map +1 -0
- package/dist/cli/commands/projects.js +26 -0
- package/dist/cli/commands/projects.js.map +1 -0
- package/dist/cli/commands/purge.js +216 -0
- package/dist/cli/commands/purge.js.map +1 -0
- package/dist/cli/commands/reingest.js +80 -0
- package/dist/cli/commands/reingest.js.map +1 -0
- package/dist/cli/commands/rekey.js +58 -0
- package/dist/cli/commands/rekey.js.map +1 -0
- package/dist/cli/commands/restore.js +376 -0
- package/dist/cli/commands/restore.js.map +1 -0
- package/dist/cli/commands/rollup.js +130 -0
- package/dist/cli/commands/rollup.js.map +1 -0
- package/dist/cli/commands/sanitize.js +72 -0
- package/dist/cli/commands/sanitize.js.map +1 -0
- package/dist/cli/commands/segment.js +153 -0
- package/dist/cli/commands/segment.js.map +1 -0
- package/dist/cli/commands/self-update.js +26 -0
- package/dist/cli/commands/self-update.js.map +1 -0
- package/dist/cli/commands/start.js +53 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/stats.js +42 -0
- package/dist/cli/commands/stats.js.map +1 -0
- package/dist/cli/commands/status.js +73 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/uninstall.js +23 -0
- package/dist/cli/commands/uninstall.js.map +1 -0
- package/dist/cli/config-wizard.js +104 -0
- package/dist/cli/config-wizard.js.map +1 -0
- package/dist/cli/destructive-op.js +32 -0
- package/dist/cli/destructive-op.js.map +1 -0
- package/dist/cli/import-wizard.js +89 -0
- package/dist/cli/import-wizard.js.map +1 -0
- package/dist/cli/index.js +74 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init-wizard.js +80 -0
- package/dist/cli/init-wizard.js.map +1 -0
- package/dist/cli/init.js +159 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/launcher-probe.js +71 -0
- package/dist/cli/launcher-probe.js.map +1 -0
- package/dist/cli/project-path.js +16 -0
- package/dist/cli/project-path.js.map +1 -0
- package/dist/cli/purge-wizard.js +200 -0
- package/dist/cli/purge-wizard.js.map +1 -0
- package/dist/cli/restore-wizard.js +101 -0
- package/dist/cli/restore-wizard.js.map +1 -0
- package/dist/cli/shared.js +128 -0
- package/dist/cli/shared.js.map +1 -0
- package/dist/cli/status.js +22 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/cli/wordmark.js +15 -0
- package/dist/cli/wordmark.js.map +1 -0
- package/dist/config/constants.js +115 -0
- package/dist/config/constants.js.map +1 -0
- package/dist/config/env.js +168 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/memory-config.js +63 -0
- package/dist/config/memory-config.js.map +1 -0
- package/dist/config/paths.js +343 -0
- package/dist/config/paths.js.map +1 -0
- package/dist/config/settings.js +132 -0
- package/dist/config/settings.js.map +1 -0
- package/dist/config/version.js +12 -0
- package/dist/config/version.js.map +1 -0
- package/dist/daemon/failure-window.js +38 -0
- package/dist/daemon/failure-window.js.map +1 -0
- package/dist/daemon/heartbeat.js +40 -0
- package/dist/daemon/heartbeat.js.map +1 -0
- package/dist/daemon/index.js +873 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/log-rotation.js +27 -0
- package/dist/daemon/log-rotation.js.map +1 -0
- package/dist/daemon/readability-guard.js +89 -0
- package/dist/daemon/readability-guard.js.map +1 -0
- package/dist/daemon/rollup-service.js +239 -0
- package/dist/daemon/rollup-service.js.map +1 -0
- package/dist/daemon/update-check.js +87 -0
- package/dist/daemon/update-check.js.map +1 -0
- package/dist/daemon/work-queue.js +28 -0
- package/dist/daemon/work-queue.js.map +1 -0
- package/dist/discovery/session-projects.js +242 -0
- package/dist/discovery/session-projects.js.map +1 -0
- package/dist/hooks/codex-trust.js +80 -0
- package/dist/hooks/codex-trust.js.map +1 -0
- package/dist/hooks/common.js +81 -0
- package/dist/hooks/common.js.map +1 -0
- package/dist/hooks/hook-log.js +38 -0
- package/dist/hooks/hook-log.js.map +1 -0
- package/dist/hooks/installer.js +200 -0
- package/dist/hooks/installer.js.map +1 -0
- package/dist/hooks/session-start.js +321 -0
- package/dist/hooks/session-start.js.map +1 -0
- package/dist/hooks/user-prompt-submit.js +248 -0
- package/dist/hooks/user-prompt-submit.js.map +1 -0
- package/dist/install/binary.js +72 -0
- package/dist/install/binary.js.map +1 -0
- package/dist/install/config-file.js +119 -0
- package/dist/install/config-file.js.map +1 -0
- package/dist/install/daemon-health.js +48 -0
- package/dist/install/daemon-health.js.map +1 -0
- package/dist/install/daemon-service.js +309 -0
- package/dist/install/daemon-service.js.map +1 -0
- package/dist/install/doctor.js +180 -0
- package/dist/install/doctor.js.map +1 -0
- package/dist/install/health-checks.js +90 -0
- package/dist/install/health-checks.js.map +1 -0
- package/dist/install/installer.js +411 -0
- package/dist/install/installer.js.map +1 -0
- package/dist/install/launchd-backend.js +261 -0
- package/dist/install/launchd-backend.js.map +1 -0
- package/dist/install/launcher.js +224 -0
- package/dist/install/launcher.js.map +1 -0
- package/dist/install/markers.js +14 -0
- package/dist/install/markers.js.map +1 -0
- package/dist/install/platform.js +29 -0
- package/dist/install/platform.js.map +1 -0
- package/dist/install/present-tools.js +16 -0
- package/dist/install/present-tools.js.map +1 -0
- package/dist/install/self-update.js +120 -0
- package/dist/install/self-update.js.map +1 -0
- package/dist/install/service-backend.js +36 -0
- package/dist/install/service-backend.js.map +1 -0
- package/dist/install/service-manifest.js +36 -0
- package/dist/install/service-manifest.js.map +1 -0
- package/dist/install/status.js +58 -0
- package/dist/install/status.js.map +1 -0
- package/dist/install/systemd-backend.js +225 -0
- package/dist/install/systemd-backend.js.map +1 -0
- package/dist/markers.js +5 -0
- package/dist/markers.js.map +1 -0
- package/dist/mcp/installer.js +131 -0
- package/dist/mcp/installer.js.map +1 -0
- package/dist/mcp/server.js +81 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools/get-context.js +3 -0
- package/dist/mcp/tools/get-context.js.map +1 -0
- package/dist/mcp/tools/list-projects.js +3 -0
- package/dist/mcp/tools/list-projects.js.map +1 -0
- package/dist/mcp/tools/save-memory.js +3 -0
- package/dist/mcp/tools/save-memory.js.map +1 -0
- package/dist/mcp/tools.js +288 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/rendering/raw-turn-renderer.js +85 -0
- package/dist/rendering/raw-turn-renderer.js.map +1 -0
- package/dist/security/file-permissions.js +15 -0
- package/dist/security/file-permissions.js.map +1 -0
- package/dist/security/provider-transcript.js +74 -0
- package/dist/security/provider-transcript.js.map +1 -0
- package/dist/security/sanitize.js +210 -0
- package/dist/security/sanitize.js.map +1 -0
- package/dist/security/self-ingestion.js +47 -0
- package/dist/security/self-ingestion.js.map +1 -0
- package/dist/security/sentinel.js +19 -0
- package/dist/security/sentinel.js.map +1 -0
- package/dist/security/subprocess-allowlist.js +421 -0
- package/dist/security/subprocess-allowlist.js.map +1 -0
- package/dist/serving/instructions.js +33 -0
- package/dist/serving/instructions.js.map +1 -0
- package/dist/serving/lexical-recall.js +249 -0
- package/dist/serving/lexical-recall.js.map +1 -0
- package/dist/serving/session-reader.js +302 -0
- package/dist/serving/session-reader.js.map +1 -0
- package/dist/storage/backfill-runner.js +34 -0
- package/dist/storage/backfill-runner.js.map +1 -0
- package/dist/storage/backup.js +46 -0
- package/dist/storage/backup.js.map +1 -0
- package/dist/storage/candidate-validator.js +77 -0
- package/dist/storage/candidate-validator.js.map +1 -0
- package/dist/storage/consent-store.js +198 -0
- package/dist/storage/consent-store.js.map +1 -0
- package/dist/storage/custom-title-backfill.js +68 -0
- package/dist/storage/custom-title-backfill.js.map +1 -0
- package/dist/storage/db.js +312 -0
- package/dist/storage/db.js.map +1 -0
- package/dist/storage/external-agent-import-purge.js +182 -0
- package/dist/storage/external-agent-import-purge.js.map +1 -0
- package/dist/storage/first-prompt-search-backfill.js +76 -0
- package/dist/storage/first-prompt-search-backfill.js.map +1 -0
- package/dist/storage/first-prompt-search.js +7 -0
- package/dist/storage/first-prompt-search.js.map +1 -0
- package/dist/storage/injection-store.js +37 -0
- package/dist/storage/injection-store.js.map +1 -0
- package/dist/storage/memory-store.js +428 -0
- package/dist/storage/memory-store.js.map +1 -0
- package/dist/storage/project-resolver.js +294 -0
- package/dist/storage/project-resolver.js.map +1 -0
- package/dist/storage/project-store.js +104 -0
- package/dist/storage/project-store.js.map +1 -0
- package/dist/storage/rendered-chars-backfill.js +85 -0
- package/dist/storage/rendered-chars-backfill.js.map +1 -0
- package/dist/storage/resegmentation.js +582 -0
- package/dist/storage/resegmentation.js.map +1 -0
- package/dist/storage/rollup-store.js +246 -0
- package/dist/storage/rollup-store.js.map +1 -0
- package/dist/storage/root-commit-backfill.js +32 -0
- package/dist/storage/root-commit-backfill.js.map +1 -0
- package/dist/storage/sanitize-backfill.js +184 -0
- package/dist/storage/sanitize-backfill.js.map +1 -0
- package/dist/storage/segmentation.js +87 -0
- package/dist/storage/segmentation.js.map +1 -0
- package/dist/storage/session-fields-backfill.js +196 -0
- package/dist/storage/session-fields-backfill.js.map +1 -0
- package/dist/storage/session-read-model.js +130 -0
- package/dist/storage/session-read-model.js.map +1 -0
- package/dist/storage/session-store.js +200 -0
- package/dist/storage/session-store.js.map +1 -0
- package/dist/storage/session-title-backfill.js +75 -0
- package/dist/storage/session-title-backfill.js.map +1 -0
- package/dist/storage/session-title.js +71 -0
- package/dist/storage/session-title.js.map +1 -0
- package/dist/storage/shown-session-list-store.js +33 -0
- package/dist/storage/shown-session-list-store.js.map +1 -0
- package/dist/storage/stats.js +30 -0
- package/dist/storage/stats.js.map +1 -0
- package/dist/storage/turn-store.js +202 -0
- package/dist/storage/turn-store.js.map +1 -0
- package/dist/storage/ulid.js +17 -0
- package/dist/storage/ulid.js.map +1 -0
- package/dist/summarizer/anthropic-call.js +76 -0
- package/dist/summarizer/anthropic-call.js.map +1 -0
- package/dist/summarizer/call-log.js +120 -0
- package/dist/summarizer/call-log.js.map +1 -0
- package/dist/summarizer/haiku-provider.js +136 -0
- package/dist/summarizer/haiku-provider.js.map +1 -0
- package/dist/summarizer/pricing.js +8 -0
- package/dist/summarizer/pricing.js.map +1 -0
- package/dist/summarizer/prompt-cache.js +6 -0
- package/dist/summarizer/prompt-cache.js.map +1 -0
- package/dist/summarizer/prompt.js +47 -0
- package/dist/summarizer/prompt.js.map +1 -0
- package/dist/summarizer/provider-config.js +25 -0
- package/dist/summarizer/provider-config.js.map +1 -0
- package/dist/summarizer/rollup-prompt.js +225 -0
- package/dist/summarizer/rollup-prompt.js.map +1 -0
- package/dist/summarizer/rollup-provider.js +224 -0
- package/dist/summarizer/rollup-provider.js.map +1 -0
- package/dist/types/index.js +10 -0
- package/dist/types/index.js.map +1 -0
- package/dist/util/error.js +5 -0
- package/dist/util/error.js.map +1 -0
- package/dist/util/fs.js +97 -0
- package/dist/util/fs.js.map +1 -0
- package/dist/util/relative-time.js +12 -0
- package/dist/util/relative-time.js.map +1 -0
- package/package.json +81 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
// Haiku-backed session rollup generation. Same defensive-parsing contract as
|
|
2
|
+
// the per-turn summarizer (see haiku-provider.ts): the model wraps JSON in
|
|
3
|
+
// markdown fences regardless of instructions, so output is extracted, not
|
|
4
|
+
// trusted.
|
|
5
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { DECISION_PROVENANCE_OVERLAP_THRESHOLD, DEFAULT_ANTHROPIC_MODEL, ROLLUP_MAX_TOKENS, ROLLUP_RETRY_MAX_TOKENS, } from '../config/constants.js';
|
|
8
|
+
import { callAnthropicJson, extractJsonCandidate } from './anthropic-call.js';
|
|
9
|
+
import { SummarizerCallLog } from './call-log.js';
|
|
10
|
+
import { cacheableSystemPrompt } from './prompt-cache.js';
|
|
11
|
+
import { buildRollupMergeUserContent, buildRollupUserContent, ROLLUP_MERGE_SYSTEM_PROMPT, ROLLUP_SYSTEM_PROMPT, } from './rollup-prompt.js';
|
|
12
|
+
/**
|
|
13
|
+
* `turn_index` arrives as a STRING from this model - `"turn_index": "0"` -
|
|
14
|
+
* despite the prompt asking for a number, in fixed-sampling runs, reproducibly.
|
|
15
|
+
*
|
|
16
|
+
* The first version of this field was `z.number().int().optional()`, whose
|
|
17
|
+
* comment claimed optionality meant "a model that ignores the instruction
|
|
18
|
+
* still parses". It does not: `optional()` tolerates an ABSENT key, not a
|
|
19
|
+
* wrong-typed one, so every rollup failed to parse and a rebuild died on
|
|
20
|
+
* its first three sessions. Textbook instance of this codebase's own
|
|
21
|
+
* "format instructions in a prompt are a suggestion, not a contract" - added
|
|
22
|
+
* while fixing a different instance of the same rule.
|
|
23
|
+
*/
|
|
24
|
+
function coerceTurnIndex(value) {
|
|
25
|
+
if (typeof value === 'number' && Number.isInteger(value)) {
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
if (typeof value === 'string' && /^-?\d+$/.test(value.trim())) {
|
|
29
|
+
return Number(value.trim());
|
|
30
|
+
}
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* One decision, or undefined if it is unusable.
|
|
35
|
+
*
|
|
36
|
+
* Nothing here can fail the enclosing rollup. A malformed or hollow decision
|
|
37
|
+
* costs that decision; it must never cost the title, the summary, the pending
|
|
38
|
+
* items and every other decision in the session, which is what a strict
|
|
39
|
+
* element schema inside `z.array()` does.
|
|
40
|
+
*/
|
|
41
|
+
function coerceDecision(raw) {
|
|
42
|
+
if (!raw || typeof raw !== 'object') {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
const rec = raw;
|
|
46
|
+
const what = typeof rec.what === 'string' ? rec.what.trim() : '';
|
|
47
|
+
// A decision with no reason is dropped, per the prompt contract ("omit
|
|
48
|
+
// that decision entirely rather than emitting a hollow one"). Counted and
|
|
49
|
+
// reported by the caller rather than dropped silently - see
|
|
50
|
+
// RollupResult.droppedDecisions.
|
|
51
|
+
const why = typeof rec.why === 'string' ? rec.why.trim() : '';
|
|
52
|
+
if (what === '' || why === '') {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
return { what, why, turn_index: coerceTurnIndex(rec.turn_index) };
|
|
56
|
+
}
|
|
57
|
+
const rollupSchema = z.object({
|
|
58
|
+
title: z.string().min(1),
|
|
59
|
+
summary: z.string().min(1),
|
|
60
|
+
decisions: z.array(z.unknown()),
|
|
61
|
+
pending_items: z.array(z.unknown()),
|
|
62
|
+
});
|
|
63
|
+
const EMPTY_ROLLUP = { title: '', summary: '', decisions: [], pending_items: [], droppedDecisions: 0 };
|
|
64
|
+
function tryParse(candidate) {
|
|
65
|
+
try {
|
|
66
|
+
const result = rollupSchema.safeParse(JSON.parse(candidate));
|
|
67
|
+
if (!result.success) {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
const decisions = [];
|
|
71
|
+
let droppedDecisions = 0;
|
|
72
|
+
for (const raw of result.data.decisions) {
|
|
73
|
+
const decision = coerceDecision(raw);
|
|
74
|
+
if (decision) {
|
|
75
|
+
decisions.push(decision);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
droppedDecisions++;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
title: result.data.title,
|
|
83
|
+
summary: result.data.summary,
|
|
84
|
+
decisions,
|
|
85
|
+
pending_items: result.data.pending_items.filter((p) => typeof p === 'string' && p.trim() !== ''),
|
|
86
|
+
droppedDecisions,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export function parseRollup(raw) {
|
|
94
|
+
for (const candidate of extractJsonCandidate(raw)) {
|
|
95
|
+
const parsed = tryParse(candidate);
|
|
96
|
+
if (parsed) {
|
|
97
|
+
return parsed;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
const STOPWORDS = new Set('the a an to for and or with by as at is are was were be been it its this that from into use used using in on of'.split(' '));
|
|
103
|
+
function tokens(s) {
|
|
104
|
+
return new Set(s
|
|
105
|
+
.toLowerCase()
|
|
106
|
+
.replace(/[^a-z0-9_./-]+/g, ' ')
|
|
107
|
+
.split(/\s+/)
|
|
108
|
+
.filter((t) => t.length > 2 && !STOPWORDS.has(t)));
|
|
109
|
+
}
|
|
110
|
+
function overlap(a, b) {
|
|
111
|
+
if (a.size === 0 || b.size === 0) {
|
|
112
|
+
return 0;
|
|
113
|
+
}
|
|
114
|
+
let shared = 0;
|
|
115
|
+
for (const t of a) {
|
|
116
|
+
if (b.has(t)) {
|
|
117
|
+
shared++;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return shared / Math.min(a.size, b.size);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Assigns each returned decision a turn of origin, IN CODE.
|
|
124
|
+
*
|
|
125
|
+
* The prompt asks for `turn_index`, but the whole reason this exists is that
|
|
126
|
+
* the merge model does not reliably follow ordering instructions - trusting it
|
|
127
|
+
* to self-report provenance would rebuild the bug one level up. So:
|
|
128
|
+
*
|
|
129
|
+
* 1. Accept the model's `turn_index` only when it names a turn that was
|
|
130
|
+
* actually in this batch. Anything else is discarded silently-as-data, not
|
|
131
|
+
* silently-as-failure: the next steps still produce a value.
|
|
132
|
+
* 2. Otherwise, match the decision text against the batch's own per-turn
|
|
133
|
+
* decision strings and take the best-overlapping turn.
|
|
134
|
+
* 3. Otherwise, fall back to the batch's LAST turn index. The decision came
|
|
135
|
+
* out of this batch, so its true origin is somewhere in it; biasing the
|
|
136
|
+
* fallback newest keeps an unplaceable decision visible in the brief
|
|
137
|
+
* instead of sinking it to the bottom, which is the failure mode being
|
|
138
|
+
* fixed. Over-attributing recency is recoverable; under-attributing it is
|
|
139
|
+
* the bug.
|
|
140
|
+
*/
|
|
141
|
+
export function attributeDecisions(decisions, batch) {
|
|
142
|
+
if (batch.length === 0) {
|
|
143
|
+
return decisions.map((d) => ({ what: d.what, why: d.why }));
|
|
144
|
+
}
|
|
145
|
+
const byIndex = new Map(batch.map((t) => [t.turnIndex, t]));
|
|
146
|
+
const lastTurn = batch[batch.length - 1];
|
|
147
|
+
const turnTokens = batch.map((t) => ({ turn: t, toks: tokens(t.decisions.join(' ')) }));
|
|
148
|
+
return decisions.map((d) => {
|
|
149
|
+
const claimed = d.turn_index !== undefined ? byIndex.get(d.turn_index) : undefined;
|
|
150
|
+
if (claimed) {
|
|
151
|
+
return { what: d.what, why: d.why, turnIndex: claimed.turnIndex, at: claimed.startedAt };
|
|
152
|
+
}
|
|
153
|
+
const dt = tokens(d.what);
|
|
154
|
+
let best;
|
|
155
|
+
for (const candidate of turnTokens) {
|
|
156
|
+
const score = overlap(dt, candidate.toks);
|
|
157
|
+
if (score >= DECISION_PROVENANCE_OVERLAP_THRESHOLD && (best === undefined || score > best.score)) {
|
|
158
|
+
best = { turn: candidate.turn, score };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const resolved = best?.turn ?? lastTurn;
|
|
162
|
+
return { what: d.what, why: d.why, turnIndex: resolved.turnIndex, at: resolved.startedAt };
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
export class HaikuRollupProvider {
|
|
166
|
+
client;
|
|
167
|
+
model;
|
|
168
|
+
callLog;
|
|
169
|
+
constructor(options = {}) {
|
|
170
|
+
this.client = new Anthropic(options.apiKey ? { apiKey: options.apiKey } : {});
|
|
171
|
+
this.model = options.model ?? DEFAULT_ANTHROPIC_MODEL;
|
|
172
|
+
this.callLog = options.callLog ?? new SummarizerCallLog();
|
|
173
|
+
}
|
|
174
|
+
rollup(turns) {
|
|
175
|
+
if (turns.length === 0) {
|
|
176
|
+
return Promise.resolve({ output: EMPTY_ROLLUP, status: 'empty_turn' });
|
|
177
|
+
}
|
|
178
|
+
return this.call(ROLLUP_SYSTEM_PROMPT, buildRollupUserContent(turns));
|
|
179
|
+
}
|
|
180
|
+
merge(previous, newTurns) {
|
|
181
|
+
if (newTurns.length === 0) {
|
|
182
|
+
return Promise.resolve({ output: EMPTY_ROLLUP, status: 'empty_turn' });
|
|
183
|
+
}
|
|
184
|
+
return this.call(ROLLUP_MERGE_SYSTEM_PROMPT, buildRollupMergeUserContent(previous, newTurns));
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* One rollup request, retried ONCE at a larger output budget if the first
|
|
188
|
+
* attempt ran out of room mid-JSON.
|
|
189
|
+
*
|
|
190
|
+
* A same-budget retry risks the same truncation, so it is pure waste.
|
|
191
|
+
* The only thing worth changing is the budget - the input was never the
|
|
192
|
+
* problem. (A malformed-but-complete response is a different failure and
|
|
193
|
+
* is not retried here: rollups are recomputed from turn rows on the next
|
|
194
|
+
* pass, and the watermark deliberately does not advance on failure.)
|
|
195
|
+
*/
|
|
196
|
+
async call(system, userContent) {
|
|
197
|
+
const first = await this.callOnce(system, userContent, ROLLUP_MAX_TOKENS, 1);
|
|
198
|
+
if (first.status === 'ok' || !first.truncated) {
|
|
199
|
+
return { output: first.output, status: first.status };
|
|
200
|
+
}
|
|
201
|
+
const retried = await this.callOnce(system, userContent, ROLLUP_RETRY_MAX_TOKENS, 2);
|
|
202
|
+
return { output: retried.output, status: retried.status };
|
|
203
|
+
}
|
|
204
|
+
async callOnce(system, userContent, maxTokens, attempt) {
|
|
205
|
+
// A session rollup is far larger than a per-turn summary: a 17-turn session
|
|
206
|
+
// produced enough decisions to blow straight through 1024 tokens and truncate
|
|
207
|
+
// mid-JSON, which surfaced only as an unexplained parse_error. Sized with
|
|
208
|
+
// headroom, and truncation is now reported distinctly rather than looking like
|
|
209
|
+
// a malformed response.
|
|
210
|
+
const result = await callAnthropicJson({
|
|
211
|
+
client: this.client,
|
|
212
|
+
model: this.model,
|
|
213
|
+
callLog: this.callLog,
|
|
214
|
+
system: cacheableSystemPrompt(system),
|
|
215
|
+
user: userContent,
|
|
216
|
+
maxTokens,
|
|
217
|
+
parse: parseRollup,
|
|
218
|
+
job: 'rollup_merge',
|
|
219
|
+
attempt,
|
|
220
|
+
});
|
|
221
|
+
return { output: result.output ?? EMPTY_ROLLUP, status: result.status, truncated: result.truncated };
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
//# sourceMappingURL=rollup-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rollup-provider.js","sourceRoot":"","sources":["../../src/summarizer/rollup-provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Shared types: SessionTurn, ToolName, etc.
|
|
2
|
+
export const TOOL_METADATA = {
|
|
3
|
+
'claude-code': { displayName: 'Claude Code' },
|
|
4
|
+
codex: { displayName: 'Codex' },
|
|
5
|
+
};
|
|
6
|
+
export const SUPPORTED_TOOLS = Object.keys(TOOL_METADATA);
|
|
7
|
+
export function isToolName(value) {
|
|
8
|
+
return SUPPORTED_TOOLS.includes(value);
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAI5C,MAAM,CAAC,MAAM,aAAa,GAAG;IACzB,aAAa,EAAE,EAAE,WAAW,EAAE,aAAa,EAAE;IAC7C,KAAK,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE;CACzB,CAAC;AAIX,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,CAAe,CAAC;AAExE,MAAM,UAAU,UAAU,CAAC,KAAc;IACrC,OAAO,eAAe,CAAC,QAAQ,CAAC,KAAiB,CAAC,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error.js","sourceRoot":"","sources":["../../src/util/error.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,MAAM,UAAU,YAAY,CAAC,KAAc;IACvC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAClE,CAAC"}
|
package/dist/util/fs.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { chmodSync, copyFileSync, lstatSync, mkdirSync, readdirSync, readFileSync, realpathSync, renameSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { PRIVATE_DIR_MODE } from '../config/constants.js';
|
|
5
|
+
/** Creates a directory and ensures it is private even when it already existed. */
|
|
6
|
+
export function ensurePrivateDir(dir) {
|
|
7
|
+
mkdirSync(dir, { recursive: true });
|
|
8
|
+
chmodSync(dir, PRIVATE_DIR_MODE);
|
|
9
|
+
}
|
|
10
|
+
/** Creates missing directories and hardens only the directories created by this call. */
|
|
11
|
+
export function ensureCreatedDirsPrivate(dir) {
|
|
12
|
+
const firstCreated = mkdirSync(dir, { recursive: true });
|
|
13
|
+
if (firstCreated === undefined) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const firstCreatedPath = path.resolve(firstCreated);
|
|
17
|
+
let current = path.resolve(dir);
|
|
18
|
+
while (true) {
|
|
19
|
+
chmodSync(current, PRIVATE_DIR_MODE);
|
|
20
|
+
if (current === firstCreatedPath) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
current = path.dirname(current);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** Applies a restrictive mode to an already-written private file. */
|
|
27
|
+
export function setPrivateFileMode(file, mode) {
|
|
28
|
+
chmodSync(file, mode);
|
|
29
|
+
}
|
|
30
|
+
/** Atomically replace a file without replacing a symlink that points at it. */
|
|
31
|
+
export function atomicWrite(file, text, mode) {
|
|
32
|
+
const target = (() => {
|
|
33
|
+
try {
|
|
34
|
+
return lstatSync(file).isSymbolicLink() ? realpathSync(file) : file;
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
if (error.code === 'ENOENT') {
|
|
38
|
+
return file;
|
|
39
|
+
}
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
})();
|
|
43
|
+
mkdirSync(path.dirname(target), { recursive: true });
|
|
44
|
+
const temporary = `${target}.${process.pid}.${randomUUID()}.tmp`;
|
|
45
|
+
writeFileSync(temporary, text, { mode });
|
|
46
|
+
renameSync(temporary, target);
|
|
47
|
+
chmodSync(target, mode);
|
|
48
|
+
}
|
|
49
|
+
/** Atomically replaces a file with a byte-for-byte private copy without treating binary data as text. */
|
|
50
|
+
export function atomicCopyPrivateFile(source, destination, mode) {
|
|
51
|
+
const target = (() => {
|
|
52
|
+
try {
|
|
53
|
+
return lstatSync(destination).isSymbolicLink() ? realpathSync(destination) : destination;
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (error.code === 'ENOENT') {
|
|
57
|
+
return destination;
|
|
58
|
+
}
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
})();
|
|
62
|
+
ensurePrivateDir(path.dirname(target));
|
|
63
|
+
const temporary = `${target}.${process.pid}.${randomUUID()}.tmp`;
|
|
64
|
+
copyFileSync(source, temporary);
|
|
65
|
+
chmodSync(temporary, mode);
|
|
66
|
+
renameSync(temporary, target);
|
|
67
|
+
chmodSync(target, mode);
|
|
68
|
+
}
|
|
69
|
+
/** Read JSON only when the complete file can be read and parsed. */
|
|
70
|
+
export function readJson(file) {
|
|
71
|
+
try {
|
|
72
|
+
return JSON.parse(readFileSync(file, 'utf8'));
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Create the parent directory and write a newline-terminated JSON file. */
|
|
79
|
+
export function writeJson(file, value, mode) {
|
|
80
|
+
mkdirSync(path.dirname(file), { recursive: true });
|
|
81
|
+
writeFileSync(file, `${JSON.stringify(value)}\n`, { mode });
|
|
82
|
+
}
|
|
83
|
+
/** Lists regular files without turning a missing optional directory into an error. */
|
|
84
|
+
export function listRegularFiles(directory) {
|
|
85
|
+
try {
|
|
86
|
+
return readdirSync(directory, { withFileTypes: true })
|
|
87
|
+
.filter((entry) => entry.isFile())
|
|
88
|
+
.map((entry) => path.join(directory, entry.name));
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
if (error.code === 'ENOENT') {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=fs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs.js","sourceRoot":"","sources":["../../src/util/fs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DAY_MS } from '../config/constants.js';
|
|
2
|
+
export function relativeTime(iso, now) {
|
|
3
|
+
const age = Math.max(0, now - Date.parse(iso));
|
|
4
|
+
if (age < 60 * 60 * 1000) {
|
|
5
|
+
return `${Math.floor(age / 60000)}m ago`;
|
|
6
|
+
}
|
|
7
|
+
if (age < DAY_MS) {
|
|
8
|
+
return `${Math.floor(age / (60 * 60 * 1000))}h ago`;
|
|
9
|
+
}
|
|
10
|
+
return `${Math.floor(age / DAY_MS)}d ago`;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=relative-time.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relative-time.js","sourceRoot":"","sources":["../../src/util/relative-time.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAEhD,MAAM,UAAU,YAAY,CAAC,GAAW,EAAE,GAAW;IACjD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/C,IAAI,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC;QACvB,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC;IAC7C,CAAC;IACD,IAAI,GAAG,GAAG,MAAM,EAAE,CAAC;QACf,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC;IACxD,CAAC;IACD,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC;AAC9C,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "elepha",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Local memory layer for developers switching between AI coding CLIs",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"claude-code",
|
|
7
|
+
"codex",
|
|
8
|
+
"coding-agent",
|
|
9
|
+
"ai-memory",
|
|
10
|
+
"agent-memory",
|
|
11
|
+
"agent-memory",
|
|
12
|
+
"session-memory",
|
|
13
|
+
"session-history",
|
|
14
|
+
"context-management",
|
|
15
|
+
"developer-tools",
|
|
16
|
+
"cli",
|
|
17
|
+
"local-first"
|
|
18
|
+
],
|
|
19
|
+
"homepage": "https://github.com/elepha-app/elepha#readme",
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/elepha-app/elepha/issues"
|
|
22
|
+
},
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/elepha-app/elepha.git"
|
|
26
|
+
},
|
|
27
|
+
"license": "MPL-2.0",
|
|
28
|
+
"type": "module",
|
|
29
|
+
"bin": {
|
|
30
|
+
"elepha": "./bin/elepha.js"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"bin"
|
|
35
|
+
],
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=22"
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"prepublishOnly": "npm run build",
|
|
44
|
+
"build": "tsc -p tsconfig.json",
|
|
45
|
+
"dev": "tsx watch src/cli/index.ts",
|
|
46
|
+
"start": "node dist/cli/index.js",
|
|
47
|
+
"test": "vitest run",
|
|
48
|
+
"typecheck": "tsc --noEmit",
|
|
49
|
+
"typecheck:tests": "tsc -p tsconfig.tests.json --noEmit",
|
|
50
|
+
"changeset": "changeset",
|
|
51
|
+
"release": "changeset version && changeset publish",
|
|
52
|
+
"format-lint": "biome check --write .",
|
|
53
|
+
"format:check": "biome check ."
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"@anthropic-ai/sdk": "^0.122.0",
|
|
57
|
+
"@clack/prompts": "^1.7.0",
|
|
58
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
59
|
+
"better-sqlite3": "^13.0.3",
|
|
60
|
+
"chokidar": "^5.0.0",
|
|
61
|
+
"commander": "^15.0.0",
|
|
62
|
+
"smol-toml": "^1.8.0",
|
|
63
|
+
"zod": "^4.5.2"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@biomejs/biome": "2.5.11",
|
|
67
|
+
"@changesets/cli": "^3.0.1",
|
|
68
|
+
"@commitlint/cli": "^21.2.2",
|
|
69
|
+
"@commitlint/config-conventional": "^21.2.2",
|
|
70
|
+
"@types/better-sqlite3": "^9.6.0",
|
|
71
|
+
"@types/node": "^26.4.0",
|
|
72
|
+
"tsx": "^4.23.12",
|
|
73
|
+
"typescript": "^7.0.2",
|
|
74
|
+
"vitest": "^4.1.11"
|
|
75
|
+
},
|
|
76
|
+
"allowScripts": {
|
|
77
|
+
"better-sqlite3@13.0.3": true,
|
|
78
|
+
"esbuild@0.28.2": true,
|
|
79
|
+
"fsevents@2.3.3": true
|
|
80
|
+
}
|
|
81
|
+
}
|