@namzu/sdk 62.10.0 → 62.11.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 +18 -0
- package/dist/bridge/a2a/mapper.d.ts.map +1 -1
- package/dist/bridge/a2a/mapper.js +1 -0
- package/dist/bridge/a2a/mapper.js.map +1 -1
- package/dist/bridge/sse/mapper.d.ts.map +1 -1
- package/dist/bridge/sse/mapper.js +9 -0
- package/dist/bridge/sse/mapper.js.map +1 -1
- package/dist/compaction/loaded-skills.d.ts +77 -0
- package/dist/compaction/loaded-skills.d.ts.map +1 -0
- package/dist/compaction/loaded-skills.js +203 -0
- package/dist/compaction/loaded-skills.js.map +1 -0
- package/dist/compaction/manager.d.ts +10 -0
- package/dist/compaction/manager.d.ts.map +1 -1
- package/dist/compaction/manager.js +20 -0
- package/dist/compaction/manager.js.map +1 -1
- package/dist/compaction/manual.d.ts.map +1 -1
- package/dist/compaction/manual.js +17 -3
- package/dist/compaction/manual.js.map +1 -1
- package/dist/compaction/restore.d.ts +5 -0
- package/dist/compaction/restore.d.ts.map +1 -1
- package/dist/compaction/restore.js +18 -18
- package/dist/compaction/restore.js.map +1 -1
- package/dist/contracts/session/api.d.ts +1 -1
- package/dist/contracts/session/api.d.ts.map +1 -1
- package/dist/execution/workflow/phases.d.ts +5 -2
- package/dist/execution/workflow/phases.d.ts.map +1 -1
- package/dist/execution/workflow/phases.js +32 -8
- package/dist/execution/workflow/phases.js.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.d.ts +1 -1
- package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.js +76 -11
- package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
- package/dist/tools/builtins/skill.d.ts.map +1 -1
- package/dist/tools/builtins/skill.js +5 -0
- package/dist/tools/builtins/skill.js.map +1 -1
- package/dist/turn/reporter.d.ts.map +1 -1
- package/dist/turn/reporter.js +9 -0
- package/dist/turn/reporter.js.map +1 -1
- package/dist/types/session/events.d.ts +26 -3
- package/dist/types/session/events.d.ts.map +1 -1
- package/dist/types/session/events.js +4 -1
- package/dist/types/session/events.js.map +1 -1
- package/dist/types/session/records.d.ts.map +1 -1
- package/dist/types/session/records.js +1 -0
- package/dist/types/session/records.js.map +1 -1
- package/package.json +1 -1
- package/src/bridge/a2a/mapper.ts +1 -0
- package/src/bridge/sse/mapper.ts +10 -0
- package/src/compaction/loaded-skills.ts +245 -0
- package/src/compaction/manager.ts +24 -0
- package/src/compaction/manual.ts +20 -2
- package/src/compaction/restore.ts +33 -18
- package/src/contracts/session/api.ts +1 -0
- package/src/execution/workflow/phases.ts +34 -8
- package/src/runtime/query/iteration/phases/compaction.ts +102 -16
- package/src/tools/builtins/skill.ts +5 -0
- package/src/turn/reporter.ts +10 -0
- package/src/types/session/events.ts +27 -2
- package/src/types/session/records.ts +1 -0
|
@@ -12,25 +12,51 @@ export interface PhaseRecord {
|
|
|
12
12
|
readonly declared: boolean
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* The entry of `entries` a title belongs to: one with exactly that title
|
|
17
|
+
* first; only when there is none, the first one whose title matches ignoring
|
|
18
|
+
* case. Two entries that differ only by case therefore stay two, because each
|
|
19
|
+
* matches itself exactly.
|
|
20
|
+
*/
|
|
21
|
+
function matchTitle<T extends { readonly title: string }>(
|
|
22
|
+
entries: Iterable<T>,
|
|
23
|
+
title: string,
|
|
24
|
+
): T | undefined {
|
|
25
|
+
const lower = title.toLowerCase()
|
|
26
|
+
let loose: T | undefined
|
|
27
|
+
for (const entry of entries) {
|
|
28
|
+
if (entry.title === title) return entry
|
|
29
|
+
if (loose === undefined && entry.title.toLowerCase() === lower) loose = entry
|
|
30
|
+
}
|
|
31
|
+
return loose
|
|
32
|
+
}
|
|
33
|
+
|
|
15
34
|
/**
|
|
16
35
|
* Phase titles of one run. A title is the only grouping key: the same title
|
|
17
|
-
* is the same group.
|
|
36
|
+
* is the same group. A title matches a registered one exactly first; only when
|
|
37
|
+
* none matches exactly does it match one ignoring case (the first registered
|
|
38
|
+
* wins, and its spelling is kept). Two declared titles that differ only by
|
|
39
|
+
* case stay two phases. Indexes are 1-based first-seen order, declared titles
|
|
18
40
|
* first. `register` reports whether the title was announced just now.
|
|
19
41
|
*/
|
|
20
42
|
export class PhaseRegistry {
|
|
21
|
-
private readonly
|
|
43
|
+
private readonly records: PhaseRecord[] = []
|
|
22
44
|
|
|
23
45
|
register(rawTitle: string, declared = false): { phase: PhaseRecord; isNew: boolean } {
|
|
24
46
|
const title = normalizePhaseTitle(rawTitle)
|
|
25
|
-
|
|
47
|
+
// A declared title is matched exactly only, so two declared titles that
|
|
48
|
+
// differ only by case each get their own phase.
|
|
49
|
+
const existing = declared
|
|
50
|
+
? this.records.find((record) => record.title === title)
|
|
51
|
+
: matchTitle(this.records, title)
|
|
26
52
|
if (existing) return { phase: existing, isNew: false }
|
|
27
|
-
const phase = { index: this.
|
|
28
|
-
this.
|
|
53
|
+
const phase = { index: this.records.length + 1, title, declared }
|
|
54
|
+
this.records.push(phase)
|
|
29
55
|
return { phase, isNew: true }
|
|
30
56
|
}
|
|
31
57
|
|
|
32
58
|
all(): PhaseRecord[] {
|
|
33
|
-
return [...this.
|
|
59
|
+
return [...this.records]
|
|
34
60
|
}
|
|
35
61
|
}
|
|
36
62
|
|
|
@@ -57,7 +83,7 @@ export function summarizeWorkflowPhases(
|
|
|
57
83
|
phases: readonly PhaseRecord[],
|
|
58
84
|
agents: readonly PhaseAgentView[],
|
|
59
85
|
): WorkflowPhaseSummary[] {
|
|
60
|
-
const
|
|
86
|
+
const named = declared.map((p) => ({ title: normalizePhaseTitle(p.title), detail: p.detail }))
|
|
61
87
|
const summarize = (
|
|
62
88
|
index: number,
|
|
63
89
|
title: string,
|
|
@@ -82,7 +108,7 @@ export function summarizeWorkflowPhases(
|
|
|
82
108
|
p.index,
|
|
83
109
|
p.title,
|
|
84
110
|
agents.filter((a) => a.phaseIndex === p.index),
|
|
85
|
-
|
|
111
|
+
matchTitle(named, p.title)?.detail,
|
|
86
112
|
),
|
|
87
113
|
)
|
|
88
114
|
const ungrouped = agents.filter((a) => a.phaseIndex === undefined)
|
|
@@ -3,6 +3,13 @@ import {
|
|
|
3
3
|
resolveTriggerThreshold,
|
|
4
4
|
} from '../../../../compaction/context-window.js'
|
|
5
5
|
import { findDanglingMessages } from '../../../../compaction/dangling.js'
|
|
6
|
+
import {
|
|
7
|
+
LOADED_SKILLS_ENTRY_KEY,
|
|
8
|
+
type RenderedSkills,
|
|
9
|
+
adoptableSkills,
|
|
10
|
+
handledSkillNames,
|
|
11
|
+
renderLoadedSkills,
|
|
12
|
+
} from '../../../../compaction/loaded-skills.js'
|
|
6
13
|
import { replaceableSummaries, summarizeWithModel } from '../../../../compaction/model-summary.js'
|
|
7
14
|
import {
|
|
8
15
|
type CompactionPlan,
|
|
@@ -29,7 +36,11 @@ import { CHARS_PER_TOKEN } from '../../../../constants/limits.js'
|
|
|
29
36
|
import { NAMZU } from '../../../../constants/telemetry/index.js'
|
|
30
37
|
import { invariants } from '../../../../invariants/index.js'
|
|
31
38
|
import { resolveTaskModel } from '../../../../model-router/task-router.js'
|
|
32
|
-
import
|
|
39
|
+
import {
|
|
40
|
+
type Message,
|
|
41
|
+
createTurnContextEntry,
|
|
42
|
+
turnContextEntries,
|
|
43
|
+
} from '../../../../types/message/index.js'
|
|
33
44
|
import { applyLifecycleHookResults } from '../../plugin-hooks.js'
|
|
34
45
|
import {
|
|
35
46
|
COMPACTION_REFILL_MESSAGE,
|
|
@@ -168,6 +179,65 @@ async function emitToolResultClear(
|
|
|
168
179
|
})
|
|
169
180
|
}
|
|
170
181
|
|
|
182
|
+
/**
|
|
183
|
+
* Keep the skills this conversation loaded where the pass cannot cut them.
|
|
184
|
+
* Called before a pass edits the history: whatever the messages show of a
|
|
185
|
+
* skill now goes into the working state, which outlives the messages.
|
|
186
|
+
*/
|
|
187
|
+
function adoptLoadedSkills(ctx: IterationContext): void {
|
|
188
|
+
ctx.workingStateManager?.adoptSkillBodies(adoptableSkills(ctx.recorder.messages))
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* After a pass that cut or cleared history: if a loaded skill's instructions
|
|
193
|
+
* are no longer in it, say them again, as reference.
|
|
194
|
+
*
|
|
195
|
+
* The entry goes at the END of the history, after everything the pass left.
|
|
196
|
+
* A clear already rewrites the request from the first result it cut, so the
|
|
197
|
+
* cached start of the request ends no earlier than it did; putting the skills
|
|
198
|
+
* anywhere before that point would move it further forward. At the end it
|
|
199
|
+
* also reads as the latest guidance, and the entry is never edited after, so
|
|
200
|
+
* later requests start with it unchanged.
|
|
201
|
+
*/
|
|
202
|
+
async function carryLoadedSkills(
|
|
203
|
+
ctx: IterationContext,
|
|
204
|
+
contextWindowTokens: number,
|
|
205
|
+
): Promise<void> {
|
|
206
|
+
const manager = ctx.workingStateManager
|
|
207
|
+
const loaded = manager?.getState().restorable?.skills
|
|
208
|
+
if (!loaded || loaded.length === 0) return
|
|
209
|
+
const handled = handledSkillNames(ctx.recorder.messages)
|
|
210
|
+
const missing = loaded.filter((skill) => !handled.has(skill.name))
|
|
211
|
+
if (missing.length === 0) return
|
|
212
|
+
const rendered = renderLoadedSkills(missing, { contextWindowTokens })
|
|
213
|
+
if (!rendered.text) return
|
|
214
|
+
const earlier = turnContextEntries(ctx.recorder.messages, LOADED_SKILLS_ENTRY_KEY)
|
|
215
|
+
ctx.recorder.pushMessage(
|
|
216
|
+
createTurnContextEntry(LOADED_SKILLS_ENTRY_KEY, rendered.text, new Date(), {
|
|
217
|
+
later: earlier.length > 0,
|
|
218
|
+
}),
|
|
219
|
+
)
|
|
220
|
+
await emitSkillsCarried(ctx, rendered)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async function emitSkillsCarried(
|
|
224
|
+
ctx: IterationContext,
|
|
225
|
+
rendered: {
|
|
226
|
+
carried: readonly string[]
|
|
227
|
+
cut: readonly string[]
|
|
228
|
+
left: readonly string[]
|
|
229
|
+
},
|
|
230
|
+
): Promise<void> {
|
|
231
|
+
await ctx.emitEvent?.({
|
|
232
|
+
type: 'compaction_skills_carried',
|
|
233
|
+
turnId: ctx.recorder.turnId,
|
|
234
|
+
iteration: ctx.recorder.currentIteration,
|
|
235
|
+
skills: [...rendered.carried],
|
|
236
|
+
cut: [...rendered.cut],
|
|
237
|
+
leftOut: [...rendered.left],
|
|
238
|
+
})
|
|
239
|
+
}
|
|
240
|
+
|
|
171
241
|
/** Install a clear-only pass and publish the state it established. */
|
|
172
242
|
async function commitToolResultClear(
|
|
173
243
|
ctx: IterationContext,
|
|
@@ -180,11 +250,13 @@ async function commitToolResultClear(
|
|
|
180
250
|
// content in the same archive used by whole-message compaction before
|
|
181
251
|
// changing the live history or publishing the clear.
|
|
182
252
|
await recordShed(ctx, [...ctx.recorder.messages], plan.messages, reason)
|
|
253
|
+
adoptLoadedSkills(ctx)
|
|
183
254
|
installMessages(ctx.recorder.messages, plan.messages)
|
|
184
255
|
// The provider counted the pre-edit prompt. Leaving that measurement live
|
|
185
256
|
// makes the next trigger compare the old prompt against the new history.
|
|
186
257
|
ctx.recorder.clearLastPromptTokens?.()
|
|
187
258
|
await emitToolResultClear(ctx, plan)
|
|
259
|
+
await carryLoadedSkills(ctx, contextWindowTokens)
|
|
188
260
|
await emitContextUsageSnapshot(ctx, estimateTokens(ctx), contextWindowTokens, windowSource)
|
|
189
261
|
// A host-requested pass is not the kernel's own relief of a full context,
|
|
190
262
|
// so it must not reset the breaker's failure streak.
|
|
@@ -1136,6 +1208,9 @@ async function runCompactionCheckInner(
|
|
|
1136
1208
|
|
|
1137
1209
|
// Skills the model loaded and files it read go back verbatim after the
|
|
1138
1210
|
// summary, with whatever the host wants carried across (its plan file).
|
|
1211
|
+
// The skills come from the conversation as it stands, so a skill loaded in
|
|
1212
|
+
// an earlier turn, or before a resume, is put back too.
|
|
1213
|
+
adoptLoadedSkills(ctx)
|
|
1139
1214
|
let hostNote: string | undefined
|
|
1140
1215
|
try {
|
|
1141
1216
|
hostNote = await ctx.postCompactionContext?.()
|
|
@@ -1146,21 +1221,6 @@ async function runCompactionCheckInner(
|
|
|
1146
1221
|
'exception.message': error instanceof Error ? error.message : String(error),
|
|
1147
1222
|
})
|
|
1148
1223
|
}
|
|
1149
|
-
const restored = renderRestoredContext(manager.getState(), {
|
|
1150
|
-
contextWindowTokens: budget,
|
|
1151
|
-
...(hostNote ? { hostNote } : {}),
|
|
1152
|
-
})
|
|
1153
|
-
if (restored) compactedContent = `${compactedContent}\n\n${restored}`
|
|
1154
|
-
|
|
1155
|
-
// The pointer is only worth a line when both tools it names are mounted.
|
|
1156
|
-
const mounted = new Set(ctx.tools?.listNames?.() ?? [])
|
|
1157
|
-
const compactionMessage = buildCompactionMessage(
|
|
1158
|
-
compactedContent,
|
|
1159
|
-
mounted.has('search_conversation') && mounted.has('read_conversation')
|
|
1160
|
-
? COMPACTION_RECOVERY_NOTE
|
|
1161
|
-
: undefined,
|
|
1162
|
-
)
|
|
1163
|
-
|
|
1164
1224
|
// Drop a replaceable PRIOR `[COMPACTED CONTEXT]` summary from this turn's
|
|
1165
1225
|
// leading floor — `serializeState` is cumulative, so the new summary
|
|
1166
1226
|
// supersedes it. A retained summary came from outside this manager's state
|
|
@@ -1183,6 +1243,29 @@ async function runCompactionCheckInner(
|
|
|
1183
1243
|
.slice(systemMessages.length, keepStart)
|
|
1184
1244
|
.filter((_, offset) => retained.has(systemMessages.length + offset))
|
|
1185
1245
|
|
|
1246
|
+
// A skill whose instructions are in what the pass keeps as it is, whole or
|
|
1247
|
+
// shown by an earlier pass, is not put back a second time.
|
|
1248
|
+
const wholeSkills = handledSkillNames([...preservedSystem, ...retainedOlder, ...recentMessages])
|
|
1249
|
+
const putBack: { rendered?: RenderedSkills } = {}
|
|
1250
|
+
const restored = renderRestoredContext(manager.getState(), {
|
|
1251
|
+
contextWindowTokens: budget,
|
|
1252
|
+
wholeSkills,
|
|
1253
|
+
onSkills: (rendered) => {
|
|
1254
|
+
putBack.rendered = rendered
|
|
1255
|
+
},
|
|
1256
|
+
...(hostNote ? { hostNote } : {}),
|
|
1257
|
+
})
|
|
1258
|
+
if (restored) compactedContent = `${compactedContent}\n\n${restored}`
|
|
1259
|
+
|
|
1260
|
+
// The pointer is only worth a line when both tools it names are mounted.
|
|
1261
|
+
const mounted = new Set(ctx.tools?.listNames?.() ?? [])
|
|
1262
|
+
const compactionMessage = buildCompactionMessage(
|
|
1263
|
+
compactedContent,
|
|
1264
|
+
mounted.has('search_conversation') && mounted.has('read_conversation')
|
|
1265
|
+
? COMPACTION_RECOVERY_NOTE
|
|
1266
|
+
: undefined,
|
|
1267
|
+
)
|
|
1268
|
+
|
|
1186
1269
|
const newMessages = [...preservedSystem, compactionMessage, ...retainedOlder, ...recentMessages]
|
|
1187
1270
|
|
|
1188
1271
|
// OPAQUE survival guard: the pinned working-memory slot is a
|
|
@@ -1282,6 +1365,9 @@ async function runCompactionCheckInner(
|
|
|
1282
1365
|
reachedResetThreshold: reachedReset,
|
|
1283
1366
|
})
|
|
1284
1367
|
await emitContextUsageSnapshot(ctx, newEstimate, budget, window.source)
|
|
1368
|
+
if (putBack.rendered && putBack.rendered.carried.length > 0) {
|
|
1369
|
+
await emitSkillsCarried(ctx, putBack.rendered)
|
|
1370
|
+
}
|
|
1285
1371
|
compactionBreaker(ctx)?.recordCompaction(
|
|
1286
1372
|
ctx.recorder.currentIteration,
|
|
1287
1373
|
beforeTokens,
|
|
@@ -2,6 +2,7 @@ import { createHash } from 'node:crypto'
|
|
|
2
2
|
import { z } from 'zod'
|
|
3
3
|
|
|
4
4
|
import { parseAllowedTools } from '../../authorization/skill-grant.js'
|
|
5
|
+
import { forkedSkillReportNote } from '../../compaction/loaded-skills.js'
|
|
5
6
|
import type { Sandbox } from '../../types/sandbox/index.js'
|
|
6
7
|
import { isInvocableBy, skillInvocation } from '../../types/skills/index.js'
|
|
7
8
|
import type { ToolContext } from '../../types/tool/index.js'
|
|
@@ -701,6 +702,10 @@ async function runForkedSkill(input: {
|
|
|
701
702
|
})
|
|
702
703
|
return {
|
|
703
704
|
...result,
|
|
705
|
+
// Says what the text is, so a later pass never carries a report as the skill's steps.
|
|
706
|
+
output: result.success
|
|
707
|
+
? `${forkedSkillReportNote(input.name)}\n\n${result.output}`
|
|
708
|
+
: result.output,
|
|
704
709
|
data: {
|
|
705
710
|
...(typeof result.data === 'object' && result.data !== null ? result.data : {}),
|
|
706
711
|
skill: input.name,
|
package/src/turn/reporter.ts
CHANGED
|
@@ -350,6 +350,16 @@ export function createTurnReporter(parentLogger?: Logger): TurnReporter {
|
|
|
350
350
|
})
|
|
351
351
|
break
|
|
352
352
|
|
|
353
|
+
case 'compaction_skills_carried':
|
|
354
|
+
log.info('Put loaded skills back after a pass cut them', {
|
|
355
|
+
[NAMZU.TURN_ID]: event.turnId,
|
|
356
|
+
[NAMZU.ITERATION]: event.iteration,
|
|
357
|
+
'namzu.compaction.skills': event.skills.join(','),
|
|
358
|
+
'namzu.compaction.skills_cut': event.cut.join(','),
|
|
359
|
+
'namzu.compaction.skills_left_out': event.leftOut.join(','),
|
|
360
|
+
})
|
|
361
|
+
break
|
|
362
|
+
|
|
353
363
|
case 'compaction_failed':
|
|
354
364
|
// warn rather than info: the turn is now continuing at a context
|
|
355
365
|
// size it had already decided was too large.
|
|
@@ -304,6 +304,27 @@ type CoreSessionEvent =
|
|
|
304
304
|
*/
|
|
305
305
|
reliefWasEnough: boolean
|
|
306
306
|
}
|
|
307
|
+
/**
|
|
308
|
+
* A pass cut or summarised history and the instructions of skills the
|
|
309
|
+
* conversation had loaded were put back in front of the model, as reference.
|
|
310
|
+
*
|
|
311
|
+
* Emitted only when something was put back: a skill whose instructions are
|
|
312
|
+
* still whole in the context is never repeated, so most passes say nothing.
|
|
313
|
+
* Live only (ephemeral): it is not written to the session log, because the
|
|
314
|
+
* put-back text is a message of its own and is in the log as one.
|
|
315
|
+
*/
|
|
316
|
+
| {
|
|
317
|
+
type: 'compaction_skills_carried'
|
|
318
|
+
sessionId: SessionId
|
|
319
|
+
turnId?: TurnId
|
|
320
|
+
iteration: number
|
|
321
|
+
/** Skills put back, oldest load first. */
|
|
322
|
+
skills: string[]
|
|
323
|
+
/** The ones among them cut to their per-skill cap, each with a line saying how to load it in full. */
|
|
324
|
+
cut: string[]
|
|
325
|
+
/** Skills left out for room, newest first; each is named in the text the model reads. */
|
|
326
|
+
leftOut: string[]
|
|
327
|
+
}
|
|
307
328
|
/**
|
|
308
329
|
* A compaction pass ran and shed nothing, so the history is unchanged.
|
|
309
330
|
*
|
|
@@ -1443,7 +1464,9 @@ export type SessionEventListener = (event: SessionEvent) => void | Promise<void>
|
|
|
1443
1464
|
* log is what cannot be written while they matter.
|
|
1444
1465
|
* `compaction_started` is ephemeral too: it only says a pass is about to run,
|
|
1445
1466
|
* and the `compaction` record or `compaction_failed` that ends it is the
|
|
1446
|
-
* durable fact.
|
|
1467
|
+
* durable fact. `compaction_skills_carried` is ephemeral for the same kind of
|
|
1468
|
+
* reason: the text it announces is a message of its own and is in the log as
|
|
1469
|
+
* one, so a log carries no record an older reader would have to skip.
|
|
1447
1470
|
*/
|
|
1448
1471
|
export const EPHEMERAL_EVENT_TYPES: ReadonlySet<SessionEventType> = new Set<SessionEventType>([
|
|
1449
1472
|
'text_delta',
|
|
@@ -1452,6 +1475,7 @@ export const EPHEMERAL_EVENT_TYPES: ReadonlySet<SessionEventType> = new Set<Sess
|
|
|
1452
1475
|
'tool_progress',
|
|
1453
1476
|
'monitor_event',
|
|
1454
1477
|
'compaction_started',
|
|
1478
|
+
'compaction_skills_carried',
|
|
1455
1479
|
'session_log_degraded',
|
|
1456
1480
|
'session_log_recovered',
|
|
1457
1481
|
])
|
|
@@ -1460,7 +1484,7 @@ export function isEphemeralEvent(event: { readonly type: string }): boolean {
|
|
|
1460
1484
|
return EPHEMERAL_EVENT_TYPES.has(event.type as SessionEventType)
|
|
1461
1485
|
}
|
|
1462
1486
|
|
|
1463
|
-
/** The event types a session log records: every literal except the ephemeral
|
|
1487
|
+
/** The event types a session log records: every literal except the ephemeral nine. */
|
|
1464
1488
|
export type PersistedSessionEventType = Exclude<
|
|
1465
1489
|
SessionEventType,
|
|
1466
1490
|
| 'text_delta'
|
|
@@ -1469,6 +1493,7 @@ export type PersistedSessionEventType = Exclude<
|
|
|
1469
1493
|
| 'tool_progress'
|
|
1470
1494
|
| 'monitor_event'
|
|
1471
1495
|
| 'compaction_started'
|
|
1496
|
+
| 'compaction_skills_carried'
|
|
1472
1497
|
| 'session_log_degraded'
|
|
1473
1498
|
| 'session_log_recovered'
|
|
1474
1499
|
>
|
|
@@ -724,6 +724,7 @@ const SESSION_EVENT_TYPE_TABLE: Record<SessionEventType, true> = {
|
|
|
724
724
|
compaction_shed: true,
|
|
725
725
|
compaction_completed: true,
|
|
726
726
|
compaction_tool_results_cleared: true,
|
|
727
|
+
compaction_skills_carried: true,
|
|
727
728
|
compaction_failed: true,
|
|
728
729
|
tool_executing: true,
|
|
729
730
|
tool_progress: true,
|