@xandout/libra-harness 0.1.131 → 0.1.133
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/dist/extensions/disk-session/extension.d.ts +12 -186
- package/dist/extensions/disk-session/extension.d.ts.map +1 -1
- package/dist/extensions/disk-session/extension.js +157 -768
- package/dist/extensions/disk-session/extension.js.map +1 -1
- package/dist/extensions/disk-session/index.d.ts +6 -2
- package/dist/extensions/disk-session/index.d.ts.map +1 -1
- package/dist/extensions/disk-session/index.js +3 -1
- package/dist/extensions/disk-session/index.js.map +1 -1
- package/dist/extensions/disk-session/ledger.d.ts +62 -0
- package/dist/extensions/disk-session/ledger.d.ts.map +1 -0
- package/dist/extensions/disk-session/ledger.js +62 -0
- package/dist/extensions/disk-session/ledger.js.map +1 -0
- package/dist/extensions/disk-session/projector.d.ts +26 -0
- package/dist/extensions/disk-session/projector.d.ts.map +1 -0
- package/dist/extensions/disk-session/projector.js +279 -0
- package/dist/extensions/disk-session/projector.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,215 +1,41 @@
|
|
|
1
|
-
import type { Extension
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
* A single record in a session's JSONL log.
|
|
5
|
-
*
|
|
6
|
-
* Maps directly to a Message plus correlation metadata. The system
|
|
7
|
-
* prompt that was active for a turn is captured on the user record's
|
|
8
|
-
* `systemPrompt` field. Background channel messages (observed but not
|
|
9
|
-
* directed at the agent) are stored as `system` records so the agent
|
|
10
|
-
* sees them as context, not as messages to respond to.
|
|
11
|
-
*/
|
|
12
|
-
export interface SessionRecord {
|
|
13
|
-
role: 'user' | 'assistant' | 'tool' | 'control' | 'system';
|
|
14
|
-
content: MessageContent;
|
|
15
|
-
toolCalls?: ToolCall[];
|
|
16
|
-
toolCallId?: string;
|
|
17
|
-
name?: string;
|
|
18
|
-
/** Message correlation id (e.g. a Slack message timestamp, a Discord message id). */
|
|
19
|
-
ts: string;
|
|
20
|
-
/** Thread parent correlation id — undefined for top-level messages. */
|
|
21
|
-
threadTs?: string;
|
|
22
|
-
/** ISO timestamp for internal tracking. */
|
|
23
|
-
recordedAt: string;
|
|
24
|
-
/**
|
|
25
|
-
* The system prompt that was active for this turn. Persisted on the
|
|
26
|
-
* user record (the first record of each turn) so every turn's JSONL
|
|
27
|
-
* entry shows exactly what instructions the model was operating under.
|
|
28
|
-
* Absent on assistant, tool, and control records. Lets you audit
|
|
29
|
-
* "why did the model respond this way?" without replaying config +
|
|
30
|
-
* extension state.
|
|
31
|
-
*/
|
|
32
|
-
systemPrompt?: string;
|
|
33
|
-
/** Token usage from the LLM provider (accumulated across iterations). */
|
|
34
|
-
usage?: {
|
|
35
|
-
promptTokens: number;
|
|
36
|
-
completionTokens: number;
|
|
37
|
-
iterations: number;
|
|
38
|
-
/** Prompt tokens served from provider cache (accumulated). */
|
|
39
|
-
cachedPromptTokens?: number;
|
|
40
|
-
/** Reasoning/thinking tokens (accumulated). */
|
|
41
|
-
reasoningTokens?: number;
|
|
42
|
-
};
|
|
43
|
-
/**
|
|
44
|
-
* Generic enrichment bag. Populated opaquely from
|
|
45
|
-
* `ctx.turn.metadata.sessionMeta` — any extension may write into
|
|
46
|
-
* that metadata key during `beforeTurn`/`afterLLM` and disk-session
|
|
47
|
-
* persists it here without inspecting the contents. Keys are owned
|
|
48
|
-
* by the extension that writes them (e.g. `keywords`, `sentiment`).
|
|
49
|
-
*/
|
|
50
|
-
meta?: Record<string, unknown>;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Identifies which session a turn belongs to.
|
|
54
|
-
*
|
|
55
|
-
* The host (or a resolver) produces this from turn metadata — the
|
|
56
|
-
* disk-session extension never reads host-specific types directly.
|
|
57
|
-
*/
|
|
1
|
+
import type { Extension } from '../../extension.js';
|
|
2
|
+
import type { Model } from '../../model.js';
|
|
3
|
+
import { type LedgerUsage, type MessageLedgerRecord, type SessionRecord } from './ledger.js';
|
|
58
4
|
export interface SessionIdentity {
|
|
59
|
-
/** Stable session key — used as the JSONL filename and in-memory cache key. e.g. 'slack_C1', 'dm_U1', 'issue_42'.
|
|
60
|
-
* Should be filesystem-safe (alphanumeric, underscore, hyphen). Characters outside [a-zA-Z0-9_-] are replaced with '_' for the filename, so keys with colons or dots won't round-trip identically through a restart. */
|
|
61
5
|
key: string;
|
|
62
|
-
/** Correlation id for the incoming message (persisted as `record.ts`). */
|
|
63
6
|
messageTs: string;
|
|
64
|
-
/** Parent correlation id — when set, this message is a reply in a thread/sub-conversation. Persisted as `record.threadTs` and used as the context fork point. */
|
|
65
7
|
threadTs?: string;
|
|
66
|
-
/** True for 1:1 conversations (DMs) — disables thread forking, uses simple last-N history. */
|
|
67
8
|
isDirect?: boolean;
|
|
68
9
|
}
|
|
69
|
-
/**
|
|
70
|
-
* Extracts a {@link SessionIdentity} from turn metadata.
|
|
71
|
-
*
|
|
72
|
-
* The host supplies a resolver so disk-session doesn't need to know
|
|
73
|
-
* about Slack channels, Discord channels, or any other host concept.
|
|
74
|
-
* Return `undefined` to skip session handling for a turn.
|
|
75
|
-
*/
|
|
76
10
|
export interface SessionResolver {
|
|
77
11
|
resolve(metadata: Record<string, unknown>): SessionIdentity | undefined;
|
|
78
12
|
}
|
|
79
13
|
export interface DiskSessionConfig {
|
|
80
|
-
/** Directory for session JSONL files. Default: ./sessions */
|
|
81
14
|
sessionDir?: string;
|
|
82
|
-
/**
|
|
83
|
-
* Max records to keep in memory per session. Older records are
|
|
84
|
-
* evicted from the cache but remain in the JSONL file.
|
|
85
|
-
* Default: 1000.
|
|
86
|
-
*/
|
|
87
|
-
maxRecords?: number;
|
|
88
|
-
/**
|
|
89
|
-
* Max messages to include in the agent's context per turn.
|
|
90
|
-
* When history reaches this threshold, auto-summarization is triggered.
|
|
91
|
-
* Default: 50.
|
|
92
|
-
*/
|
|
93
15
|
maxContextMessages?: number;
|
|
94
|
-
/**
|
|
95
|
-
* Number of messages to evict or compact at once when exceeding maxContextMessages.
|
|
96
|
-
* By evicting in discrete chunks rather than 1-by-1 per turn, the prompt prefix
|
|
97
|
-
* stays stable for consecutive turns, maximizing LLM prompt cache hits.
|
|
98
|
-
* Default: Math.max(1, Math.floor(maxContextMessages / 2)).
|
|
99
|
-
*/
|
|
100
|
-
contextEvictionStep?: number;
|
|
101
|
-
/**
|
|
102
|
-
* Fallback message limits to back off to if a context length error occurs
|
|
103
|
-
* (e.g. [100, 50, 25]). If the model provider rejects a request due to
|
|
104
|
-
* context length, the extension steps down to the next fallback limit.
|
|
105
|
-
* Default: [maxContextMessages, Math.floor(maxContextMessages / 2), Math.floor(maxContextMessages / 4)].
|
|
106
|
-
*/
|
|
107
|
-
fallbackThresholds?: number[];
|
|
108
|
-
/**
|
|
109
|
-
* Whether to automatically summarize older conversation messages when history
|
|
110
|
-
* reaches maxContextMessages.
|
|
111
|
-
* Default: true.
|
|
112
|
-
*/
|
|
113
16
|
autoSummarize?: boolean;
|
|
114
|
-
/**
|
|
115
|
-
* Model to use for generating auto-summaries.
|
|
116
|
-
* Defaults to the agent's configured model if not explicitly provided.
|
|
117
|
-
*/
|
|
118
17
|
model?: Model;
|
|
119
|
-
/**
|
|
120
|
-
* Number of top-level (non-thread) messages to include as channel
|
|
121
|
-
* context when forking a thread. These are messages that came BEFORE
|
|
122
|
-
* the thread parent — what the channel was discussing when the thread
|
|
123
|
-
* started. Default: 10.
|
|
124
|
-
*/
|
|
125
18
|
channelContextMessages?: number;
|
|
126
|
-
/**
|
|
127
|
-
* Number of recent top-level messages to include after the last thread
|
|
128
|
-
* reply. This gives the agent awareness of what's happened in the
|
|
129
|
-
* channel since the thread was last active (e.g. if someone comes back
|
|
130
|
-
* to a thread a week later, the agent sees recent channel activity).
|
|
131
|
-
* Default: 5.
|
|
132
|
-
*/
|
|
133
19
|
recentChannelMessages?: number;
|
|
134
|
-
|
|
20
|
+
toolCallRetention?: number;
|
|
135
21
|
loadOnStartup?: boolean;
|
|
136
|
-
/**
|
|
137
|
-
* Print a summary line when sessions are loaded from disk.
|
|
138
|
-
* Default: true.
|
|
139
|
-
*/
|
|
140
22
|
verbose?: boolean;
|
|
141
|
-
/**
|
|
142
|
-
* Resolve a {@link SessionIdentity} from turn metadata. Default:
|
|
143
|
-
* reads `metadata.session` as a `SessionIdentity`, falls back to
|
|
144
|
-
* `metadata.sessionId` as a plain key, then to `'default'`.
|
|
145
|
-
*/
|
|
146
23
|
resolver?: SessionResolver;
|
|
147
24
|
}
|
|
148
|
-
|
|
149
|
-
* Sanitizes a message sequence to strictly conform to LLM tool-call schemas:
|
|
150
|
-
* 1. Every message with role 'tool' MUST directly follow an assistant message
|
|
151
|
-
* with a matching tool call id (or follow valid sibling tool messages for that same assistant).
|
|
152
|
-
* Any orphan or duplicate tool messages are dropped.
|
|
153
|
-
* 2. If an assistant message has toolCalls, but some or all tool calls were never
|
|
154
|
-
* fulfilled (e.g. session interrupted mid-turn or tool execution halted), its
|
|
155
|
-
* toolCalls array is pruned to only the fulfilled tool calls.
|
|
156
|
-
* 3. If an assistant message has toolCalls but none were fulfilled:
|
|
157
|
-
* - If the assistant message has non-empty text content, toolCalls is stripped.
|
|
158
|
-
* - If the assistant message has no text content, it is removed entirely.
|
|
159
|
-
*/
|
|
160
|
-
export declare function sanitizeConversationMessages(messages: Message[]): Message[];
|
|
161
|
-
export declare function isContextLengthError(err: unknown): boolean;
|
|
162
|
-
export declare function generateSessionSummary(model: Model, records: SessionRecord[]): Promise<string>;
|
|
163
|
-
/**
|
|
164
|
-
* Disk-backed session extension.
|
|
165
|
-
*
|
|
166
|
-
* ## Architecture
|
|
167
|
-
*
|
|
168
|
-
* **One JSONL file per session** (`<sessionKey>.jsonl`). Append-only —
|
|
169
|
-
* every message the agent sees (human or bot, top-level or threaded) is
|
|
170
|
-
* appended. This log is the source of truth.
|
|
171
|
-
*
|
|
172
|
-
* **Snapshot at beforeTurn**: when a turn starts, the extension takes a
|
|
173
|
-
* read-only snapshot of the session log at that moment. The turn runs
|
|
174
|
-
* against the snapshot. Concurrent turns each take their own snapshot
|
|
175
|
-
* and don't see each other's in-progress work.
|
|
176
|
-
*
|
|
177
|
-
* **Fork for threads**: when a thread reply comes in (identity has
|
|
178
|
-
* `threadTs`), the snapshot is filtered to include only:
|
|
179
|
-
* 1. Top-level messages before the thread parent (channel context)
|
|
180
|
-
* 2. All messages in that thread (parent + replies)
|
|
181
|
-
* 3. Recent top-level messages after the last thread reply
|
|
182
|
-
*
|
|
183
|
-
* **Append after turn**: when the turn finishes, new messages (user +
|
|
184
|
-
* assistant + tool calls) are appended to the session log. No data is
|
|
185
|
-
* ever rewritten or reordered.
|
|
186
|
-
*
|
|
187
|
-
* **Stable prefix for LLM caching**: the messages array is built as
|
|
188
|
-
* [system prompt] + [stable session history] + [new user message]
|
|
189
|
-
* The session history grows monotonically (append-only), so earlier
|
|
190
|
-
* tokens stay cached upstream. Per-turn metadata is injected at the end
|
|
191
|
-
* of the context (by a host-side `beforeContext` hook), not the
|
|
192
|
-
* beginning, to preserve the cache prefix.
|
|
193
|
-
*
|
|
194
|
-
* ## Host integration
|
|
195
|
-
*
|
|
196
|
-
* The host supplies a {@link SessionResolver} (or writes
|
|
197
|
-
* `metadata.session`) so the extension knows which session a turn
|
|
198
|
-
* belongs to. The extension itself is host-agnostic — it doesn't know
|
|
199
|
-
* about Slack, Discord, or any other platform.
|
|
200
|
-
*/
|
|
201
|
-
export default function createDiskSessionExtension(config?: DiskSessionConfig): Extension & {
|
|
25
|
+
export interface DiskSessionExtension extends Extension {
|
|
202
26
|
getRecords(sessionKey?: string): SessionRecord[];
|
|
203
27
|
getSessions(): string[];
|
|
204
|
-
clear(sessionKey?: string): void;
|
|
205
|
-
clearAll(): void;
|
|
206
28
|
appendControl(sessionKey: string, content: string, ts?: string, threadTs?: string): void;
|
|
207
29
|
appendMessage(sessionKey: string, content: string, opts?: {
|
|
208
30
|
ts?: string;
|
|
209
31
|
threadTs?: string;
|
|
210
32
|
meta?: Record<string, unknown>;
|
|
211
33
|
}): void;
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
34
|
+
}
|
|
35
|
+
export declare function generateSessionSummary(model: Model, records: MessageLedgerRecord[]): Promise<{
|
|
36
|
+
content: string;
|
|
37
|
+
usage?: LedgerUsage;
|
|
38
|
+
}>;
|
|
39
|
+
export default function createDiskSessionExtension(config?: DiskSessionConfig): DiskSessionExtension;
|
|
40
|
+
export type { SessionRecord } from './ledger.js';
|
|
215
41
|
//# sourceMappingURL=extension.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extension.d.ts","sourceRoot":"","sources":["../../../src/extensions/disk-session/extension.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"extension.d.ts","sourceRoot":"","sources":["../../../src/extensions/disk-session/extension.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAG5C,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,aAAa,EACnB,MAAM,aAAa,CAAC;AAYrB,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,eAAe,GAAG,SAAS,CAAC;CACzE;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,eAAe,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAqB,SAAQ,SAAS;IACrD,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,aAAa,EAAE,CAAC;IACjD,WAAW,IAAI,MAAM,EAAE,CAAC;IACxB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzF,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QACxD,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAChC,GAAG,IAAI,CAAC;CACV;AAYD,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,mBAAmB,EAAE,GAC7B,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,WAAW,CAAA;CAAE,CAAC,CAenD;AAED,MAAM,CAAC,OAAO,UAAU,0BAA0B,CAAC,MAAM,GAAE,iBAAsB,GAAG,oBAAoB,CAgOvG;AAED,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
|