@sapiom/harness 0.1.5 → 0.2.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/CHANGELOG.md +151 -0
- package/dist/cli/args.d.ts +16 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +80 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/bin.js +30 -66
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/consent.js +1 -1
- package/dist/cli/settings.d.ts.map +1 -1
- package/dist/cli/settings.js +33 -1
- package/dist/cli/settings.js.map +1 -1
- package/dist/core/adapters/adapter.d.ts +2 -2
- package/dist/core/adapters/claude-code.d.ts +60 -2
- package/dist/core/adapters/claude-code.d.ts.map +1 -1
- package/dist/core/adapters/claude-code.js +119 -32
- package/dist/core/adapters/claude-code.js.map +1 -1
- package/dist/core/adapters/codex.d.ts +21 -2
- package/dist/core/adapters/codex.d.ts.map +1 -1
- package/dist/core/adapters/codex.js +53 -2
- package/dist/core/adapters/codex.js.map +1 -1
- package/dist/core/adapters/detect.d.ts +9 -0
- package/dist/core/adapters/detect.d.ts.map +1 -1
- package/dist/core/adapters/detect.js +9 -0
- package/dist/core/adapters/detect.js.map +1 -1
- package/dist/core/api-key-provider.d.ts +79 -0
- package/dist/core/api-key-provider.d.ts.map +1 -0
- package/dist/core/api-key-provider.js +91 -0
- package/dist/core/api-key-provider.js.map +1 -0
- package/dist/core/canvas-body.d.ts +28 -28
- package/dist/core/canvas-body.d.ts.map +1 -1
- package/dist/core/canvas-body.js +117 -55
- package/dist/core/canvas-body.js.map +1 -1
- package/dist/core/canvas-derive.d.ts +43 -0
- package/dist/core/canvas-derive.d.ts.map +1 -0
- package/dist/core/canvas-derive.js +95 -0
- package/dist/core/canvas-derive.js.map +1 -0
- package/dist/core/canvas-enrichment.d.ts +52 -127
- package/dist/core/canvas-enrichment.d.ts.map +1 -1
- package/dist/core/canvas-enrichment.js +17 -178
- package/dist/core/canvas-enrichment.js.map +1 -1
- package/dist/core/canvas-graph.d.ts +40 -1
- package/dist/core/canvas-graph.d.ts.map +1 -1
- package/dist/core/canvas-graph.js +71 -9
- package/dist/core/canvas-graph.js.map +1 -1
- package/dist/core/canvas-interconnections.d.ts +26 -8
- package/dist/core/canvas-interconnections.d.ts.map +1 -1
- package/dist/core/canvas-interconnections.js +132 -24
- package/dist/core/canvas-interconnections.js.map +1 -1
- package/dist/core/canvas-manifest-check.d.ts.map +1 -1
- package/dist/core/canvas-manifest-check.js +15 -2
- package/dist/core/canvas-manifest-check.js.map +1 -1
- package/dist/core/canvas-render.d.ts +8 -15
- package/dist/core/canvas-render.d.ts.map +1 -1
- package/dist/core/canvas-render.js +29 -41
- package/dist/core/canvas-render.js.map +1 -1
- package/dist/core/canvas-run-state.d.ts +50 -5
- package/dist/core/canvas-run-state.d.ts.map +1 -1
- package/dist/core/canvas-run-state.js +176 -11
- package/dist/core/canvas-run-state.js.map +1 -1
- package/dist/core/canvas-svg.d.ts +9 -4
- package/dist/core/canvas-svg.d.ts.map +1 -1
- package/dist/core/canvas-svg.js +84 -13
- package/dist/core/canvas-svg.js.map +1 -1
- package/dist/core/canvas-template.d.ts +9 -10
- package/dist/core/canvas-template.d.ts.map +1 -1
- package/dist/core/canvas-template.js +57 -20
- package/dist/core/canvas-template.js.map +1 -1
- package/dist/core/canvas-watcher.d.ts +22 -1
- package/dist/core/canvas-watcher.d.ts.map +1 -1
- package/dist/core/canvas-watcher.js +111 -30
- package/dist/core/canvas-watcher.js.map +1 -1
- package/dist/core/collector/normalizer.d.ts +8 -0
- package/dist/core/collector/normalizer.d.ts.map +1 -1
- package/dist/core/collector/normalizer.js +8 -0
- package/dist/core/collector/normalizer.js.map +1 -1
- package/dist/core/collector/store.d.ts +75 -1
- package/dist/core/collector/store.d.ts.map +1 -1
- package/dist/core/collector/store.js +334 -2
- package/dist/core/collector/store.js.map +1 -1
- package/dist/core/definition-slug-resolver.d.ts +13 -0
- package/dist/core/definition-slug-resolver.d.ts.map +1 -1
- package/dist/core/definition-slug-resolver.js +29 -0
- package/dist/core/definition-slug-resolver.js.map +1 -1
- package/dist/core/errors.d.ts +10 -4
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +11 -5
- package/dist/core/errors.js.map +1 -1
- package/dist/core/example-seed.d.ts +8 -0
- package/dist/core/example-seed.d.ts.map +1 -1
- package/dist/core/example-seed.js +47 -10
- package/dist/core/example-seed.js.map +1 -1
- package/dist/core/inject/system-prompt.d.ts +8 -0
- package/dist/core/inject/system-prompt.d.ts.map +1 -1
- package/dist/core/inject/system-prompt.js +3 -1
- package/dist/core/inject/system-prompt.js.map +1 -1
- package/dist/core/macros.d.ts.map +1 -1
- package/dist/core/macros.js +5 -6
- package/dist/core/macros.js.map +1 -1
- package/dist/core/paths.d.ts +1 -0
- package/dist/core/paths.d.ts.map +1 -1
- package/dist/core/paths.js +1 -0
- package/dist/core/paths.js.map +1 -1
- package/dist/core/record-archive.d.ts +187 -0
- package/dist/core/record-archive.d.ts.map +1 -0
- package/dist/core/record-archive.js +465 -0
- package/dist/core/record-archive.js.map +1 -0
- package/dist/core/rehydration.d.ts +72 -0
- package/dist/core/rehydration.d.ts.map +1 -0
- package/dist/core/rehydration.js +72 -0
- package/dist/core/rehydration.js.map +1 -0
- package/dist/core/render-local-run.d.ts +82 -0
- package/dist/core/render-local-run.d.ts.map +1 -0
- package/dist/core/render-local-run.js +105 -0
- package/dist/core/render-local-run.js.map +1 -0
- package/dist/core/render-log-slice.d.ts +30 -0
- package/dist/core/render-log-slice.d.ts.map +1 -0
- package/dist/core/render-log-slice.js +49 -0
- package/dist/core/render-log-slice.js.map +1 -0
- package/dist/core/render-run-state.d.ts.map +1 -1
- package/dist/core/render-run-state.js +30 -49
- package/dist/core/render-run-state.js.map +1 -1
- package/dist/core/resume-brief.d.ts +130 -0
- package/dist/core/resume-brief.d.ts.map +1 -0
- package/dist/core/resume-brief.js +404 -0
- package/dist/core/resume-brief.js.map +1 -0
- package/dist/core/rolling-summary.d.ts +130 -0
- package/dist/core/rolling-summary.d.ts.map +1 -0
- package/dist/core/rolling-summary.js +227 -0
- package/dist/core/rolling-summary.js.map +1 -0
- package/dist/core/run-local-bootstrap.d.ts +93 -0
- package/dist/core/run-local-bootstrap.d.ts.map +1 -0
- package/dist/core/run-local-bootstrap.js +155 -0
- package/dist/core/run-local-bootstrap.js.map +1 -0
- package/dist/core/run-state.d.ts +16 -1
- package/dist/core/run-state.d.ts.map +1 -1
- package/dist/core/run-state.js +75 -25
- package/dist/core/run-state.js.map +1 -1
- package/dist/core/session-manager.d.ts +11 -3
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +56 -7
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-record.d.ts +165 -0
- package/dist/core/session-record.d.ts.map +1 -0
- package/dist/core/session-record.js +470 -0
- package/dist/core/session-record.js.map +1 -0
- package/dist/core/spawn-target.d.ts +12 -0
- package/dist/core/spawn-target.d.ts.map +1 -0
- package/dist/core/spawn-target.js +187 -0
- package/dist/core/spawn-target.js.map +1 -0
- package/dist/core/stub-feedback.d.ts +52 -0
- package/dist/core/stub-feedback.d.ts.map +1 -0
- package/dist/core/stub-feedback.js +33 -0
- package/dist/core/stub-feedback.js.map +1 -0
- package/dist/core/task-manager.d.ts.map +1 -1
- package/dist/core/task-manager.js +9 -1
- package/dist/core/task-manager.js.map +1 -1
- package/dist/core/template-catalog.d.ts +48 -0
- package/dist/core/template-catalog.d.ts.map +1 -0
- package/dist/core/template-catalog.js +338 -0
- package/dist/core/template-catalog.js.map +1 -0
- package/dist/core/workflow-registry.d.ts.map +1 -1
- package/dist/core/workflow-registry.js +2 -2
- package/dist/core/workflow-registry.js.map +1 -1
- package/dist/core/workspace-watcher.d.ts.map +1 -1
- package/dist/core/workspace-watcher.js +4 -3
- package/dist/core/workspace-watcher.js.map +1 -1
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -1
- package/dist/profiles/default.d.ts.map +1 -1
- package/dist/profiles/default.js +9 -14
- package/dist/profiles/default.js.map +1 -1
- package/dist/server/actions.d.ts +139 -0
- package/dist/server/actions.d.ts.map +1 -0
- package/dist/server/actions.js +430 -0
- package/dist/server/actions.js.map +1 -0
- package/dist/server/auth-routes.d.ts +92 -0
- package/dist/server/auth-routes.d.ts.map +1 -0
- package/dist/server/auth-routes.js +151 -0
- package/dist/server/auth-routes.js.map +1 -0
- package/dist/server/canvas.d.ts.map +1 -1
- package/dist/server/canvas.js +7 -7
- package/dist/server/canvas.js.map +1 -1
- package/dist/server/fs.d.ts.map +1 -1
- package/dist/server/fs.js +17 -1
- package/dist/server/fs.js.map +1 -1
- package/dist/server/index.d.ts +12 -3
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +0 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/ingest.d.ts +11 -0
- package/dist/server/ingest.d.ts.map +1 -1
- package/dist/server/ingest.js +1 -0
- package/dist/server/ingest.js.map +1 -1
- package/dist/server/macros.d.ts +4 -6
- package/dist/server/macros.d.ts.map +1 -1
- package/dist/server/macros.js.map +1 -1
- package/dist/server/rest.d.ts +18 -6
- package/dist/server/rest.d.ts.map +1 -1
- package/dist/server/rest.js +185 -50
- package/dist/server/rest.js.map +1 -1
- package/dist/server/runs.d.ts +16 -15
- package/dist/server/runs.d.ts.map +1 -1
- package/dist/server/runs.js +15 -80
- package/dist/server/runs.js.map +1 -1
- package/dist/server/static.d.ts +15 -1
- package/dist/server/static.d.ts.map +1 -1
- package/dist/server/static.js +54 -4
- package/dist/server/static.js.map +1 -1
- package/dist/server/templates.d.ts +24 -0
- package/dist/server/templates.d.ts.map +1 -0
- package/dist/server/templates.js +54 -0
- package/dist/server/templates.js.map +1 -0
- package/dist/shared/types.d.ts +558 -66
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/types.js +19 -11
- package/dist/shared/types.js.map +1 -1
- package/dist/web/assets/index-DC65cskq.js +446 -0
- package/dist/web/assets/index-xtD-3FFa.css +32 -0
- package/dist/web/canvas/sess-boot/index.html +305 -0
- package/dist/web/index.html +4 -9
- package/package.json +8 -4
- package/dist/core/canvas-enrich.d.ts +0 -115
- package/dist/core/canvas-enrich.d.ts.map +0 -1
- package/dist/core/canvas-enrich.js +0 -186
- package/dist/core/canvas-enrich.js.map +0 -1
- package/dist/core/run-spend.d.ts +0 -55
- package/dist/core/run-spend.d.ts.map +0 -1
- package/dist/core/run-spend.js +0 -109
- package/dist/core/run-spend.js.map +0 -1
- package/dist/core/run-transactions.d.ts +0 -60
- package/dist/core/run-transactions.d.ts.map +0 -1
- package/dist/core/run-transactions.js +0 -129
- package/dist/core/run-transactions.js.map +0 -1
- package/dist/profiles/canvas-guidelines.d.ts +0 -20
- package/dist/profiles/canvas-guidelines.d.ts.map +0 -1
- package/dist/profiles/canvas-guidelines.js +0 -48
- package/dist/profiles/canvas-guidelines.js.map +0 -1
- package/dist/server/skills.d.ts +0 -51
- package/dist/server/skills.d.ts.map +0 -1
- package/dist/server/skills.js +0 -312
- package/dist/server/skills.js.map +0 -1
- package/dist/web/assets/index-DM84J83Y.js +0 -234
- package/dist/web/assets/index-DemtWW4L.css +0 -32
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The "portable continue" brief: a bounded markdown reconstruction of a prior
|
|
3
|
+
* session, rendered from the SessionRecord our own events already produce (see
|
|
4
|
+
* core/session-record.ts).
|
|
5
|
+
*
|
|
6
|
+
* Why this exists rather than a vendor resume: `claude --resume` / `codex
|
|
7
|
+
* resume` only work when that agent, on this machine, still holds that
|
|
8
|
+
* conversation. H1 (SAP-2057) made us stop pretending otherwise; this is the
|
|
9
|
+
* other half — for every row where the agent does NOT hold it, start a fresh
|
|
10
|
+
* session seeded with what we recorded. Because the input is our normalized
|
|
11
|
+
* record and the output is plain text, the same code path serves claude-code
|
|
12
|
+
* (`--append-system-prompt`) and codex (`developer_instructions`), and would
|
|
13
|
+
* serve a harness with no prompt flag at all via post-ready injection.
|
|
14
|
+
*
|
|
15
|
+
* HONESTY IS THE PRODUCT HERE. A brief that reads like restored memory is
|
|
16
|
+
* worse than no brief: the agent will assert a file's contents or a command's
|
|
17
|
+
* outcome it never saw. So the header says outright that this is a
|
|
18
|
+
* reconstruction, `record.limitations` are spelled out, and every omission
|
|
19
|
+
* (dropped turns, clamped text, capped file lists) is stated in the text
|
|
20
|
+
* rather than silently applied.
|
|
21
|
+
*
|
|
22
|
+
* BUDGET. The brief is capped at `maxTokens` (default
|
|
23
|
+
* {@link RESUME_BRIEF_DEFAULT_MAX_TOKENS}), estimated at
|
|
24
|
+
* {@link CHARS_PER_TOKEN} characters per token — deliberately a crude
|
|
25
|
+
* estimator, since the consumer is a system prompt with room to spare, not a
|
|
26
|
+
* hard API limit, and a tokenizer dependency here would have to be right for
|
|
27
|
+
* two different vendors' tokenizers anyway. What gets squeezed, in order:
|
|
28
|
+
*
|
|
29
|
+
* 1. turns, oldest-first — the newest are the ones that explain what is in
|
|
30
|
+
* flight right now, and an old turn is the most verbose thing here per
|
|
31
|
+
* unit of usefulness;
|
|
32
|
+
* 2. the derived digests (files written, commands run). They go AFTER turns,
|
|
33
|
+
* not before, because they are hard-capped ({@link MAX_FILES} /
|
|
34
|
+
* {@link MAX_COMMANDS}) at a few hundred tokens for a whole session,
|
|
35
|
+
* where each turn costs a comparable amount on its own — trading a
|
|
36
|
+
* whole-session view for one more ancient turn is a bad deal. They can
|
|
37
|
+
* also be recovered by looking at the repository, so this only bites
|
|
38
|
+
* once every turn is already gone;
|
|
39
|
+
* 3. only then the rolling summary, tail-clamped. It is squeezed last
|
|
40
|
+
* because it is the one section that is a lossy fold of turns already
|
|
41
|
+
* dropped in step 1: cutting it loses history nothing else carries.
|
|
42
|
+
*
|
|
43
|
+
* The honesty header and the identity block are never dropped — a brief
|
|
44
|
+
* without them is a lie, not a shorter brief — so they act as a floor of
|
|
45
|
+
* roughly {@link RESUME_BRIEF_MIN_TOKENS} tokens that a smaller `maxTokens`
|
|
46
|
+
* cannot squeeze below.
|
|
47
|
+
*/
|
|
48
|
+
import * as path from "node:path";
|
|
49
|
+
/** Default token ceiling for a brief — see the module header on the estimate. */
|
|
50
|
+
export const RESUME_BRIEF_DEFAULT_MAX_TOKENS = 6000;
|
|
51
|
+
/** Turns considered before budgeting; the budget may keep fewer, never more. */
|
|
52
|
+
export const RESUME_BRIEF_DEFAULT_MAX_TURNS = 12;
|
|
53
|
+
/**
|
|
54
|
+
* Characters per token. Roughly right for English prose and code across both
|
|
55
|
+
* vendors' tokenizers, and biased the safe way: real text averages slightly
|
|
56
|
+
* more than 4 chars/token, so this over-counts and lands under the ceiling.
|
|
57
|
+
*/
|
|
58
|
+
export const CHARS_PER_TOKEN = 4;
|
|
59
|
+
/**
|
|
60
|
+
* Floor the budget can never squeeze below — the honesty header plus the
|
|
61
|
+
* identity block. Approximate (it moves with the header's wording); it exists
|
|
62
|
+
* so callers can reason about "what does maxTokens: 200 even mean" rather than
|
|
63
|
+
* as an assertion about exact size.
|
|
64
|
+
*/
|
|
65
|
+
export const RESUME_BRIEF_MIN_TOKENS = 400;
|
|
66
|
+
/** Per-turn clamps, so one pathological prompt can't consume the whole budget. */
|
|
67
|
+
const MAX_PROMPT_CHARS = 1200;
|
|
68
|
+
const MAX_ASSISTANT_CHARS = 1200;
|
|
69
|
+
/** How much of a tool call's target (path or command) to show. */
|
|
70
|
+
const MAX_TOOL_TARGET_CHARS = 80;
|
|
71
|
+
/** Tool calls listed per turn before the rest are counted rather than named. */
|
|
72
|
+
const MAX_TOOLS_PER_TURN = 12;
|
|
73
|
+
/** Entries in the derived digests. */
|
|
74
|
+
const MAX_FILES = 30;
|
|
75
|
+
const MAX_COMMANDS = 10;
|
|
76
|
+
const MAX_COMMAND_CHARS = 120;
|
|
77
|
+
/** The marker `truncateForPayload` (core/collector/normalizer.ts) leaves. */
|
|
78
|
+
const CLAMP_MARKER = "…[truncated]";
|
|
79
|
+
/** The crude char-count estimate the budget is enforced against. */
|
|
80
|
+
export function estimateBriefTokens(text) {
|
|
81
|
+
return Math.ceil(text.length / CHARS_PER_TOKEN);
|
|
82
|
+
}
|
|
83
|
+
/** Clamp `text` to `maxChars`, marking the cut so nothing reads as complete
|
|
84
|
+
* when it isn't. */
|
|
85
|
+
function clamp(text, maxChars) {
|
|
86
|
+
const trimmed = text.trim();
|
|
87
|
+
if (trimmed.length <= maxChars)
|
|
88
|
+
return trimmed;
|
|
89
|
+
return `${trimmed.slice(0, maxChars).trimEnd()}${CLAMP_MARKER}`;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The tool's target as a human would name it: the file it edited, the command
|
|
93
|
+
* it ran, or — failing both — a clamped slice of the raw input.
|
|
94
|
+
*
|
|
95
|
+
* `toolInput` is a string on the wire for both harnesses: claude-code's
|
|
96
|
+
* normalizer stores `JSON.stringify(tool_input)` and codex's tailer stores the
|
|
97
|
+
* function call's raw `arguments`, so a JSON parse covers both and a plain
|
|
98
|
+
* string (an untruncated free-text argument) falls through to the raw slice.
|
|
99
|
+
* Never throws on malformed input — a brief must render from whatever was
|
|
100
|
+
* recorded, including a payload the collector truncated mid-JSON.
|
|
101
|
+
*/
|
|
102
|
+
export function describeToolTarget(call, cwd) {
|
|
103
|
+
const parsed = parseToolInput(call.input);
|
|
104
|
+
if (parsed) {
|
|
105
|
+
const filePath = firstString(parsed, ["file_path", "filePath", "notebook_path", "path"]);
|
|
106
|
+
if (filePath)
|
|
107
|
+
return clamp(displayPath(filePath, cwd), MAX_TOOL_TARGET_CHARS);
|
|
108
|
+
const command = readCommand(parsed);
|
|
109
|
+
if (command)
|
|
110
|
+
return clamp(command, MAX_TOOL_TARGET_CHARS);
|
|
111
|
+
}
|
|
112
|
+
if (!call.input)
|
|
113
|
+
return null;
|
|
114
|
+
// No structure to read — show a slice of what was recorded rather than
|
|
115
|
+
// nothing, on one line so a turn's tool list stays scannable.
|
|
116
|
+
return clamp(call.input.replace(/\s+/g, " "), MAX_TOOL_TARGET_CHARS);
|
|
117
|
+
}
|
|
118
|
+
function parseToolInput(input) {
|
|
119
|
+
if (!input)
|
|
120
|
+
return null;
|
|
121
|
+
try {
|
|
122
|
+
const parsed = JSON.parse(input);
|
|
123
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)
|
|
124
|
+
? parsed
|
|
125
|
+
: null;
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function firstString(source, keys) {
|
|
132
|
+
for (const key of keys) {
|
|
133
|
+
const value = source[key];
|
|
134
|
+
if (typeof value === "string" && value.length > 0)
|
|
135
|
+
return value;
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
/** claude-code's Bash carries `command` as a string; codex's shell tool carries
|
|
140
|
+
* it as an argv array (`["bash", "-lc", "…"]`). Both read as one line here. */
|
|
141
|
+
function readCommand(source) {
|
|
142
|
+
const command = source.command;
|
|
143
|
+
if (typeof command === "string" && command.length > 0)
|
|
144
|
+
return command.replace(/\s+/g, " ").trim();
|
|
145
|
+
if (Array.isArray(command)) {
|
|
146
|
+
const joined = command.filter((part) => typeof part === "string").join(" ").trim();
|
|
147
|
+
return joined.length > 0 ? joined.replace(/\s+/g, " ") : null;
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
/** Paths under the session's cwd read far better relative; anything else (or a
|
|
152
|
+
* record with no recorded cwd) stays absolute so it's never ambiguous. */
|
|
153
|
+
function displayPath(filePath, cwd) {
|
|
154
|
+
if (!cwd || !path.isAbsolute(filePath))
|
|
155
|
+
return filePath;
|
|
156
|
+
const relative = path.relative(cwd, filePath);
|
|
157
|
+
return relative && !relative.startsWith("..") ? relative : filePath;
|
|
158
|
+
}
|
|
159
|
+
/** Tools whose input names a file the session wrote to. Read-only tools are
|
|
160
|
+
* deliberately excluded: "files touched" that includes everything the agent
|
|
161
|
+
* merely looked at stops being a signal. */
|
|
162
|
+
const WRITE_TOOL_NAMES = new Set([
|
|
163
|
+
"write",
|
|
164
|
+
"edit",
|
|
165
|
+
"multiedit",
|
|
166
|
+
"notebookedit",
|
|
167
|
+
"update",
|
|
168
|
+
"apply_patch",
|
|
169
|
+
"str_replace_editor",
|
|
170
|
+
]);
|
|
171
|
+
const SHELL_TOOL_NAMES = new Set(["bash", "shell", "run_terminal_cmd", "local_shell"]);
|
|
172
|
+
/**
|
|
173
|
+
* Files touched and commands run, derived from `tool.call` inputs. Exported
|
|
174
|
+
* for tests and because the same derivation is worth reusing anywhere that
|
|
175
|
+
* needs "what did this session actually do to the tree".
|
|
176
|
+
*/
|
|
177
|
+
export function deriveDigests(record) {
|
|
178
|
+
const fileCounts = new Map();
|
|
179
|
+
const commands = [];
|
|
180
|
+
const seenCommands = new Set();
|
|
181
|
+
for (const turn of record.turns) {
|
|
182
|
+
for (const call of turn.toolCalls) {
|
|
183
|
+
const name = (call.name ?? "").toLowerCase();
|
|
184
|
+
const parsed = parseToolInput(call.input);
|
|
185
|
+
if (WRITE_TOOL_NAMES.has(name)) {
|
|
186
|
+
const filePath = parsed
|
|
187
|
+
? firstString(parsed, ["file_path", "filePath", "notebook_path", "path"])
|
|
188
|
+
: null;
|
|
189
|
+
if (filePath) {
|
|
190
|
+
const display = displayPath(filePath, record.cwd);
|
|
191
|
+
fileCounts.set(display, (fileCounts.get(display) ?? 0) + 1);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
else if (SHELL_TOOL_NAMES.has(name)) {
|
|
195
|
+
const command = parsed ? readCommand(parsed) : null;
|
|
196
|
+
if (command && !seenCommands.has(command)) {
|
|
197
|
+
seenCommands.add(command);
|
|
198
|
+
commands.push(clamp(command, MAX_COMMAND_CHARS));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// Insertion order is chronological; both digests read newest-first, which is
|
|
204
|
+
// what a reader scanning for "where did this leave off" wants.
|
|
205
|
+
const files = [...fileCounts.entries()].reverse().map(([display, count]) => ({ display, count }));
|
|
206
|
+
return {
|
|
207
|
+
files: files.slice(0, MAX_FILES),
|
|
208
|
+
totalFiles: files.length,
|
|
209
|
+
commands: commands.reverse().slice(0, MAX_COMMANDS),
|
|
210
|
+
totalCommands: commands.length,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
/** Plain-language wording for each machine-readable gap on the record.
|
|
214
|
+
* Addressed to the agent reading the brief, so it says what to do about the
|
|
215
|
+
* gap rather than merely naming it — the web view (web/src/lib/
|
|
216
|
+
* session-record-view.ts) words the same codes for a human reader. */
|
|
217
|
+
const LIMITATION_PROSE = {
|
|
218
|
+
"truncated-tool-output": "Tool output was size-capped when recorded. Any command result quoted below may be cut off — re-run it rather than trusting the excerpt.",
|
|
219
|
+
"assistant-narration-gap": "Only the final assistant message of each turn was recorded. Whatever was said between tool calls — including reasoning for a change — is missing.",
|
|
220
|
+
"missing-assistant-text": "At least one turn has no assistant text at all (Codex records none). Those turns show prompts and tool calls only.",
|
|
221
|
+
"incomplete-final-turn": "The last turn never completed — the session ended mid-turn, so its work may be half-applied.",
|
|
222
|
+
"compacted-archive": "This brief was built from the session's archived copy, whose tool inputs and results are clipped to keep it bounded. The conversation is whole, but any payload quoted below is an excerpt — re-read the file or re-run the command rather than trusting it.",
|
|
223
|
+
"dropped-early-turns": "The archived copy had room only for the most recent turns; earlier ones are gone. Treat what follows as the tail of a longer session — ask before assuming why something earlier was done.",
|
|
224
|
+
};
|
|
225
|
+
function renderTurn(turn, cwd) {
|
|
226
|
+
const lines = [];
|
|
227
|
+
const when = turn.promptAt ?? turn.completedAt;
|
|
228
|
+
lines.push(`### Turn ${turn.index}${when ? ` · ${when}` : ""}${turn.incomplete ? " · never completed" : ""}`);
|
|
229
|
+
if (turn.prompt !== null) {
|
|
230
|
+
lines.push("", "**Prompt:**", "", blockquote(clamp(turn.prompt, MAX_PROMPT_CHARS)));
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
lines.push("", "**Prompt:** _not recorded — our recording started mid-turn, or the agent started this turn itself._");
|
|
234
|
+
}
|
|
235
|
+
if (turn.toolCalls.length > 0) {
|
|
236
|
+
const shown = turn.toolCalls.slice(0, MAX_TOOLS_PER_TURN).map((call) => {
|
|
237
|
+
const target = describeToolTarget(call, cwd);
|
|
238
|
+
return `\`${call.name ?? "unknown tool"}\`${target ? ` ${target}` : ""}`;
|
|
239
|
+
});
|
|
240
|
+
const overflow = turn.toolCalls.length - shown.length;
|
|
241
|
+
lines.push("", `**Tools:** ${shown.join(" · ")}${overflow > 0 ? ` · …and ${overflow} more` : ""}`);
|
|
242
|
+
}
|
|
243
|
+
if (turn.assistantText !== null) {
|
|
244
|
+
lines.push("", "**Reply:**", "", blockquote(clamp(turn.assistantText, MAX_ASSISTANT_CHARS)));
|
|
245
|
+
}
|
|
246
|
+
else if (!turn.incomplete) {
|
|
247
|
+
lines.push("", "**Reply:** _not recorded._");
|
|
248
|
+
}
|
|
249
|
+
return lines.join("\n");
|
|
250
|
+
}
|
|
251
|
+
function blockquote(text) {
|
|
252
|
+
return text
|
|
253
|
+
.split("\n")
|
|
254
|
+
.map((line) => `> ${line}`)
|
|
255
|
+
.join("\n");
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* The fixed preamble. Never dropped by the budget: an unlabelled
|
|
259
|
+
* reconstruction is the failure mode this whole feature exists to avoid.
|
|
260
|
+
*/
|
|
261
|
+
const HONESTY_HEADER = `# Continuing a prior session — reconstruction, not restored context
|
|
262
|
+
|
|
263
|
+
You are a **fresh session**. The agent that ran the session described below is
|
|
264
|
+
gone and none of its context is loaded. What follows was assembled by the
|
|
265
|
+
Sapiom Harness from its own recorded events — it is a briefing about that
|
|
266
|
+
session, not a replay of it, and it is partial by construction.
|
|
267
|
+
|
|
268
|
+
Treat every claim here as something a colleague told you, not as something you
|
|
269
|
+
remember. Before you rely on any of it — what a file contains, what a command
|
|
270
|
+
returned, whether a task was finished — check the current state of the
|
|
271
|
+
repository yourself. If the brief and the working tree disagree, the working
|
|
272
|
+
tree is right.`;
|
|
273
|
+
/**
|
|
274
|
+
* Render a bounded brief for `record`. Pure and synchronous; the caller
|
|
275
|
+
* supplies the rolling summary and any registry-only context (title, branch,
|
|
276
|
+
* bound workflow) it can resolve.
|
|
277
|
+
*/
|
|
278
|
+
export function buildResumeBrief(record, options = {}) {
|
|
279
|
+
const maxTokens = options.maxTokens ?? RESUME_BRIEF_DEFAULT_MAX_TOKENS;
|
|
280
|
+
const maxTurns = options.maxTurns ?? RESUME_BRIEF_DEFAULT_MAX_TURNS;
|
|
281
|
+
const budgetChars = Math.max(0, maxTokens) * CHARS_PER_TOKEN;
|
|
282
|
+
const identity = renderIdentity(record, options);
|
|
283
|
+
// The floor: these two are unconditional, so everything else is budgeted
|
|
284
|
+
// against whatever remains after them.
|
|
285
|
+
const fixed = `${HONESTY_HEADER}\n\n${identity}`;
|
|
286
|
+
const digests = deriveDigests(record);
|
|
287
|
+
const renderedTurns = (maxTurns > 0 ? record.turns.slice(-maxTurns) : []).map((turn) => renderTurn(turn, record.cwd));
|
|
288
|
+
// Mutable knobs, squeezed in the order the module header documents: the
|
|
289
|
+
// derived digests first (recoverable from the tree), then turns oldest-first,
|
|
290
|
+
// then — only if the summary alone still overflows — the summary itself.
|
|
291
|
+
let summaryText = options.summary?.trim() ?? "";
|
|
292
|
+
let keepFiles = true;
|
|
293
|
+
let keepCommands = true;
|
|
294
|
+
let keptTurns = renderedTurns.length;
|
|
295
|
+
const assemble = () => {
|
|
296
|
+
const parts = [fixed];
|
|
297
|
+
const summarySection = renderSummary(summaryText);
|
|
298
|
+
if (summarySection)
|
|
299
|
+
parts.push(summarySection);
|
|
300
|
+
const dropped = [];
|
|
301
|
+
const filesSection = keepFiles ? renderFiles(digests) : null;
|
|
302
|
+
if (filesSection)
|
|
303
|
+
parts.push(filesSection);
|
|
304
|
+
else if (!keepFiles && digests.files.length > 0)
|
|
305
|
+
dropped.push("files written");
|
|
306
|
+
const commandsSection = keepCommands ? renderCommands(digests) : null;
|
|
307
|
+
if (commandsSection)
|
|
308
|
+
parts.push(commandsSection);
|
|
309
|
+
else if (!keepCommands && digests.commands.length > 0)
|
|
310
|
+
dropped.push("commands run");
|
|
311
|
+
if (record.turns.length > 0) {
|
|
312
|
+
const heading = ["## Recent turns"];
|
|
313
|
+
const omitted = record.turns.length - keptTurns;
|
|
314
|
+
if (omitted > 0) {
|
|
315
|
+
heading.push("", `_The earliest ${omitted} of ${record.turns.length} recorded turns are omitted here to fit the context budget._`);
|
|
316
|
+
}
|
|
317
|
+
if (keptTurns === 0)
|
|
318
|
+
heading.push("", "_No turns fit the context budget._");
|
|
319
|
+
parts.push(heading.join("\n"));
|
|
320
|
+
parts.push(...renderedTurns.slice(renderedTurns.length - keptTurns));
|
|
321
|
+
}
|
|
322
|
+
if (dropped.length > 0) {
|
|
323
|
+
parts.push(`_Also omitted to fit the context budget: ${dropped.join(", ")}._`);
|
|
324
|
+
}
|
|
325
|
+
return parts.join("\n\n").trimEnd() + "\n";
|
|
326
|
+
};
|
|
327
|
+
const over = () => assemble().length > budgetChars;
|
|
328
|
+
// 1. Turns, oldest-first.
|
|
329
|
+
while (keptTurns > 0 && over())
|
|
330
|
+
keptTurns -= 1;
|
|
331
|
+
// 2. Derived digests, once no turn is left to give. Commands go first — the
|
|
332
|
+
// shorter list, and the one most cheaply reconstructed from the repo.
|
|
333
|
+
if (over())
|
|
334
|
+
keepCommands = false;
|
|
335
|
+
if (over())
|
|
336
|
+
keepFiles = false;
|
|
337
|
+
// 3. Last resort. Only reachable when the fixed preamble plus the summary
|
|
338
|
+
// alone overflow, which means either the summary was produced outside its
|
|
339
|
+
// own ≤500-word contract or `maxTokens` is below the preamble floor. Each
|
|
340
|
+
// pass strictly shortens `summaryText`, so this terminates.
|
|
341
|
+
while (summaryText.length > 0 && over()) {
|
|
342
|
+
const overflow = assemble().length - budgetChars;
|
|
343
|
+
summaryText = clamp(summaryText, Math.max(0, summaryText.length - overflow - CLAMP_MARKER.length));
|
|
344
|
+
if (summaryText === CLAMP_MARKER)
|
|
345
|
+
summaryText = "";
|
|
346
|
+
}
|
|
347
|
+
return assemble();
|
|
348
|
+
}
|
|
349
|
+
function renderIdentity(record, options) {
|
|
350
|
+
const lines = ["## What the prior session was", ""];
|
|
351
|
+
if (options.title)
|
|
352
|
+
lines.push(`- **Title:** ${options.title}`);
|
|
353
|
+
lines.push(`- **Directory:** ${record.cwd ?? "not recorded"}`);
|
|
354
|
+
if (options.gitBranch)
|
|
355
|
+
lines.push(`- **Git branch:** ${options.gitBranch}`);
|
|
356
|
+
lines.push(`- **Agent:** ${record.harness}`);
|
|
357
|
+
if (options.workflow) {
|
|
358
|
+
const { name, path: workflowPath, definitionId } = options.workflow;
|
|
359
|
+
lines.push(`- **Bound workflow:** ${name} (\`${workflowPath}\`)${definitionId !== null ? ` — definition ${definitionId}` : " — not yet linked to a deployed definition"}`);
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
lines.push("- **Bound workflow:** none");
|
|
363
|
+
}
|
|
364
|
+
const span = [record.startedAt, record.endedAt].filter(Boolean).join(" → ");
|
|
365
|
+
if (span)
|
|
366
|
+
lines.push(`- **Ran:** ${span}`);
|
|
367
|
+
lines.push(`- **Recorded turns:** ${record.turnCount}`);
|
|
368
|
+
if (record.limitations.length > 0) {
|
|
369
|
+
lines.push("", "**Known gaps in this reconstruction:**", "");
|
|
370
|
+
for (const limitation of record.limitations)
|
|
371
|
+
lines.push(`- ${LIMITATION_PROSE[limitation]}`);
|
|
372
|
+
}
|
|
373
|
+
return lines.join("\n");
|
|
374
|
+
}
|
|
375
|
+
function renderSummary(summary) {
|
|
376
|
+
const trimmed = summary?.trim();
|
|
377
|
+
if (!trimmed)
|
|
378
|
+
return null;
|
|
379
|
+
return `## Rolling summary of the prior session\n\n${trimmed}`;
|
|
380
|
+
}
|
|
381
|
+
function renderFiles(digests) {
|
|
382
|
+
if (digests.files.length === 0)
|
|
383
|
+
return null;
|
|
384
|
+
const lines = ["## Files the prior session wrote to", "", "_Derived from recorded tool calls, newest first._", ""];
|
|
385
|
+
for (const file of digests.files) {
|
|
386
|
+
lines.push(`- \`${file.display}\`${file.count > 1 ? ` (${file.count} edits)` : ""}`);
|
|
387
|
+
}
|
|
388
|
+
if (digests.totalFiles > digests.files.length) {
|
|
389
|
+
lines.push(`- …and ${digests.totalFiles - digests.files.length} more`);
|
|
390
|
+
}
|
|
391
|
+
return lines.join("\n");
|
|
392
|
+
}
|
|
393
|
+
function renderCommands(digests) {
|
|
394
|
+
if (digests.commands.length === 0)
|
|
395
|
+
return null;
|
|
396
|
+
const lines = ["## Shell commands it ran", "", "_Distinct commands, newest first._", ""];
|
|
397
|
+
for (const command of digests.commands)
|
|
398
|
+
lines.push(`- \`${command}\``);
|
|
399
|
+
if (digests.totalCommands > digests.commands.length) {
|
|
400
|
+
lines.push(`- …and ${digests.totalCommands - digests.commands.length} more`);
|
|
401
|
+
}
|
|
402
|
+
return lines.join("\n");
|
|
403
|
+
}
|
|
404
|
+
//# sourceMappingURL=resume-brief.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resume-brief.js","sourceRoot":"","sources":["../../src/core/resume-brief.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rolling session summary — the thing that makes rehydration good rather than
|
|
3
|
+
* merely possible.
|
|
4
|
+
*
|
|
5
|
+
* A resume brief built from the last N turns alone tells a fresh session what
|
|
6
|
+
* just happened; it cannot tell it what the session was *for*. So, opt-in, we
|
|
7
|
+
* periodically fold the whole record down to a ≤500-word summary and cache it
|
|
8
|
+
* at `<generated>/<harnessSessionId>/summary.md`. The brief
|
|
9
|
+
* (core/resume-brief.ts) picks it up when it's there and degrades cleanly to
|
|
10
|
+
* last-N-turns when it isn't — which is the default, since this is
|
|
11
|
+
* setting-gated (`HarnessSettings.rollingSummary`).
|
|
12
|
+
*
|
|
13
|
+
* NEVER BLOCKS A TURN. Everything here is fire-and-forget off the ingest path:
|
|
14
|
+
* `noteEvent` returns synchronously, the fold runs as a headless one-shot
|
|
15
|
+
* `TaskManager` job (a separate process, a cheap model, `--max-turns 1` — the
|
|
16
|
+
* bounded shape `HarnessAdapter.launchTask` exists for), and every failure
|
|
17
|
+
* mode short-circuits to "no summary". A user's session must never be slower,
|
|
18
|
+
* or fail, because a summary didn't happen.
|
|
19
|
+
*
|
|
20
|
+
* The task does not write the file itself. It is asked for the summary text
|
|
21
|
+
* and nothing else; we write `summary.md` from its `resultText` when it
|
|
22
|
+
* completes. Two reasons: the target lives under `<generated>/`, outside the
|
|
23
|
+
* session's cwd — which codex's `workspace-write` sandbox forbids and
|
|
24
|
+
* claude-code's `acceptEdits` shouldn't be asked to reach — and a task that
|
|
25
|
+
* writes its own output can half-write it, where a task that returns text
|
|
26
|
+
* either produces a whole summary or none.
|
|
27
|
+
*
|
|
28
|
+
* WHERE IT DOESN'T WORK: `launchTask` is optional on the adapter contract and
|
|
29
|
+
* codex has no non-interactive mode wired for it, so a codex session simply
|
|
30
|
+
* never produces a summary and its briefs are last-N-turns. That is a real
|
|
31
|
+
* gap, stated rather than papered over; the brief is honest either way.
|
|
32
|
+
*/
|
|
33
|
+
import type { AnalyticsEvent, BackgroundTask, HarnessKind, SessionRecord } from "../shared/types.js";
|
|
34
|
+
/** Filename under `<generated>/<harnessSessionId>/`. */
|
|
35
|
+
export declare const ROLLING_SUMMARY_FILE = "summary.md";
|
|
36
|
+
/** Completed turns that must accumulate before a re-fold is worth its cost. */
|
|
37
|
+
export declare const ROLLING_SUMMARY_TURN_INTERVAL = 10;
|
|
38
|
+
/** The contract stated in the prompt, and the clamp applied to what comes back. */
|
|
39
|
+
export declare const ROLLING_SUMMARY_MAX_WORDS = 500;
|
|
40
|
+
/**
|
|
41
|
+
* Model alias for the fold. A summary of text we already hold is the cheapest
|
|
42
|
+
* possible LLM job — pinning a small model here keeps an opt-in background
|
|
43
|
+
* feature from quietly costing what the user's own session does.
|
|
44
|
+
*/
|
|
45
|
+
export declare const ROLLING_SUMMARY_MODEL = "haiku";
|
|
46
|
+
/** Hard turn cap: one turn in, one summary out. */
|
|
47
|
+
export declare const ROLLING_SUMMARY_MAX_TURNS = 1;
|
|
48
|
+
/** Macro id the task is registered under — also TaskManager's dedupe key, so
|
|
49
|
+
* a session can never have two folds in flight at once. */
|
|
50
|
+
export declare const ROLLING_SUMMARY_MACRO_ID = "rolling-summary";
|
|
51
|
+
/**
|
|
52
|
+
* Token budget for the material handed to the fold. Larger than a brief's
|
|
53
|
+
* (this is throwaway input to a cheap model, not a system prompt competing
|
|
54
|
+
* with the user's own context) but still bounded, so a marathon session can't
|
|
55
|
+
* turn one fold into a huge request.
|
|
56
|
+
*/
|
|
57
|
+
export declare const ROLLING_SUMMARY_INPUT_MAX_TOKENS = 20000;
|
|
58
|
+
/** Absolute path of a session's cached summary. */
|
|
59
|
+
export declare function rollingSummaryPath(generatedRoot: string, harnessSessionId: string): string;
|
|
60
|
+
/**
|
|
61
|
+
* The cached summary for a session, or null when there is none — never throws.
|
|
62
|
+
* A brief that fails to find a summary is a brief without one, not an error.
|
|
63
|
+
*/
|
|
64
|
+
export declare function readRollingSummary(generatedRoot: string, harnessSessionId: string): Promise<string | null>;
|
|
65
|
+
/** Trim `text` to at most `maxWords` words, marking the cut. */
|
|
66
|
+
export declare function clampToWords(text: string, maxWords?: number): string;
|
|
67
|
+
/**
|
|
68
|
+
* The one-shot prompt handed to `launchTask`. The material is the record
|
|
69
|
+
* rendered by {@link buildResumeBrief} at a larger budget — deliberately the
|
|
70
|
+
* same renderer the brief uses, so the fold reads exactly the shape a future
|
|
71
|
+
* brief will present, and there is one place where "what a record looks like
|
|
72
|
+
* as text" is defined.
|
|
73
|
+
*
|
|
74
|
+
* `previous` is folded back in rather than discarded: the record itself is
|
|
75
|
+
* capped at the input budget, so on a long session the earliest turns are
|
|
76
|
+
* already gone from the material, and the last summary is the only surviving
|
|
77
|
+
* account of them.
|
|
78
|
+
*/
|
|
79
|
+
export declare function buildRollingSummaryPrompt(record: SessionRecord, previous: string | null): string;
|
|
80
|
+
/** What the summarizer needs from the outside world. All injectable, so the
|
|
81
|
+
* unit tests drive it without a filesystem or a spawned process. */
|
|
82
|
+
export interface RollingSummarizerDeps {
|
|
83
|
+
/** Root the per-session generated dirs live under. */
|
|
84
|
+
generatedRoot: string;
|
|
85
|
+
/** The `HarnessSettings.rollingSummary` gate, re-read per fold so toggling
|
|
86
|
+
* the setting takes effect without a restart. Never throws. */
|
|
87
|
+
enabled: () => Promise<boolean>;
|
|
88
|
+
/** Reads the reconstructed record for a session (SessionRecordReader.read). */
|
|
89
|
+
readRecord: (harnessSessionId: string) => Promise<SessionRecord | null>;
|
|
90
|
+
/** The live session, for the harness kind and cwd the task runs as. Returns
|
|
91
|
+
* undefined once the session is gone from the registry. */
|
|
92
|
+
getSession: (harnessSessionId: string) => {
|
|
93
|
+
harness: HarnessKind;
|
|
94
|
+
cwd: string;
|
|
95
|
+
} | undefined;
|
|
96
|
+
/** TaskManager.run, narrowed to what this needs. */
|
|
97
|
+
runTask: (req: {
|
|
98
|
+
macroId: string;
|
|
99
|
+
label: string;
|
|
100
|
+
harnessSessionId: string;
|
|
101
|
+
harness: HarnessKind;
|
|
102
|
+
cwd: string;
|
|
103
|
+
prompt: string;
|
|
104
|
+
model: string;
|
|
105
|
+
maxTurns: number;
|
|
106
|
+
}) => Promise<BackgroundTask>;
|
|
107
|
+
/** Completed turns between folds. Default {@link ROLLING_SUMMARY_TURN_INTERVAL}. */
|
|
108
|
+
turnInterval?: number;
|
|
109
|
+
/** Diagnostics sink. Defaults to console.error — a fold failing is worth a
|
|
110
|
+
* log line and nothing more. */
|
|
111
|
+
onError?: (err: unknown) => void;
|
|
112
|
+
}
|
|
113
|
+
export interface RollingSummarizer {
|
|
114
|
+
/**
|
|
115
|
+
* Feed one normalized analytics event. Synchronous and total: it counts
|
|
116
|
+
* `turn.completed`, triggers a fold on the interval and on `session.end`,
|
|
117
|
+
* and ignores everything else. Any work it starts is detached.
|
|
118
|
+
*/
|
|
119
|
+
noteEvent(event: AnalyticsEvent): void;
|
|
120
|
+
/**
|
|
121
|
+
* Feed a task status change (TaskManager.onStatusChange). A completed fold
|
|
122
|
+
* task's `resultText` is what becomes `summary.md`.
|
|
123
|
+
*/
|
|
124
|
+
noteTaskStatus(task: BackgroundTask): void;
|
|
125
|
+
/** Resolves when no fold this summarizer started is still in flight. For
|
|
126
|
+
* tests and for shutdown; production callers never need to await it. */
|
|
127
|
+
idle(): Promise<void>;
|
|
128
|
+
}
|
|
129
|
+
export declare function createRollingSummarizer(deps: RollingSummarizerDeps): RollingSummarizer;
|
|
130
|
+
//# sourceMappingURL=rolling-summary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rolling-summary.d.ts","sourceRoot":"","sources":["../../src/core/rolling-summary.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGrG,wDAAwD;AACxD,eAAO,MAAM,oBAAoB,eAAe,CAAC;AACjD,+EAA+E;AAC/E,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAChD,mFAAmF;AACnF,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAC7C;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,UAAU,CAAC;AAC7C,mDAAmD;AACnD,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAC3C;4DAC4D;AAC5D,eAAO,MAAM,wBAAwB,oBAAoB,CAAC;AAC1D;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,QAAS,CAAC;AAEvD,mDAAmD;AACnD,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAE1F;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,aAAa,EAAE,MAAM,EACrB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQxB;AAED,gEAAgE;AAChE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAkC,GAAG,MAAM,CAI/F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CA+BhG;AAED;qEACqE;AACrE,MAAM,WAAW,qBAAqB;IACpC,sDAAsD;IACtD,aAAa,EAAE,MAAM,CAAC;IACtB;oEACgE;IAChE,OAAO,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAChC,+EAA+E;IAC/E,UAAU,EAAE,CAAC,gBAAgB,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IACxE;gEAC4D;IAC5D,UAAU,EAAE,CAAC,gBAAgB,EAAE,MAAM,KAAK;QAAE,OAAO,EAAE,WAAW,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IAC5F,oDAAoD;IACpD,OAAO,EAAE,CAAC,GAAG,EAAE;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,gBAAgB,EAAE,MAAM,CAAC;QACzB,OAAO,EAAE,WAAW,CAAC;QACrB,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;KAClB,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAC9B,oFAAoF;IACpF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;qCACiC;IACjC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAAC;IACvC;;;OAGG;IACH,cAAc,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;IAC3C;6EACyE;IACzE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,qBAAqB,GAAG,iBAAiB,CAgGtF"}
|