@agimon-ai/po-autocompact 0.0.0-alpha.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 +21 -0
- package/README.md +114 -0
- package/dist/apiContracts.cjs +17 -0
- package/dist/apiContracts.cjs.map +1 -0
- package/dist/apiContracts.d.mts +53 -0
- package/dist/apiContracts.d.mts.map +1 -0
- package/dist/apiContracts.mjs +13 -0
- package/dist/apiContracts.mjs.map +1 -0
- package/dist/compactionPolicy-BIJJHcBP.cjs +459 -0
- package/dist/compactionPolicy-BIJJHcBP.cjs.map +1 -0
- package/dist/compactionPolicy-CDCYTj2b.mjs +328 -0
- package/dist/compactionPolicy-CDCYTj2b.mjs.map +1 -0
- package/dist/extensions/server.cjs +855 -0
- package/dist/extensions/server.cjs.map +1 -0
- package/dist/extensions/server.d.mts +5 -0
- package/dist/extensions/server.d.mts.map +1 -0
- package/dist/extensions/server.mjs +850 -0
- package/dist/extensions/server.mjs.map +1 -0
- package/dist/extensions/web.mjs +161 -0
- package/dist/extensions/web.mjs.map +1 -0
- package/dist/index.cjs +3 -0
- package/dist/index.d.mts +11 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2 -0
- package/package.json +111 -0
|
@@ -0,0 +1,850 @@
|
|
|
1
|
+
import { S as STATE_CUSTOM_TYPE, _ as withCanonicalFileSections, a as createInitialState, b as RESUME_MESSAGE_TYPE, c as fileDetailsFromUnknown, d as latestCheckpointArtifactEntry, f as mergeFileDetails, h as projectContextMessages, i as contextDetailsFromUnknown, l as fileOperationsFromMessages, m as parseState, n as checkpointRequestDetails, o as createRequestId, p as parseCheckpointDecision, r as checkpointSummaryFromEntry, s as effectiveThresholds, t as baselineUsageIsSettled, u as isStructuredCheckpoint, v as CHECKPOINT_MESSAGE_TYPE, x as RUNTIME_STATE_MESSAGE_TYPE, y as CONTEXT_MESSAGE_TYPE } from "../compactionPolicy-CDCYTj2b.mjs";
|
|
2
|
+
import { composeServerProviderOverrides, defineServerPlugin } from "@agimon-ai/po-core/serverFacet";
|
|
3
|
+
import { composeRootHooks, defineHook, defineRoot } from "@agimon-ai/po-core/extensionFile";
|
|
4
|
+
import { DOOM_CONTEXT_CONTRIBUTIONS_SERVICE, requireDoomContextContributions } from "@agimon-ai/po-core/contextContributions";
|
|
5
|
+
import { DOOM_UI_HUB_SERVICE, requireDoomUiHub } from "@agimon-ai/po-core/uiHub";
|
|
6
|
+
import { loadDoomConfig } from "@agimon-ai/po-config";
|
|
7
|
+
import { createDoomTelemetry } from "@agimon-ai/po-telemetry";
|
|
8
|
+
import { minimatch } from "minimatch";
|
|
9
|
+
const FOOTER_SOURCE = "doom-autocompact";
|
|
10
|
+
const STATUS_KEY = "doom-autocompact";
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/services/autocompactRuntime/index.ts
|
|
13
|
+
function errorMessage(error) {
|
|
14
|
+
return error instanceof Error ? error.message : String(error);
|
|
15
|
+
}
|
|
16
|
+
function latestPlanSnapshot(branch) {
|
|
17
|
+
for (const entry of branch.toReversed()) {
|
|
18
|
+
if (entry.type !== "custom" || entry.customType !== "agent-harness-plan-document") continue;
|
|
19
|
+
if (!entry.data || typeof entry.data !== "object") return void 0;
|
|
20
|
+
const data = entry.data;
|
|
21
|
+
if (typeof data.content !== "string" || typeof data.path !== "string") return void 0;
|
|
22
|
+
return {
|
|
23
|
+
content: data.content,
|
|
24
|
+
path: data.path
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const EMPTY_CONTEXT_CONTRIBUTIONS = Object.freeze({
|
|
29
|
+
entries: Object.freeze([]),
|
|
30
|
+
errors: Object.freeze([])
|
|
31
|
+
});
|
|
32
|
+
function readContextContributionsSnapshot(service) {
|
|
33
|
+
if (!service) return EMPTY_CONTEXT_CONTRIBUTIONS;
|
|
34
|
+
try {
|
|
35
|
+
return service.snapshot();
|
|
36
|
+
} catch (error) {
|
|
37
|
+
return {
|
|
38
|
+
entries: [],
|
|
39
|
+
errors: [{
|
|
40
|
+
source: DOOM_CONTEXT_CONTRIBUTIONS_SERVICE,
|
|
41
|
+
id: "service-snapshot",
|
|
42
|
+
label: "Context contributions",
|
|
43
|
+
order: 0,
|
|
44
|
+
message: errorMessage(error)
|
|
45
|
+
}]
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function formatContextContributions(snapshot) {
|
|
50
|
+
const sections = snapshot.entries.map((entry) => `### ${entry.label}\n${entry.text}`);
|
|
51
|
+
sections.push(...snapshot.errors.map((error) => `### ${error.label}\n(unavailable: ${error.message})`));
|
|
52
|
+
return sections.length > 0 ? sections.join("\n\n") : "(no registered context contributions)";
|
|
53
|
+
}
|
|
54
|
+
function runtimeStateSnapshot(branch, contextContributions) {
|
|
55
|
+
const plan = latestPlanSnapshot(branch);
|
|
56
|
+
return {
|
|
57
|
+
...plan ? { planPath: plan.path } : {},
|
|
58
|
+
contributions: contextContributions.entries,
|
|
59
|
+
contributionErrors: contextContributions.errors
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function formatRuntimeState(snapshot) {
|
|
63
|
+
return `<compaction-runtime-state>
|
|
64
|
+
Authoritative runtime state captured when compaction committed. Use it to resume coordination without reconstructing state from the summary.
|
|
65
|
+
<active-plan>
|
|
66
|
+
${snapshot.planPath ? `Source: ${snapshot.planPath}\nExact plan content is restored separately by Doom Plan.` : "(no active plan document)"}
|
|
67
|
+
</active-plan>
|
|
68
|
+
<context-contributions>
|
|
69
|
+
${formatContextContributions({
|
|
70
|
+
entries: snapshot.contributions,
|
|
71
|
+
errors: snapshot.contributionErrors
|
|
72
|
+
})}
|
|
73
|
+
</context-contributions>
|
|
74
|
+
</compaction-runtime-state>`;
|
|
75
|
+
}
|
|
76
|
+
function steeringEnvelope(snapshot) {
|
|
77
|
+
const plan = snapshot.plan ? `Source: ${snapshot.plan.path}\n\n${snapshot.plan.content}` : "(no active plan document)";
|
|
78
|
+
const importantContext = [snapshot.readFiles.length > 0 ? `Read files: ${snapshot.readFiles.join(", ")}` : void 0, snapshot.modifiedFiles.length > 0 ? `Modified files: ${snapshot.modifiedFiles.join(", ")}` : void 0].filter((value) => Boolean(value));
|
|
79
|
+
return `<compaction-steering priority="user,plan,context-contributions,important-context,recent-messages">
|
|
80
|
+
<user-prompt source="conversation">
|
|
81
|
+
Treat the user's explicit goals, constraints, corrections, and preferences in the conversation as authoritative. Newer user instructions override older ones.
|
|
82
|
+
</user-prompt>
|
|
83
|
+
<active-plan>
|
|
84
|
+
${plan}
|
|
85
|
+
</active-plan>
|
|
86
|
+
<context-contributions>
|
|
87
|
+
${formatContextContributions(snapshot.contextContributions)}
|
|
88
|
+
</context-contributions>
|
|
89
|
+
<important-context>
|
|
90
|
+
${importantContext.length > 0 ? importantContext.join("\n") : "(none recorded outside the conversation)"}
|
|
91
|
+
</important-context>
|
|
92
|
+
</compaction-steering>`;
|
|
93
|
+
}
|
|
94
|
+
function checkpointInstructions(pass, steering) {
|
|
95
|
+
const decision = pass === 2 ? "Start with exactly <shouldCompact>true</shouldCompact> or <shouldCompact>false</shouldCompact>. Use true when the staged summary safely retains the important parent context. Use false only when it is not yet sufficient for safe compaction.\n\n" : "";
|
|
96
|
+
return `Create Doom autocompact checkpoint pass ${pass}.
|
|
97
|
+
|
|
98
|
+
${steeringEnvelope(steering)}
|
|
99
|
+
|
|
100
|
+
Apply the steering evidence in priority order. Do not invent plan or contributed runtime state. Preserve verified facts and mark unresolved inference explicitly. Newer evidence replaces superseded information.
|
|
101
|
+
|
|
102
|
+
${decision}Return only the decision tag when required, followed by a concise structured checkpoint using every heading below. Keep exact goals, constraints, decisions, blockers, commands, errors, file paths, validation evidence, and the immediate next action. Separate verified facts from unresolved inference.
|
|
103
|
+
|
|
104
|
+
## Goal
|
|
105
|
+
## Constraints & Preferences
|
|
106
|
+
## Progress
|
|
107
|
+
### Done
|
|
108
|
+
### In Progress
|
|
109
|
+
### Blocked
|
|
110
|
+
## Key Decisions
|
|
111
|
+
## Next Steps
|
|
112
|
+
## Critical Context`;
|
|
113
|
+
}
|
|
114
|
+
const DEFAULT_RUNTIME_CONFIG = {
|
|
115
|
+
enabled: true,
|
|
116
|
+
ratios: {},
|
|
117
|
+
overrides: []
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* What the Doom config says about this package.
|
|
121
|
+
*
|
|
122
|
+
* `modes.autocompact` is read first and `modes.planning.subagents` is the
|
|
123
|
+
* fallback, because summarization borrowed the planning subagent model before
|
|
124
|
+
* it had keys of its own and existing files still spell it that way.
|
|
125
|
+
*/
|
|
126
|
+
function autocompactRuntimeConfig(root, onLoadFailure) {
|
|
127
|
+
let modes;
|
|
128
|
+
try {
|
|
129
|
+
modes = loadDoomConfig(root).modes;
|
|
130
|
+
} catch (error) {
|
|
131
|
+
process.emitWarning("Doom autocompact configuration could not be loaded; using the active session model instead.");
|
|
132
|
+
onLoadFailure?.(error);
|
|
133
|
+
return DEFAULT_RUNTIME_CONFIG;
|
|
134
|
+
}
|
|
135
|
+
const configured = modes?.autocompact;
|
|
136
|
+
const agent = (configured && (configured.model !== void 0 || configured.thinking !== void 0) ? {
|
|
137
|
+
...configured.model ? { model: configured.model } : {},
|
|
138
|
+
...configured.thinking ? { thinking: configured.thinking } : {}
|
|
139
|
+
} : void 0) ?? modes?.planning?.subagents;
|
|
140
|
+
const thresholds = configured?.thresholds;
|
|
141
|
+
return {
|
|
142
|
+
enabled: configured?.enabled ?? true,
|
|
143
|
+
...agent ? { agent } : {},
|
|
144
|
+
ratios: {
|
|
145
|
+
...thresholds?.pass1 === void 0 ? {} : { 1: thresholds.pass1 },
|
|
146
|
+
...thresholds?.pass2 === void 0 ? {} : { 2: thresholds.pass2 },
|
|
147
|
+
...thresholds?.pass3 === void 0 ? {} : { 3: thresholds.pass3 }
|
|
148
|
+
},
|
|
149
|
+
overrides: configured?.overrides ?? []
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
//#endregion
|
|
153
|
+
//#region src/services/autocompactTelemetry/index.ts
|
|
154
|
+
const SERVICE_NAME = "doom-autocompact";
|
|
155
|
+
const PACKAGE_NAME = "@agimon-ai/po-autocompact";
|
|
156
|
+
const AUTOCOMPACT_EVENT = {
|
|
157
|
+
checkpointStarted: "doom_autocompact.checkpoint_started",
|
|
158
|
+
checkpointCompleted: "doom_autocompact.checkpoint_completed",
|
|
159
|
+
checkpointFailed: "doom_autocompact.checkpoint_failed",
|
|
160
|
+
checkpointInvalid: "doom_autocompact.checkpoint_invalid",
|
|
161
|
+
configurationLoadFailed: "doom_autocompact.configuration_load_failed",
|
|
162
|
+
contextContributionDegraded: "doom_autocompact.context_contribution_degraded",
|
|
163
|
+
contextCommitted: "doom_autocompact.context_committed",
|
|
164
|
+
contextApplied: "doom_autocompact.context_applied",
|
|
165
|
+
contextMarkerInvalid: "doom_autocompact.context_marker_invalid",
|
|
166
|
+
contextHookSlow: "doom_autocompact.context_hook_slow",
|
|
167
|
+
nativeCompactionCompleted: "doom_autocompact.native_compaction_completed"
|
|
168
|
+
};
|
|
169
|
+
function createAutocompactTelemetry(options = {}) {
|
|
170
|
+
const telemetry = createDoomTelemetry({
|
|
171
|
+
serviceName: SERVICE_NAME,
|
|
172
|
+
packageName: PACKAGE_NAME,
|
|
173
|
+
cwd: options.cwd,
|
|
174
|
+
workspaceRoot: options.workspaceRoot,
|
|
175
|
+
env: options.env,
|
|
176
|
+
telemetryFactory: options.telemetryFactory,
|
|
177
|
+
warn: options.warn,
|
|
178
|
+
enableLogs: true,
|
|
179
|
+
enableTraces: true
|
|
180
|
+
});
|
|
181
|
+
return {
|
|
182
|
+
recordError: (event, error, attributes) => telemetry.recordError(event, error, attributes),
|
|
183
|
+
recordWarning: (event, error, attributes) => telemetry.recordWarning(event, error, attributes),
|
|
184
|
+
recordEvent: (event, attributes) => telemetry.recordEvent(event, attributes),
|
|
185
|
+
runInSpan: (name, attributes, callback) => telemetry.runInSpan(name, attributes, callback),
|
|
186
|
+
flush: () => telemetry.flush(),
|
|
187
|
+
shutdown: () => telemetry.shutdown()
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
//#endregion
|
|
191
|
+
//#region src/services/modelOverrides/index.ts
|
|
192
|
+
/**
|
|
193
|
+
* A pattern naming a provider is matched against `provider/id`, anything else
|
|
194
|
+
* against the bare id. `*` does not cross `/`, so a bare pattern tested against a
|
|
195
|
+
* qualified subject would never match and would read as a silently dead rule.
|
|
196
|
+
*/
|
|
197
|
+
function matchesModelPattern(pattern, model) {
|
|
198
|
+
const subject = pattern.includes("/") ? `${model.provider}/${model.id}` : model.id;
|
|
199
|
+
return minimatch(subject, pattern, { nocase: true });
|
|
200
|
+
}
|
|
201
|
+
function resolveModelTokenOverrides(model, overrides) {
|
|
202
|
+
if (!model) return {};
|
|
203
|
+
const matched = overrides.find((entry) => matchesModelPattern(entry.model, model));
|
|
204
|
+
if (!matched) return {};
|
|
205
|
+
const { pass1, pass2, pass3 } = matched.tokens;
|
|
206
|
+
return {
|
|
207
|
+
...pass1 === void 0 ? {} : { 1: pass1 },
|
|
208
|
+
...pass2 === void 0 ? {} : { 2: pass2 },
|
|
209
|
+
...pass3 === void 0 ? {} : { 3: pass3 }
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
//#endregion
|
|
213
|
+
//#region src/services/summarizationModel/index.ts
|
|
214
|
+
const THINKING_SUFFIX_PATTERN = /:(off|minimal|low|medium|high|xhigh|max)$/;
|
|
215
|
+
/** Splits a `provider/model` reference, tolerating the optional thinking suffix. */
|
|
216
|
+
function parseModelReference(reference) {
|
|
217
|
+
const trimmed = reference.trim();
|
|
218
|
+
const thinking = THINKING_SUFFIX_PATTERN.exec(trimmed)?.[1];
|
|
219
|
+
const identity = trimmed.replace(THINKING_SUFFIX_PATTERN, "");
|
|
220
|
+
const separator = identity.indexOf("/");
|
|
221
|
+
if (separator <= 0 || separator >= identity.length - 1) return void 0;
|
|
222
|
+
return {
|
|
223
|
+
provider: identity.slice(0, separator),
|
|
224
|
+
modelId: identity.slice(separator + 1),
|
|
225
|
+
...thinking ? { thinking } : {}
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
//#endregion
|
|
229
|
+
//#region src/services/nativeAutocompact/index.ts
|
|
230
|
+
/** Exact canonical-prefix comparison refuses to apply summaries to edited or replaced evidence. */
|
|
231
|
+
function tailAfter(snapshot, current) {
|
|
232
|
+
if (snapshot.length > current.length || snapshot.some((message, index) => JSON.stringify(message) !== JSON.stringify(current[index]))) return void 0;
|
|
233
|
+
return current.slice(snapshot.length);
|
|
234
|
+
}
|
|
235
|
+
function leaf(entries) {
|
|
236
|
+
return entries.findLast((entry) => entry.type !== "custom" || entry.customType !== "doom-autocompact-state" && entry.customType !== "doom-autocompact-checkpoint")?.id ?? "root";
|
|
237
|
+
}
|
|
238
|
+
function createNativeAutocompact(dependencies = {}) {
|
|
239
|
+
const telemetry = dependencies.telemetry ?? createAutocompactTelemetry();
|
|
240
|
+
let active = true;
|
|
241
|
+
let generation = 0;
|
|
242
|
+
let state = createInitialState();
|
|
243
|
+
let snapshot;
|
|
244
|
+
let previousSnapshot;
|
|
245
|
+
let context;
|
|
246
|
+
let contributions;
|
|
247
|
+
let controller;
|
|
248
|
+
let serial = Promise.resolve();
|
|
249
|
+
const work = /* @__PURE__ */ new Set();
|
|
250
|
+
const applied = /* @__PURE__ */ new Set();
|
|
251
|
+
const invalid = /* @__PURE__ */ new Set();
|
|
252
|
+
let configFailureReported = false;
|
|
253
|
+
let footer;
|
|
254
|
+
let navigating = false;
|
|
255
|
+
let lastNativeCompactionId;
|
|
256
|
+
let checkpointBoundary;
|
|
257
|
+
const attributes = () => ({
|
|
258
|
+
"pi.session.id": context?.sessionId ?? "",
|
|
259
|
+
"autocompact.cycle": state.cycle,
|
|
260
|
+
"autocompact.pass": state.pass,
|
|
261
|
+
...state.requestId ? { "autocompact.request.id": state.requestId } : {},
|
|
262
|
+
...state.snapshotLeafId ? { "autocompact.snapshot_leaf_id": state.snapshotLeafId } : {},
|
|
263
|
+
...state.snapshotTokens === void 0 ? {} : { "autocompact.tokens_before": state.snapshotTokens }
|
|
264
|
+
});
|
|
265
|
+
const configured = (execution) => autocompactRuntimeConfig(execution.repoRoot, (error) => {
|
|
266
|
+
if (!configFailureReported) {
|
|
267
|
+
configFailureReported = true;
|
|
268
|
+
telemetry.recordWarning(AUTOCOMPACT_EVENT.configurationLoadFailed, error, attributes());
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
const alive = (version) => active && !navigating && generation === version;
|
|
272
|
+
const refreshStatus = (execution) => {
|
|
273
|
+
const phase = state.phase === "checkpoint_pending" ? "summarizing" : state.phase === "checkpoint_ready" ? "applying" : void 0;
|
|
274
|
+
footer?.update(phase ? {
|
|
275
|
+
fullText: `Compacting p${state.pass}`,
|
|
276
|
+
compactText: `C${state.pass}`
|
|
277
|
+
} : void 0);
|
|
278
|
+
execution.client.setStatus(STATUS_KEY, phase ? `Compacting pass ${state.pass} (${phase})…` : void 0);
|
|
279
|
+
};
|
|
280
|
+
const enqueue = (callback) => {
|
|
281
|
+
const version = generation;
|
|
282
|
+
const operation = serial.then(async () => {
|
|
283
|
+
if (alive(version)) await callback();
|
|
284
|
+
});
|
|
285
|
+
serial = operation.catch(() => void 0);
|
|
286
|
+
return operation;
|
|
287
|
+
};
|
|
288
|
+
const persist = async (execution, version) => {
|
|
289
|
+
if (!alive(version)) return;
|
|
290
|
+
await execution.session.appendCustomEntry(STATE_CUSTOM_TYPE, {
|
|
291
|
+
...state,
|
|
292
|
+
nativeSnapshot: snapshot,
|
|
293
|
+
nativePreviousSnapshot: previousSnapshot,
|
|
294
|
+
nativeLastCompactionId: lastNativeCompactionId
|
|
295
|
+
});
|
|
296
|
+
if (alive(version)) refreshStatus(execution);
|
|
297
|
+
};
|
|
298
|
+
const clear = () => {
|
|
299
|
+
delete state.requestId;
|
|
300
|
+
delete state.snapshotLeafId;
|
|
301
|
+
delete state.snapshotTokens;
|
|
302
|
+
delete state.pendingCheckpoint;
|
|
303
|
+
delete state.compactionPass;
|
|
304
|
+
snapshot = void 0;
|
|
305
|
+
};
|
|
306
|
+
const fail = async (execution, version, error, malformed = false) => {
|
|
307
|
+
if (!alive(version)) return;
|
|
308
|
+
const eventAttributes = attributes();
|
|
309
|
+
const attempts = state.invalidAttempts + 1;
|
|
310
|
+
const pass = state.pass;
|
|
311
|
+
const failedLeaf = state.snapshotLeafId;
|
|
312
|
+
state.invalidAttempts += 1;
|
|
313
|
+
if (state.invalidAttempts >= 3) {
|
|
314
|
+
state.checkpointQueue = state.checkpointQueue.filter((item) => item !== pass);
|
|
315
|
+
state.exhaustedPasses.push(pass);
|
|
316
|
+
state.invalidAttempts = 0;
|
|
317
|
+
}
|
|
318
|
+
state.phase = "waiting";
|
|
319
|
+
clear();
|
|
320
|
+
state.lastAttemptLeafId = failedLeaf;
|
|
321
|
+
await persist(execution, version);
|
|
322
|
+
if (!alive(version)) return;
|
|
323
|
+
await execution.client.notify({
|
|
324
|
+
body: `Doom autocompact summarization pass ${pass} failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
325
|
+
level: "error"
|
|
326
|
+
});
|
|
327
|
+
await telemetry.recordError(malformed ? AUTOCOMPACT_EVENT.checkpointInvalid : AUTOCOMPACT_EVENT.checkpointFailed, error, {
|
|
328
|
+
...eventAttributes,
|
|
329
|
+
"autocompact.checkpoint.attempts": attempts,
|
|
330
|
+
"autocompact.checkpoint.exhausted": attempts >= 3
|
|
331
|
+
});
|
|
332
|
+
};
|
|
333
|
+
const branch = async (execution) => await execution.session.entries();
|
|
334
|
+
const canonical = async (execution, entries) => {
|
|
335
|
+
if (!execution.session.contextMessages) throw new Error("Canonical context is unavailable for autocompact.");
|
|
336
|
+
return projectContextMessages(await execution.session.contextMessages(), entries).messages;
|
|
337
|
+
};
|
|
338
|
+
const contributionSnapshot = async (execution) => {
|
|
339
|
+
const result = readContextContributionsSnapshot(contributions);
|
|
340
|
+
for (const error of result.errors) {
|
|
341
|
+
await execution.client.notify({
|
|
342
|
+
body: `Doom autocompact context contribution degraded (${error.source}/${error.id}): ${error.message}`,
|
|
343
|
+
level: "warning"
|
|
344
|
+
});
|
|
345
|
+
await telemetry.recordWarning(AUTOCOMPACT_EVENT.contextContributionDegraded, new Error(error.message), {
|
|
346
|
+
...attributes(),
|
|
347
|
+
"autocompact.context_contribution.source": error.source,
|
|
348
|
+
"autocompact.context_contribution.id": error.id,
|
|
349
|
+
"autocompact.context_contribution.order": error.order
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
return result;
|
|
353
|
+
};
|
|
354
|
+
const runtimeState = async (execution, entries, version, appendMessage = checkpointBoundary?.appendMessage.bind(checkpointBoundary)) => {
|
|
355
|
+
const saved = runtimeStateSnapshot(entries, await contributionSnapshot(execution));
|
|
356
|
+
if (!alive(version)) return;
|
|
357
|
+
await appendMessage?.({
|
|
358
|
+
type: RUNTIME_STATE_MESSAGE_TYPE,
|
|
359
|
+
text: formatRuntimeState(saved),
|
|
360
|
+
display: false,
|
|
361
|
+
details: {
|
|
362
|
+
version: 2,
|
|
363
|
+
...saved
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
return {
|
|
367
|
+
"autocompact.runtime.plan_present": Boolean(saved.planPath),
|
|
368
|
+
"autocompact.runtime.contribution_count": saved.contributions.length,
|
|
369
|
+
"autocompact.runtime.contribution_error_count": saved.contributionErrors.length
|
|
370
|
+
};
|
|
371
|
+
};
|
|
372
|
+
const resetCycle = () => {
|
|
373
|
+
state.phase = "waiting";
|
|
374
|
+
state.checkpointQueue = [];
|
|
375
|
+
state.exhaustedPasses = [];
|
|
376
|
+
state.invalidAttempts = 0;
|
|
377
|
+
state.baselinePending = true;
|
|
378
|
+
state.cycle += 1;
|
|
379
|
+
state.pass = 1;
|
|
380
|
+
delete state.lastAttemptLeafId;
|
|
381
|
+
delete state.latestCheckpointSnapshotLeafId;
|
|
382
|
+
clear();
|
|
383
|
+
previousSnapshot = void 0;
|
|
384
|
+
};
|
|
385
|
+
const applyReady = async (execution, version) => {
|
|
386
|
+
if (state.phase !== "checkpoint_ready" || !state.pendingCheckpoint || !state.requestId || !state.snapshotLeafId) return false;
|
|
387
|
+
const decision = parseCheckpointDecision(state.pendingCheckpoint);
|
|
388
|
+
if (!isStructuredCheckpoint(decision.summary)) {
|
|
389
|
+
await fail(execution, version, /* @__PURE__ */ new Error("Summarization returned an incomplete checkpoint."), true);
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
const entries = await branch(execution);
|
|
393
|
+
const messages = await canonical(execution, entries);
|
|
394
|
+
if (!alive(version)) return false;
|
|
395
|
+
const tail = snapshot && tailAfter(snapshot, messages);
|
|
396
|
+
if (!tail) {
|
|
397
|
+
await fail(execution, version, /* @__PURE__ */ new Error("Canonical checkpoint evidence changed; summary must be regenerated."));
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
const pass = state.pass;
|
|
401
|
+
if (pass === 1 || pass === 2 && decision.shouldCompact !== true) {
|
|
402
|
+
await execution.session.appendCustomEntry(CHECKPOINT_MESSAGE_TYPE, {
|
|
403
|
+
...checkpointRequestDetails(state.cycle, pass, state.requestId),
|
|
404
|
+
summary: decision.summary
|
|
405
|
+
});
|
|
406
|
+
if (!alive(version)) return false;
|
|
407
|
+
previousSnapshot = snapshot;
|
|
408
|
+
state.latestCheckpointSnapshotLeafId = state.snapshotLeafId;
|
|
409
|
+
state.checkpointQueue = state.checkpointQueue.filter((item) => item !== pass);
|
|
410
|
+
state.pass = pass === 1 ? 2 : 3;
|
|
411
|
+
state.phase = "waiting";
|
|
412
|
+
state.invalidAttempts = 0;
|
|
413
|
+
clear();
|
|
414
|
+
await persist(execution, version);
|
|
415
|
+
return false;
|
|
416
|
+
}
|
|
417
|
+
if (!execution.session.appendMessage) throw new Error("Checkpoint messages are unavailable.");
|
|
418
|
+
const files = mergeFileDetails(state, fileOperationsFromMessages(messages));
|
|
419
|
+
const details = {
|
|
420
|
+
...files,
|
|
421
|
+
doomAutocompact: {
|
|
422
|
+
version: 2,
|
|
423
|
+
cycle: state.cycle,
|
|
424
|
+
pass,
|
|
425
|
+
requestId: state.requestId,
|
|
426
|
+
snapshotLeafId: state.snapshotLeafId,
|
|
427
|
+
tokensBefore: state.snapshotTokens ?? 0
|
|
428
|
+
},
|
|
429
|
+
retainedMessages: tail
|
|
430
|
+
};
|
|
431
|
+
const idle = (await execution.session.activity()).isIdle;
|
|
432
|
+
if (!alive(version)) return false;
|
|
433
|
+
const id = await execution.session.appendMessage({
|
|
434
|
+
type: CONTEXT_MESSAGE_TYPE,
|
|
435
|
+
text: withCanonicalFileSections(decision.summary, files),
|
|
436
|
+
display: false,
|
|
437
|
+
details
|
|
438
|
+
});
|
|
439
|
+
if (!alive(version) || !id) return false;
|
|
440
|
+
await execution.session.setLabel?.(id, `autocompact:c${state.cycle}:p${pass}`);
|
|
441
|
+
if (!alive(version)) return false;
|
|
442
|
+
const runtimeAttributes = await runtimeState(execution, entries, version, execution.session.appendMessage.bind(execution.session));
|
|
443
|
+
if (!alive(version)) return false;
|
|
444
|
+
const committedAttributes = attributes();
|
|
445
|
+
state.readFiles = files.readFiles;
|
|
446
|
+
state.modifiedFiles = files.modifiedFiles;
|
|
447
|
+
resetCycle();
|
|
448
|
+
await persist(execution, version);
|
|
449
|
+
if (!alive(version)) return false;
|
|
450
|
+
await telemetry.recordEvent(AUTOCOMPACT_EVENT.contextCommitted, {
|
|
451
|
+
...committedAttributes,
|
|
452
|
+
"autocompact.message_count.before": messages.length,
|
|
453
|
+
"autocompact.message_count.retained": tail.length,
|
|
454
|
+
"autocompact.apply_mode": idle ? "idle" : "turn_end",
|
|
455
|
+
...runtimeAttributes
|
|
456
|
+
});
|
|
457
|
+
if (idle && alive(version)) await execution.session.admitPrompt?.("Asynchronous compaction completed. Continue from the compacted checkpoint's immediate next action without repeating completed work.", "steer", void 0, `${RESUME_MESSAGE_TYPE}:${details.doomAutocompact.requestId}`);
|
|
458
|
+
return true;
|
|
459
|
+
};
|
|
460
|
+
const progress = async (execution, allowBusy = false, appendMessage = checkpointBoundary?.appendMessage.bind(checkpointBoundary)) => {
|
|
461
|
+
const version = generation;
|
|
462
|
+
context = execution;
|
|
463
|
+
const activity = await execution.session.activity();
|
|
464
|
+
if (!alive(version) || activity.hasPendingMessages) return;
|
|
465
|
+
if (appendMessage && (activity.isIdle || allowBusy)) {
|
|
466
|
+
const checkpointExecution = {
|
|
467
|
+
...execution,
|
|
468
|
+
session: {
|
|
469
|
+
...execution.session,
|
|
470
|
+
appendMessage
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
if (await applyReady(checkpointExecution, version)) return;
|
|
474
|
+
}
|
|
475
|
+
if (!alive(version) || state.phase !== "waiting") return;
|
|
476
|
+
const configuration = configured(execution);
|
|
477
|
+
if (!configuration.enabled || !execution.session.contextUsage || !execution.textCompletion?.summarize) return;
|
|
478
|
+
const usage = await execution.session.contextUsage();
|
|
479
|
+
const entries = await branch(execution);
|
|
480
|
+
if (!alive(version) || !usage || usage.tokens === null) return;
|
|
481
|
+
if (state.baselinePending) {
|
|
482
|
+
if (!baselineUsageIsSettled(entries)) return;
|
|
483
|
+
state.baselineTokens = usage.tokens;
|
|
484
|
+
state.baselinePending = false;
|
|
485
|
+
await persist(execution, version);
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
const currentLeaf = leaf(entries);
|
|
489
|
+
if (currentLeaf === state.lastAttemptLeafId) return;
|
|
490
|
+
const thresholds = effectiveThresholds(usage.contextWindow, state.baselineTokens, configuration.ratios, resolveModelTokenOverrides(execution.model, configuration.overrides));
|
|
491
|
+
for (const pass of [
|
|
492
|
+
1,
|
|
493
|
+
2,
|
|
494
|
+
3
|
|
495
|
+
]) if (pass >= state.pass && !state.checkpointQueue.includes(pass) && !state.exhaustedPasses.includes(pass) && usage.tokens >= thresholds[pass]) state.checkpointQueue.push(pass);
|
|
496
|
+
const pass = state.checkpointQueue[0];
|
|
497
|
+
if (!pass) return;
|
|
498
|
+
const messages = await canonical(execution, entries);
|
|
499
|
+
const steering = await contributionSnapshot(execution);
|
|
500
|
+
if (!alive(version)) return;
|
|
501
|
+
snapshot = messages;
|
|
502
|
+
const previous = pass > 1 ? checkpointSummaryFromEntry(latestCheckpointArtifactEntry(entries)) : void 0;
|
|
503
|
+
const requestId = createRequestId(execution.sessionId, state.cycle, pass, currentLeaf);
|
|
504
|
+
state.phase = "checkpoint_pending";
|
|
505
|
+
state.pass = pass;
|
|
506
|
+
state.requestId = requestId;
|
|
507
|
+
state.snapshotLeafId = currentLeaf;
|
|
508
|
+
state.snapshotTokens = usage.tokens;
|
|
509
|
+
delete state.lastAttemptLeafId;
|
|
510
|
+
delete state.pendingCheckpoint;
|
|
511
|
+
await persist(execution, version);
|
|
512
|
+
if (!alive(version)) return;
|
|
513
|
+
controller?.abort();
|
|
514
|
+
controller = new AbortController();
|
|
515
|
+
const signal = controller.signal;
|
|
516
|
+
const ownReference = configuration.agent?.model ? parseModelReference(configuration.agent.model) : void 0;
|
|
517
|
+
const candidate = ownReference ? `${ownReference.provider}/${ownReference.modelId}` : void 0;
|
|
518
|
+
const reference = candidate && execution.textCompletion.available(candidate) ? candidate : execution.model ? `${execution.model.provider}/${execution.model.id}` : "";
|
|
519
|
+
const thinking = configuration.agent?.thinking ?? (reference === candidate ? ownReference?.thinking : void 0) ?? (await execution.session.readModelSettings?.())?.thinkingLevel;
|
|
520
|
+
if (!alive(version)) return;
|
|
521
|
+
const requestAttributes = {
|
|
522
|
+
...attributes(),
|
|
523
|
+
"autocompact.message_count.before": messages.length
|
|
524
|
+
};
|
|
525
|
+
const delta = previousSnapshot ? tailAfter(previousSnapshot, messages) : void 0;
|
|
526
|
+
const completion = telemetry.runInSpan("doom_autocompact.checkpoint", requestAttributes, async () => {
|
|
527
|
+
await telemetry.recordEvent(AUTOCOMPACT_EVENT.checkpointStarted, requestAttributes);
|
|
528
|
+
return execution.textCompletion.summarize(reference, {
|
|
529
|
+
messages: pass > 1 && delta ? delta : messages,
|
|
530
|
+
instructions: checkpointInstructions(pass, {
|
|
531
|
+
plan: latestPlanSnapshot(entries),
|
|
532
|
+
contextContributions: steering,
|
|
533
|
+
readFiles: state.readFiles,
|
|
534
|
+
modifiedFiles: state.modifiedFiles
|
|
535
|
+
}),
|
|
536
|
+
...previous ? { previousSummary: previous } : {},
|
|
537
|
+
...thinking ? { thinkingLevel: thinking } : {},
|
|
538
|
+
signal
|
|
539
|
+
});
|
|
540
|
+
}).then(async (summary) => {
|
|
541
|
+
await enqueue(async () => {
|
|
542
|
+
if (!alive(version) || signal.aborted || state.requestId !== requestId) return;
|
|
543
|
+
if (!summary.trim()) {
|
|
544
|
+
await fail(execution, version, /* @__PURE__ */ new Error("Summarization returned an empty checkpoint."));
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
state.phase = "checkpoint_ready";
|
|
548
|
+
state.pendingCheckpoint = summary.trim();
|
|
549
|
+
await persist(execution, version);
|
|
550
|
+
if (!alive(version)) return;
|
|
551
|
+
await telemetry.recordEvent(AUTOCOMPACT_EVENT.checkpointCompleted, {
|
|
552
|
+
...requestAttributes,
|
|
553
|
+
"autocompact.checkpoint.characters": summary.length
|
|
554
|
+
});
|
|
555
|
+
if ((await execution.session.activity()).isIdle && alive(version)) await progress(execution);
|
|
556
|
+
});
|
|
557
|
+
}).catch(async (error) => {
|
|
558
|
+
await enqueue(async () => {
|
|
559
|
+
if (alive(version) && state.requestId === requestId) await fail(execution, version, error);
|
|
560
|
+
});
|
|
561
|
+
});
|
|
562
|
+
work.add(completion);
|
|
563
|
+
completion.then(() => work.delete(completion), () => work.delete(completion));
|
|
564
|
+
};
|
|
565
|
+
const restore = async (execution) => {
|
|
566
|
+
if (!active) return;
|
|
567
|
+
if (!checkpointBoundary) {
|
|
568
|
+
if (!execution.session.registerCheckpointBoundary) throw new Error("Native checkpoint boundaries are unavailable.");
|
|
569
|
+
checkpointBoundary = execution.session.registerCheckpointBoundary(async (boundary) => {
|
|
570
|
+
if (!active || navigating || boundary.signal?.aborted) return;
|
|
571
|
+
const current = context ?? execution;
|
|
572
|
+
await enqueue(() => boundary.signal?.aborted ? Promise.resolve() : progress(current, true, boundary.appendMessage.bind(boundary)));
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
navigating = false;
|
|
576
|
+
generation += 1;
|
|
577
|
+
const version = generation;
|
|
578
|
+
controller?.abort(/* @__PURE__ */ new Error("Autocompact session generation changed."));
|
|
579
|
+
controller = void 0;
|
|
580
|
+
context = execution;
|
|
581
|
+
applied.clear();
|
|
582
|
+
invalid.clear();
|
|
583
|
+
const entries = await branch(execution);
|
|
584
|
+
if (!alive(version)) return;
|
|
585
|
+
const entry = entries.findLast((item) => item.type === "custom" && item.customType === "doom-autocompact-state");
|
|
586
|
+
const data = entry?.type === "custom" && entry.data && typeof entry.data === "object" ? entry.data : void 0;
|
|
587
|
+
state = parseState(data) ?? createInitialState();
|
|
588
|
+
lastNativeCompactionId = typeof data?.nativeLastCompactionId === "string" ? data.nativeLastCompactionId : void 0;
|
|
589
|
+
const validMessages = (value) => Array.isArray(value) && value.every((message) => message && typeof message === "object" && typeof message.role === "string");
|
|
590
|
+
snapshot = validMessages(data?.nativeSnapshot) ? data.nativeSnapshot : void 0;
|
|
591
|
+
previousSnapshot = validMessages(data?.nativePreviousSnapshot) ? data.nativePreviousSnapshot : void 0;
|
|
592
|
+
if (state.phase === "checkpoint_ready" && state.requestId) {
|
|
593
|
+
const artifacts = entries.slice(entry ? entries.indexOf(entry) + 1 : 0);
|
|
594
|
+
const committed = artifacts.findLast((item) => {
|
|
595
|
+
const message = item.type === "message" ? item.message : void 0;
|
|
596
|
+
if (message?.role !== "custom" || message.customType !== "doom-autocompact-context") return false;
|
|
597
|
+
const details = contextDetailsFromUnknown(message.details);
|
|
598
|
+
return details !== void 0 && details.doomAutocompact.requestId === state.requestId && details.doomAutocompact.cycle === state.cycle && details.doomAutocompact.pass === state.pass && details.doomAutocompact.snapshotLeafId === state.snapshotLeafId && isStructuredCheckpoint(checkpointSummaryFromEntry(item) ?? "");
|
|
599
|
+
});
|
|
600
|
+
if (committed?.type === "message" && committed.message.role === "custom") {
|
|
601
|
+
const details = contextDetailsFromUnknown(committed.message.details);
|
|
602
|
+
state.readFiles = details.readFiles;
|
|
603
|
+
state.modifiedFiles = details.modifiedFiles;
|
|
604
|
+
resetCycle();
|
|
605
|
+
await persist(execution, version);
|
|
606
|
+
} else if (artifacts.findLast((item) => {
|
|
607
|
+
if (item.type !== "custom" || item.customType !== "doom-autocompact-checkpoint") return false;
|
|
608
|
+
const details = item.data;
|
|
609
|
+
return details?.version === 2 && details.requestId === state.requestId && details.cycle === state.cycle && details.pass === state.pass && isStructuredCheckpoint(checkpointSummaryFromEntry(item) ?? "");
|
|
610
|
+
}) && (state.pass === 1 || state.pass === 2)) {
|
|
611
|
+
previousSnapshot = snapshot;
|
|
612
|
+
state.latestCheckpointSnapshotLeafId = state.snapshotLeafId;
|
|
613
|
+
state.checkpointQueue = state.checkpointQueue.filter((pass) => pass !== state.pass);
|
|
614
|
+
state.pass = state.pass === 1 ? 2 : 3;
|
|
615
|
+
state.phase = "waiting";
|
|
616
|
+
state.invalidAttempts = 0;
|
|
617
|
+
clear();
|
|
618
|
+
await persist(execution, version);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
if (state.phase !== "waiting" && state.phase !== "checkpoint_ready") {
|
|
622
|
+
state.phase = "waiting";
|
|
623
|
+
clear();
|
|
624
|
+
await persist(execution, version);
|
|
625
|
+
}
|
|
626
|
+
if (alive(version)) await enqueue(() => progress(execution));
|
|
627
|
+
};
|
|
628
|
+
return {
|
|
629
|
+
hooks: [
|
|
630
|
+
{
|
|
631
|
+
event: "session_start",
|
|
632
|
+
handle: (_event, execution) => restore(execution)
|
|
633
|
+
},
|
|
634
|
+
{
|
|
635
|
+
event: "session_tree",
|
|
636
|
+
handle: (event, execution) => {
|
|
637
|
+
if (!active) return;
|
|
638
|
+
if (event.status === "started") {
|
|
639
|
+
navigating = true;
|
|
640
|
+
generation += 1;
|
|
641
|
+
controller?.abort(/* @__PURE__ */ new Error("Autocompact navigation started."));
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
return restore(execution);
|
|
645
|
+
}
|
|
646
|
+
},
|
|
647
|
+
{
|
|
648
|
+
event: "turn_end",
|
|
649
|
+
handle: (_event, execution) => enqueue(() => progress(execution))
|
|
650
|
+
},
|
|
651
|
+
{
|
|
652
|
+
event: "agent_settled",
|
|
653
|
+
handle: (_event, execution) => enqueue(() => progress(execution))
|
|
654
|
+
},
|
|
655
|
+
{
|
|
656
|
+
event: "session_before_compact",
|
|
657
|
+
handle: (event, execution) => active && !navigating && configured(execution).enabled && event.reason === "threshold" && (state.phase === "checkpoint_pending" || state.phase === "checkpoint_ready" || state.baselinePending) ? { cancel: true } : void 0
|
|
658
|
+
},
|
|
659
|
+
{
|
|
660
|
+
event: "context",
|
|
661
|
+
async handle(event, execution) {
|
|
662
|
+
if (!active || navigating || !Array.isArray(event.messages)) return;
|
|
663
|
+
const started = Date.now();
|
|
664
|
+
try {
|
|
665
|
+
const version = generation;
|
|
666
|
+
const entries = await branch(execution);
|
|
667
|
+
if (!alive(version)) return;
|
|
668
|
+
const projection = projectContextMessages(event.messages, entries);
|
|
669
|
+
if (projection.invalidMarker) {
|
|
670
|
+
const id = leaf(entries);
|
|
671
|
+
if (!invalid.has(id)) {
|
|
672
|
+
invalid.add(id);
|
|
673
|
+
await telemetry.recordError(AUTOCOMPACT_EVENT.contextMarkerInvalid, /* @__PURE__ */ new Error("Doom autocompact context marker is malformed."), {
|
|
674
|
+
...attributes(),
|
|
675
|
+
"autocompact.leaf_id": id
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
if (!projection.marker) return;
|
|
681
|
+
if (!applied.has(projection.marker.requestId)) {
|
|
682
|
+
applied.add(projection.marker.requestId);
|
|
683
|
+
await telemetry.recordEvent(AUTOCOMPACT_EVENT.contextApplied, {
|
|
684
|
+
...attributes(),
|
|
685
|
+
"autocompact.request.id": projection.marker.requestId,
|
|
686
|
+
"autocompact.cycle": projection.marker.cycle,
|
|
687
|
+
"autocompact.pass": projection.marker.pass,
|
|
688
|
+
"autocompact.snapshot_leaf_id": projection.marker.snapshotLeafId,
|
|
689
|
+
"autocompact.tokens_before": projection.marker.tokensBefore,
|
|
690
|
+
"autocompact.message_count.before": event.messages.length,
|
|
691
|
+
"autocompact.message_count.after": projection.messages.length,
|
|
692
|
+
"autocompact.message_count.retained": projection.retainedMessageCount
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
return alive(version) ? { messages: projection.messages } : void 0;
|
|
696
|
+
} finally {
|
|
697
|
+
const hookMs = Date.now() - started;
|
|
698
|
+
if (active && hookMs >= 25) await telemetry.recordEvent(AUTOCOMPACT_EVENT.contextHookSlow, {
|
|
699
|
+
...attributes(),
|
|
700
|
+
hook_ms: hookMs,
|
|
701
|
+
"autocompact.message_count": event.messages.length
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
},
|
|
706
|
+
{
|
|
707
|
+
event: "session_compact",
|
|
708
|
+
handle: (event, execution) => {
|
|
709
|
+
if (!active || event.status !== "completed" || typeof event.entryId !== "string" || event.entryId === lastNativeCompactionId) return;
|
|
710
|
+
const observedGeneration = generation;
|
|
711
|
+
return enqueue(async () => {
|
|
712
|
+
const entries = await branch(execution);
|
|
713
|
+
const compacted = entries.findLast((entry) => entry.type === "compaction");
|
|
714
|
+
if (!alive(observedGeneration) || !compacted || compacted.id !== event.entryId || compacted.id === lastNativeCompactionId) return;
|
|
715
|
+
generation += 1;
|
|
716
|
+
const version = generation;
|
|
717
|
+
controller?.abort(/* @__PURE__ */ new Error("Native compaction superseded checkpoint."));
|
|
718
|
+
controller = void 0;
|
|
719
|
+
lastNativeCompactionId = compacted.id;
|
|
720
|
+
const files = mergeFileDetails(state, void 0, fileDetailsFromUnknown("details" in compacted ? compacted.details : void 0));
|
|
721
|
+
state.readFiles = files.readFiles;
|
|
722
|
+
state.modifiedFiles = files.modifiedFiles;
|
|
723
|
+
await execution.session.setLabel?.(compacted.id, `autocompact:c${state.cycle}:external`);
|
|
724
|
+
if (!alive(version)) return;
|
|
725
|
+
const runtimeAttributes = await runtimeState(execution, entries, version);
|
|
726
|
+
if (!alive(version)) return;
|
|
727
|
+
const nativeAttributes = attributes();
|
|
728
|
+
resetCycle();
|
|
729
|
+
await persist(execution, version);
|
|
730
|
+
if (alive(version)) await telemetry.recordEvent(AUTOCOMPACT_EVENT.nativeCompactionCompleted, {
|
|
731
|
+
...nativeAttributes,
|
|
732
|
+
"autocompact.native.reason": typeof event.reason === "string" ? event.reason : "unknown",
|
|
733
|
+
"autocompact.native.from_extension": "fromHook" in compacted && compacted.fromHook === true,
|
|
734
|
+
"autocompact.tokens_before": compacted.tokensBefore,
|
|
735
|
+
...runtimeAttributes
|
|
736
|
+
});
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
],
|
|
741
|
+
plugin(cordis) {
|
|
742
|
+
cordis.inject([DOOM_UI_HUB_SERVICE], (injected) => {
|
|
743
|
+
const contribution = requireDoomUiHub(injected).registerFooter({
|
|
744
|
+
source: FOOTER_SOURCE,
|
|
745
|
+
id: "autocompact",
|
|
746
|
+
order: 20
|
|
747
|
+
});
|
|
748
|
+
footer = contribution;
|
|
749
|
+
if (context && active) refreshStatus(context);
|
|
750
|
+
return () => {
|
|
751
|
+
contribution.dispose();
|
|
752
|
+
if (footer === contribution) footer = void 0;
|
|
753
|
+
};
|
|
754
|
+
});
|
|
755
|
+
cordis.inject([DOOM_CONTEXT_CONTRIBUTIONS_SERVICE], (injected) => {
|
|
756
|
+
const service = requireDoomContextContributions(injected);
|
|
757
|
+
contributions = service;
|
|
758
|
+
return () => {
|
|
759
|
+
if (contributions === service) contributions = void 0;
|
|
760
|
+
};
|
|
761
|
+
});
|
|
762
|
+
},
|
|
763
|
+
start: restore,
|
|
764
|
+
async stop() {
|
|
765
|
+
active = false;
|
|
766
|
+
checkpointBoundary?.dispose();
|
|
767
|
+
checkpointBoundary = void 0;
|
|
768
|
+
generation += 1;
|
|
769
|
+
controller?.abort(/* @__PURE__ */ new Error("Autocompact is shutting down."));
|
|
770
|
+
await Promise.allSettled([...work, serial]);
|
|
771
|
+
context?.client.setStatus(STATUS_KEY, void 0);
|
|
772
|
+
footer?.update(void 0);
|
|
773
|
+
footer = void 0;
|
|
774
|
+
context = void 0;
|
|
775
|
+
contributions = void 0;
|
|
776
|
+
await telemetry.shutdown();
|
|
777
|
+
}
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
//#endregion
|
|
781
|
+
//#region src/extensions/workspaces/sessions/(backend)/root.server.ts
|
|
782
|
+
var root_server_default = defineRoot(() => {
|
|
783
|
+
const runtime = createNativeAutocompact();
|
|
784
|
+
return {
|
|
785
|
+
value: runtime,
|
|
786
|
+
services: [runtime.plugin],
|
|
787
|
+
onDispose: runtime.stop
|
|
788
|
+
};
|
|
789
|
+
});
|
|
790
|
+
//#endregion
|
|
791
|
+
//#region src/extensions/workspaces/sessions/(backend)/hook/agent-settled.server.ts
|
|
792
|
+
var agent_settled_server_default = defineHook(({ root }) => root.hooks.find((hook) => hook.event === "agent_settled"));
|
|
793
|
+
//#endregion
|
|
794
|
+
//#region src/extensions/workspaces/sessions/(backend)/hook/context.server.ts
|
|
795
|
+
var context_server_default = defineHook(({ root }) => root.hooks.find((hook) => hook.event === "context"));
|
|
796
|
+
//#endregion
|
|
797
|
+
//#region src/extensions/workspaces/sessions/(backend)/hook/session-before-compact.server.ts
|
|
798
|
+
var session_before_compact_server_default = defineHook(({ root }) => root.hooks.find((hook) => hook.event === "session_before_compact"));
|
|
799
|
+
//#endregion
|
|
800
|
+
//#region src/extensions/workspaces/sessions/(backend)/hook/session-compact.server.ts
|
|
801
|
+
var session_compact_server_default = defineHook(({ root }) => root.hooks.find((hook) => hook.event === "session_compact"));
|
|
802
|
+
//#endregion
|
|
803
|
+
//#region src/extensions/workspaces/sessions/(backend)/hook/session-start.server.ts
|
|
804
|
+
var session_start_server_default = defineHook(({ root }) => root.hooks.find((hook) => hook.event === "session_start"));
|
|
805
|
+
//#endregion
|
|
806
|
+
//#region src/extensions/workspaces/sessions/(backend)/hook/session-tree.server.ts
|
|
807
|
+
var session_tree_server_default = defineHook(({ root }) => root.hooks.find((hook) => hook.event === "session_tree"));
|
|
808
|
+
//#endregion
|
|
809
|
+
//#region src/extensions/workspaces/sessions/(backend)/hook/turn-end.server.ts
|
|
810
|
+
var turn_end_server_default = defineHook(({ root }) => root.hooks.find((hook) => hook.event === "turn_end"));
|
|
811
|
+
//#endregion
|
|
812
|
+
//#region generated/server.ts
|
|
813
|
+
const at = (value, context) => typeof value === "function" ? value(context) : value;
|
|
814
|
+
const via = (identity, value) => value === void 0 ? void 0 : {
|
|
815
|
+
...identity,
|
|
816
|
+
...value
|
|
817
|
+
};
|
|
818
|
+
const facet = defineServerPlugin({
|
|
819
|
+
name: "@agimon-ai/po-autocompact",
|
|
820
|
+
providerOverrides: composeServerProviderOverrides(root_server_default),
|
|
821
|
+
session: async (context) => {
|
|
822
|
+
const scopeSession = await root_server_default(context);
|
|
823
|
+
const contextSession = {
|
|
824
|
+
...context,
|
|
825
|
+
root: scopeSession.value
|
|
826
|
+
};
|
|
827
|
+
return {
|
|
828
|
+
...composeRootHooks(scopeSession),
|
|
829
|
+
get hooks() {
|
|
830
|
+
return [
|
|
831
|
+
via({ event: "agent_settled" }, at(agent_settled_server_default, contextSession)),
|
|
832
|
+
via({ event: "context" }, at(context_server_default, contextSession)),
|
|
833
|
+
via({ event: "session_before_compact" }, at(session_before_compact_server_default, contextSession)),
|
|
834
|
+
via({ event: "session_compact" }, at(session_compact_server_default, contextSession)),
|
|
835
|
+
via({ event: "session_start" }, at(session_start_server_default, contextSession)),
|
|
836
|
+
via({ event: "session_tree" }, at(session_tree_server_default, contextSession)),
|
|
837
|
+
via({ event: "turn_end" }, at(turn_end_server_default, contextSession))
|
|
838
|
+
];
|
|
839
|
+
},
|
|
840
|
+
services: [...scopeSession.services ?? []],
|
|
841
|
+
get activities() {
|
|
842
|
+
return [...scopeSession.activities ?? []];
|
|
843
|
+
}
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
});
|
|
847
|
+
//#endregion
|
|
848
|
+
export { facet as default, facet };
|
|
849
|
+
|
|
850
|
+
//# sourceMappingURL=server.mjs.map
|