@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.
@@ -1,215 +1,41 @@
1
- import type { Extension, Model } from '@xandout/libra-harness';
2
- import type { Message, MessageContent, ToolCall } from '@xandout/libra-harness';
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
- /** Load existing JSONL files into memory on startup. Default: true. */
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
- getEffectiveLimit(sessionKey?: string): number;
213
- setEffectiveLimit(sessionKey: string, limit: number): void;
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":"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"}
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"}