@wrongstack/core 0.9.19 → 0.9.20
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/{agent-bridge-DMVOX0cF.d.ts → agent-bridge-Dti3KXGk.d.ts} +1 -1
- package/dist/{agent-subagent-runner-C4qt9e5Y.d.ts → agent-subagent-runner-U-rs7kk7.d.ts} +3 -4
- package/dist/compactor-D7X96RLZ.d.ts +41 -0
- package/dist/{config-CWva0qoL.d.ts → config-CLXMDOSs.d.ts} +1 -1
- package/dist/{context-BRNbHmRM.d.ts → context-zkZeILpr.d.ts} +46 -0
- package/dist/coordination/index.d.ts +13 -13
- package/dist/coordination/index.js +660 -146
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +20 -20
- package/dist/defaults/index.js +918 -350
- package/dist/defaults/index.js.map +1 -1
- package/dist/{events-CiG9qUM_.d.ts → events-DH-9r-_C.d.ts} +42 -1
- package/dist/execution/index.d.ts +41 -30
- package/dist/execution/index.js +358 -112
- package/dist/execution/index.js.map +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/extension/index.js.map +1 -1
- package/dist/{index-aizK8olO.d.ts → index-BIHJ4uII.d.ts} +11 -8
- package/dist/{index-p95HQ22A.d.ts → index-CFO9QmJo.d.ts} +8 -8
- package/dist/index.d.ts +311 -35
- package/dist/index.js +1933 -512
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/infrastructure/index.js +36 -0
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +9 -9
- package/dist/kernel/index.js.map +1 -1
- package/dist/{mcp-servers-BkVEqkRe.d.ts → mcp-servers-DkESgh0G.d.ts} +25 -3
- package/dist/models/index.d.ts +2 -2
- package/dist/models/index.js +1 -1
- package/dist/models/index.js.map +1 -1
- package/dist/{multi-agent-3ZnTB1aT.d.ts → multi-agent-DNp6lAzg.d.ts} +35 -23
- package/dist/{multi-agent-coordinator-bRaI_aD1.d.ts → multi-agent-coordinator-CAhsegPz.d.ts} +20 -2
- package/dist/{null-fleet-bus-DKM3Iy9d.d.ts → null-fleet-bus-Dnl19vmf.d.ts} +411 -110
- package/dist/observability/index.d.ts +2 -2
- package/dist/{path-resolver-TcJfc29Y.d.ts → path-resolver-CHiBL0DD.d.ts} +2 -2
- package/dist/{permission-bPuzAy4x.d.ts → permission-H35s9Evv.d.ts} +1 -1
- package/dist/{permission-policy-BUQSutpl.d.ts → permission-policy-CT-nRmTn.d.ts} +2 -2
- package/dist/{plan-templates-fkQTyz3U.d.ts → plan-templates-Bs8iRwi6.d.ts} +4 -4
- package/dist/{provider-runner-BEpikbbN.d.ts → provider-runner-BZdDrWrS.d.ts} +3 -3
- package/dist/{retry-policy-BYkq0ugs.d.ts → retry-policy-J9N_PM40.d.ts} +1 -1
- package/dist/sdd/index.d.ts +9 -10
- package/dist/sdd/index.js +224 -68
- package/dist/sdd/index.js.map +1 -1
- package/dist/security/index.d.ts +3 -3
- package/dist/{selector-pox8abg0.d.ts → selector-CFTh3Z6p.d.ts} +1 -1
- package/dist/{session-reader-CSWcb5Ga.d.ts → session-reader-C7JJlxOw.d.ts} +2 -2
- package/dist/skills/index.d.ts +1 -1
- package/dist/skills/index.js +1 -1
- package/dist/skills/index.js.map +1 -1
- package/dist/storage/index.d.ts +390 -6
- package/dist/storage/index.js +672 -35
- package/dist/storage/index.js.map +1 -1
- package/dist/{system-prompt-Bs-Wliab.d.ts → system-prompt-CneIxVip.d.ts} +1 -1
- package/dist/{tool-executor-Boo3dekH.d.ts → tool-executor-flTuxsqO.d.ts} +9 -4
- package/dist/types/index.d.ts +14 -14
- package/dist/types/index.js +60 -0
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +18 -6
- package/dist/utils/index.js +61 -56
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/compactor-DVTKL7XD.d.ts +0 -23
package/dist/storage/index.d.ts
CHANGED
|
@@ -1,17 +1,401 @@
|
|
|
1
|
-
export { A as AbandonedSession, a as AttachmentStoreOptions, C as ConfigLoaderOptions, b as ConfigMigration, c as ConfigMigrationError, d as ConfigSource, D as DEFAULT_CONFIG_MIGRATIONS, e as DefaultAttachmentStore, f as DefaultConfigLoader, g as DefaultConfigStore, h as DefaultMemoryStore, i as DefaultSessionStore, M as MemoryStoreOptions, j as MigrationContext, k as MigrationResult, P as PersistedQueueItem, l as PlanFile, m as PlanItem, n as PlanTemplate, Q as QueueStore, R as RecoveryLock, o as RecoveryLockOptions, S as SessionAnalyzer, p as SessionStoreOptions, T as TodosCheckpointFile, q as addPlanItem, r as attachPlanCheckpoint, s as attachTodosCheckpoint, t as clearPlan, u as deriveTodosFromPlanItem, v as emptyPlan, w as formatPlan, x as formatPlanTemplates, y as getPlanTemplate, z as listPlanTemplates, B as loadPlan, E as loadTodosCheckpoint, F as removePlanItem, G as runConfigMigrations, H as savePlan, I as saveTodosCheckpoint, J as setPlanItemStatus } from '../plan-templates-
|
|
2
|
-
export { D as DefaultSessionReader } from '../session-reader-
|
|
1
|
+
export { A as AbandonedSession, a as AttachmentStoreOptions, C as ConfigLoaderOptions, b as ConfigMigration, c as ConfigMigrationError, d as ConfigSource, D as DEFAULT_CONFIG_MIGRATIONS, e as DefaultAttachmentStore, f as DefaultConfigLoader, g as DefaultConfigStore, h as DefaultMemoryStore, i as DefaultSessionStore, M as MemoryStoreOptions, j as MigrationContext, k as MigrationResult, P as PersistedQueueItem, l as PlanFile, m as PlanItem, n as PlanTemplate, Q as QueueStore, R as RecoveryLock, o as RecoveryLockOptions, S as SessionAnalyzer, p as SessionStoreOptions, T as TodosCheckpointFile, q as addPlanItem, r as attachPlanCheckpoint, s as attachTodosCheckpoint, t as clearPlan, u as deriveTodosFromPlanItem, v as emptyPlan, w as formatPlan, x as formatPlanTemplates, y as getPlanTemplate, z as listPlanTemplates, B as loadPlan, E as loadTodosCheckpoint, F as removePlanItem, G as runConfigMigrations, H as savePlan, I as saveTodosCheckpoint, J as setPlanItemStatus } from '../plan-templates-Bs8iRwi6.js';
|
|
2
|
+
export { D as DefaultSessionReader, f as DefaultSessionReaderOptions, S as SessionReader } from '../session-reader-C7JJlxOw.js';
|
|
3
|
+
import { p as Request, q as Response, w as SessionEvent } from '../context-zkZeILpr.js';
|
|
3
4
|
import { S as SessionRewinder, C as CheckpointInfo, a as RewindResultExtended } from '../session-rewinder-C9HnMkhP.js';
|
|
4
5
|
export { D as DirectorStateCheckpoint, a as DirectorStateSnapshot, b as DirectorSubagentState, c as DirectorTaskState, l as loadDirectorState } from '../director-state-BmYi3DGA.js';
|
|
5
6
|
export { G as GoalFile, J as JournalEntry, M as MAX_JOURNAL_ENTRIES, a as appendJournal, e as emptyGoal, f as formatGoal, g as goalFilePath, l as loadGoal, s as saveGoal, b as summarizeUsage } from '../goal-store-C7jcumEh.js';
|
|
6
7
|
import { a as WstackPaths } from '../wstack-paths-BCgmTNlG.js';
|
|
7
|
-
import { S as SyncCategory, m as SyncConfig } from '../config-
|
|
8
|
-
import '../events-
|
|
9
|
-
import '../context-BRNbHmRM.js';
|
|
8
|
+
import { S as SyncCategory, m as SyncConfig } from '../config-CLXMDOSs.js';
|
|
9
|
+
import '../events-DH-9r-_C.js';
|
|
10
10
|
import '../secret-scrubber-3MHDDAtm.js';
|
|
11
11
|
import '../memory-CEXuo7sz.js';
|
|
12
12
|
import '../secret-vault-DoISxaKO.js';
|
|
13
13
|
import '../models-registry-BcYJDKLm.js';
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* L2-B: AnnotationsStore — sidecar storage for collaboration annotations
|
|
17
|
+
* (Phase 2 of idea #13 from IDEAS.md).
|
|
18
|
+
*
|
|
19
|
+
* Why a sidecar file, not the session JSONL?
|
|
20
|
+
*
|
|
21
|
+
* The session log is an event-sourced append-only journal
|
|
22
|
+
* (`packages/core/src/types/session.ts` invariant: events are
|
|
23
|
+
* append-only, with `truncateToCheckpoint` only as an explicit
|
|
24
|
+
* rewind). Mixing in human-typed annotations would break that
|
|
25
|
+
* invariant — the user's note about "this rm looks dangerous"
|
|
26
|
+
* is not part of the agent's event history; it is meta-commentary
|
|
27
|
+
* on the history.
|
|
28
|
+
*
|
|
29
|
+
* So we keep annotations in a sibling file: one JSON document per
|
|
30
|
+
* session, at `<sessionDir>/<sessionId>.annotations.json`. The
|
|
31
|
+
* shape is a simple versioned array, written atomically.
|
|
32
|
+
*
|
|
33
|
+
* Concurrency model:
|
|
34
|
+
*
|
|
35
|
+
* The store uses a per-session Promise chain to serialize writes.
|
|
36
|
+
* Multiple annotators adding notes at the same time will queue,
|
|
37
|
+
* not race. The atomic write itself is the second line of
|
|
38
|
+
* defense (in case the chain is bypassed — e.g. two processes
|
|
39
|
+
* pointing at the same dir).
|
|
40
|
+
*/
|
|
41
|
+
/** Wire/storage shape for one annotation. */
|
|
42
|
+
interface Annotation {
|
|
43
|
+
/** Stable id (UUIDv4-ish). Referenced by resolve/delete. */
|
|
44
|
+
id: string;
|
|
45
|
+
/** Session this annotation belongs to. */
|
|
46
|
+
sessionId: string;
|
|
47
|
+
/** Index into the session event log the annotation refers to. */
|
|
48
|
+
atEventIndex: number;
|
|
49
|
+
/** Participant id of the annotator (matches WSCollabParticipantJoined.participantId). */
|
|
50
|
+
authorId: string;
|
|
51
|
+
/** Human-readable role label snapshot for display (e.g. "annotator"). */
|
|
52
|
+
authorRole: 'annotator';
|
|
53
|
+
/** The note itself. Trimmed, capped at `MAX_TEXT_LENGTH` on add. */
|
|
54
|
+
text: string;
|
|
55
|
+
/** ISO timestamp of creation. */
|
|
56
|
+
createdAt: string;
|
|
57
|
+
/** Resolved state. Annotations start unresolved. */
|
|
58
|
+
resolved: boolean;
|
|
59
|
+
/** ISO timestamp when resolved (if resolved). */
|
|
60
|
+
resolvedAt?: string;
|
|
61
|
+
/** Participant id of the resolver (if resolved). */
|
|
62
|
+
resolvedBy?: string;
|
|
63
|
+
}
|
|
64
|
+
interface AnnotationsStoreOptions {
|
|
65
|
+
/** Directory where `<sessionId>.annotations.json` files live. */
|
|
66
|
+
dir: string;
|
|
67
|
+
}
|
|
68
|
+
declare class AnnotationsStore {
|
|
69
|
+
private readonly dir;
|
|
70
|
+
/** Per-session write queue. Created lazily on first add. */
|
|
71
|
+
private readonly writeChains;
|
|
72
|
+
constructor(opts: AnnotationsStoreOptions);
|
|
73
|
+
/**
|
|
74
|
+
* Return all annotations for `sessionId` in insertion order
|
|
75
|
+
* (oldest first). Returns an empty array when no file exists
|
|
76
|
+
* yet (the normal case for a fresh session).
|
|
77
|
+
*/
|
|
78
|
+
list(sessionId: string): Promise<Annotation[]>;
|
|
79
|
+
/**
|
|
80
|
+
* Convenience: only unresolved annotations, newest first — the
|
|
81
|
+
* common UI rendering for "what still needs attention?".
|
|
82
|
+
*/
|
|
83
|
+
listOpen(sessionId: string): Promise<Annotation[]>;
|
|
84
|
+
/**
|
|
85
|
+
* Add a new annotation. Returns the persisted record (with id
|
|
86
|
+
* and timestamps filled in). Throws when `text` is empty or
|
|
87
|
+
* exceeds `MAX_TEXT_LENGTH`.
|
|
88
|
+
*/
|
|
89
|
+
add(input: {
|
|
90
|
+
sessionId: string;
|
|
91
|
+
atEventIndex: number;
|
|
92
|
+
authorId: string;
|
|
93
|
+
text: string;
|
|
94
|
+
}): Promise<Annotation>;
|
|
95
|
+
/**
|
|
96
|
+
* Mark an annotation as resolved. Returns the updated record, or
|
|
97
|
+
* `null` if no annotation with that id exists in this session.
|
|
98
|
+
* Idempotent: resolving an already-resolved annotation refreshes
|
|
99
|
+
* `resolvedAt` / `resolvedBy` to the latest call.
|
|
100
|
+
*/
|
|
101
|
+
resolve(input: {
|
|
102
|
+
sessionId: string;
|
|
103
|
+
annotationId: string;
|
|
104
|
+
resolvedBy: string;
|
|
105
|
+
}): Promise<Annotation | null>;
|
|
106
|
+
private filePath;
|
|
107
|
+
private readFile;
|
|
108
|
+
private writeFile;
|
|
109
|
+
/**
|
|
110
|
+
* Serialize writes per-sessionId. We chain promises instead of
|
|
111
|
+
* using a Mutex class so the contract is obvious from the
|
|
112
|
+
* call-site: `enqueue(sid, fn)` runs `fn` after every prior
|
|
113
|
+
* enqueue for `sid` has settled.
|
|
114
|
+
*/
|
|
115
|
+
private enqueue;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* ReplayLogStore — sidecar store for deterministic-replay support
|
|
120
|
+
* (idea #2 from IDEAS.md). One JSONL file per session, recording
|
|
121
|
+
* every provider request/response pair so the same agent loop can
|
|
122
|
+
* be re-run later with frozen API responses.
|
|
123
|
+
*
|
|
124
|
+
* Why a sidecar (not the session JSONL)?
|
|
125
|
+
*
|
|
126
|
+
* Same reason as `AnnotationsStore` — the session log is
|
|
127
|
+
* event-sourced and append-only; a provider request payload can be
|
|
128
|
+
* tens of kilobytes (especially with long conversation history),
|
|
129
|
+
* and we want replay to be opt-in (recorded only when the user
|
|
130
|
+
* runs with `--replay` or a future equivalent). Mixing it into
|
|
131
|
+
* the event log would inflate every read for replay-irrelevant
|
|
132
|
+
* paths.
|
|
133
|
+
*
|
|
134
|
+
* File layout: `<dir>/<sessionId>.replay.jsonl`, one entry per line.
|
|
135
|
+
* Each entry: `{ hash, ts, request, response }`. The `hash` is
|
|
136
|
+
* computed via `hashRequest` so lookups are O(1) by hash.
|
|
137
|
+
*
|
|
138
|
+
* Concurrency: per-session write queue (same pattern as
|
|
139
|
+
* `AnnotationsStore`). Reads are lock-free; the write chain makes
|
|
140
|
+
* the append + rehash sequence atomic.
|
|
141
|
+
*/
|
|
142
|
+
interface ReplayEntry {
|
|
143
|
+
hash: string;
|
|
144
|
+
ts: string;
|
|
145
|
+
request: Request;
|
|
146
|
+
response: Response;
|
|
147
|
+
}
|
|
148
|
+
interface ReplayLogStoreOptions {
|
|
149
|
+
/** Directory where `<sessionId>.replay.jsonl` files live. */
|
|
150
|
+
dir: string;
|
|
151
|
+
/**
|
|
152
|
+
* Cap on the number of entries per session. When a `record` would
|
|
153
|
+
* push the file beyond this, the oldest entries are evicted (LRU
|
|
154
|
+
* by insertion order). Set to `Infinity` to disable rotation.
|
|
155
|
+
* Defaults to 1000 — a single LLM call averages ~5KB serialized
|
|
156
|
+
* (messages + tools + response), so 1000 entries is ~5MB per
|
|
157
|
+
* session which is a reasonable upper bound.
|
|
158
|
+
*/
|
|
159
|
+
maxEntries?: number;
|
|
160
|
+
}
|
|
161
|
+
declare class ReplayLogStore {
|
|
162
|
+
private readonly dir;
|
|
163
|
+
private readonly writeChains;
|
|
164
|
+
/** Per-session hash → entry index, kept in memory after the first load. */
|
|
165
|
+
private readonly cache;
|
|
166
|
+
private readonly maxEntries;
|
|
167
|
+
constructor(opts: ReplayLogStoreOptions);
|
|
168
|
+
/**
|
|
169
|
+
* Record a request/response pair. Idempotent on hash: a second
|
|
170
|
+
* `record` for the same hash is a no-op (the existing entry wins).
|
|
171
|
+
* Returns the hash.
|
|
172
|
+
*/
|
|
173
|
+
record(input: {
|
|
174
|
+
sessionId: string;
|
|
175
|
+
request: Request;
|
|
176
|
+
response: Response;
|
|
177
|
+
}): Promise<string>;
|
|
178
|
+
/**
|
|
179
|
+
* Look up an entry by hash. Returns `null` when the request has
|
|
180
|
+
* not been recorded for this session. O(1) after the first call
|
|
181
|
+
* per session (in-memory cache).
|
|
182
|
+
*/
|
|
183
|
+
lookup(sessionId: string, hash: string): Promise<ReplayEntry | null>;
|
|
184
|
+
/** All recorded entries for a session, in insertion order. */
|
|
185
|
+
load(sessionId: string): Promise<ReplayEntry[]>;
|
|
186
|
+
/**
|
|
187
|
+
* List every session id that has a replay log in the store dir.
|
|
188
|
+
* Returns an array of `{ sessionId, entryCount, path }` sorted
|
|
189
|
+
* by sessionId for stable output. Used by `wstack replay --list`.
|
|
190
|
+
*/
|
|
191
|
+
list(): Promise<Array<{
|
|
192
|
+
sessionId: string;
|
|
193
|
+
entryCount: number;
|
|
194
|
+
path: string;
|
|
195
|
+
}>>;
|
|
196
|
+
private filePath;
|
|
197
|
+
private readAll;
|
|
198
|
+
private writeAll;
|
|
199
|
+
private ensureCache;
|
|
200
|
+
private enqueue;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Idea #1 from IDEAS.md — Stateful Session Recovery.
|
|
205
|
+
*
|
|
206
|
+
* `SessionRecovery` is the read-side companion to the in-flight
|
|
207
|
+
* marker mechanism. When the agent loop is running, it writes an
|
|
208
|
+
* `in_flight_start` event at the current point in the log. On
|
|
209
|
+
* clean shutdown, a matching `in_flight_end` follows. If the
|
|
210
|
+
* process dies (crash, OOM, machine sleep, SIGKILL) the marker
|
|
211
|
+
* is the last event in the file — and `detectStale` flags the
|
|
212
|
+
* session as "incomplete, can be resumed".
|
|
213
|
+
*
|
|
214
|
+
* Phase 1 of this feature is **detection only**. The actual
|
|
215
|
+
* re-execution of incomplete work is a follow-up: it requires
|
|
216
|
+
* tracking pending tool calls, mid-stream LLM responses, and
|
|
217
|
+
* uncommitted file changes — and re-running the agent loop from
|
|
218
|
+
* the last `checkpoint` event. The detection layer is independent
|
|
219
|
+
* and ships first because (a) it gives the user immediate
|
|
220
|
+
* visibility into what died, and (b) it's the foundation for the
|
|
221
|
+
* resume command and the CLI's "Incomplete sessions" surface.
|
|
222
|
+
*
|
|
223
|
+
* Concurrency: pure read; no writes. Safe to call from multiple
|
|
224
|
+
* processes simultaneously.
|
|
225
|
+
*/
|
|
226
|
+
interface StaleSession {
|
|
227
|
+
sessionId: string;
|
|
228
|
+
/** Path to the JSONL log. */
|
|
229
|
+
path: string;
|
|
230
|
+
/** Last event ts (the in_flight_start timestamp). */
|
|
231
|
+
lastEventTs: string;
|
|
232
|
+
/** Context the agent was working on when it died. */
|
|
233
|
+
context: string;
|
|
234
|
+
/** Total events in the log. */
|
|
235
|
+
eventCount: number;
|
|
236
|
+
}
|
|
237
|
+
interface RecoveryPlan {
|
|
238
|
+
sessionId: string;
|
|
239
|
+
/** True if the session is stale (has a dangling in_flight_start). */
|
|
240
|
+
stale: boolean;
|
|
241
|
+
/** The last `checkpoint` event before the un-replayed work, or null. */
|
|
242
|
+
lastCheckpoint: SessionEvent | null;
|
|
243
|
+
/** All events after the last checkpoint (i.e. the work that needs re-execution). */
|
|
244
|
+
pendingEvents: SessionEvent[];
|
|
245
|
+
/** The dangling in_flight_start event, if any. */
|
|
246
|
+
inFlightStart: SessionEvent | null;
|
|
247
|
+
/** Free-form context the agent was working on, if any. */
|
|
248
|
+
context: string | null;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Result of `SessionRecovery.recover(sessionId)`. Distinct from
|
|
252
|
+
* `StaleSession`: a session is "stale" if the last event is an
|
|
253
|
+
* open marker, but a "recovery plan" can also be generated for
|
|
254
|
+
* clean sessions whose last checkpoint is older than the
|
|
255
|
+
* conversation history (e.g. a user-initiated "rewind to last
|
|
256
|
+
* good state" flow). Phase 2 of idea #1: this returns the plan;
|
|
257
|
+
* the actual kernel re-execution is a follow-up.
|
|
258
|
+
*/
|
|
259
|
+
declare class SessionRecovery {
|
|
260
|
+
private readonly dir;
|
|
261
|
+
/**
|
|
262
|
+
* Scan a session log and return a `StaleSession` if and only
|
|
263
|
+
* if the last event is an `in_flight_start` without a matching
|
|
264
|
+
* `in_flight_end`. Returns `null` when:
|
|
265
|
+
* - the log does not exist;
|
|
266
|
+
* - the log is empty;
|
|
267
|
+
* - the last event is `in_flight_end` (clean shutdown);
|
|
268
|
+
* - the last event is something else (e.g. an unannotated
|
|
269
|
+
* legacy log without in-flight markers).
|
|
270
|
+
*/
|
|
271
|
+
detectStale(sessionId: string): Promise<StaleSession | null>;
|
|
272
|
+
/**
|
|
273
|
+
* Generate a recovery plan for a session. The plan describes
|
|
274
|
+
* "what would be re-executed" if the user chose to resume —
|
|
275
|
+
* everything after the last `checkpoint` event, plus the
|
|
276
|
+
* dangling in-flight marker if present.
|
|
277
|
+
*
|
|
278
|
+
* Returns a non-null plan for ANY session that has at least
|
|
279
|
+
* one event after a checkpoint (or, for legacy sessions, at
|
|
280
|
+
* least one event). Pure read; no mutation.
|
|
281
|
+
*/
|
|
282
|
+
recover(sessionId: string): Promise<RecoveryPlan | null>;
|
|
283
|
+
/**
|
|
284
|
+
* List every stale session in a directory. Returns an array
|
|
285
|
+
* (possibly empty) sorted by `lastEventTs` descending — most
|
|
286
|
+
* recent crash first.
|
|
287
|
+
*/
|
|
288
|
+
listResumable(): Promise<StaleSession[]>;
|
|
289
|
+
private filePath;
|
|
290
|
+
/**
|
|
291
|
+
* Stream-parse the last few lines of a JSONL log. We do NOT load
|
|
292
|
+
* the whole file into memory — for long-running sessions the log
|
|
293
|
+
* can be megabytes. Instead we read tail-ward and find the last
|
|
294
|
+
* `in_flight_start` / `in_flight_end` pair.
|
|
295
|
+
*/
|
|
296
|
+
private parseForStale;
|
|
297
|
+
constructor(dir: string);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* ToolAuditLog — idea #9 from IDEAS.md.
|
|
302
|
+
*
|
|
303
|
+
* Tamper-evident audit trail for tool calls. Every tool_use /
|
|
304
|
+
* tool_result pair is appended to a sidecar JSONL with a chained
|
|
305
|
+
* SHA-256 — each entry's `prevHash` is the prior entry's `hash`,
|
|
306
|
+
* so any post-hoc modification of a single line breaks the chain
|
|
307
|
+
* from that point forward.
|
|
308
|
+
*
|
|
309
|
+
* Why a sidecar (not the session JSONL)?
|
|
310
|
+
* Same reason as `AnnotationsStore` and `ReplayLogStore`: the
|
|
311
|
+
* session log is an event-sourced journal. Mixing in a hash
|
|
312
|
+
* chain would inflate every read and tightly couple the
|
|
313
|
+
* integrity check to the event format. Sidecar keeps both
|
|
314
|
+
* concerns orthogonal.
|
|
315
|
+
*
|
|
316
|
+
* What "tamper-evident" means here:
|
|
317
|
+
* - The hash covers the full serialized entry: tool name, id,
|
|
318
|
+
* input, output, timestamp, author. Changing any byte
|
|
319
|
+
* changes the hash.
|
|
320
|
+
* - The chain is sequential — a verifier walks the file in
|
|
321
|
+
* order, recomputing each hash, and checks `prevHash`
|
|
322
|
+
* matches the previous entry's `hash`.
|
|
323
|
+
* - Any insertion, deletion, or modification of a single
|
|
324
|
+
* entry surfaces as a "chain broken at entry N" verdict.
|
|
325
|
+
*
|
|
326
|
+
* What it does NOT defend against:
|
|
327
|
+
* - An attacker who rewrites the whole file consistently.
|
|
328
|
+
* For that you'd need an external anchor (signing key,
|
|
329
|
+
* transparency log, etc.) — out of scope for Phase 1.
|
|
330
|
+
* - The agent itself misbehaving; this is post-hoc audit, not
|
|
331
|
+
* real-time enforcement. Use `PermissionPolicy` for that.
|
|
332
|
+
*
|
|
333
|
+
* File layout: `<dir>/<sessionId>.audit.jsonl`, one entry per
|
|
334
|
+
* line. The chain starts with a `genesis` entry whose
|
|
335
|
+
* `prevHash` is all zeros.
|
|
336
|
+
*/
|
|
337
|
+
interface AuditEntry {
|
|
338
|
+
/** Monotonic index (0-based). */
|
|
339
|
+
index: number;
|
|
340
|
+
/** UUID for cross-referencing with logs. */
|
|
341
|
+
id: string;
|
|
342
|
+
/** ISO timestamp. */
|
|
343
|
+
ts: string;
|
|
344
|
+
/** Hash of the previous entry (or all-zeros for the genesis entry). */
|
|
345
|
+
prevHash: string;
|
|
346
|
+
/** Hash of this entry's content (sha256 over the canonical JSON). */
|
|
347
|
+
hash: string;
|
|
348
|
+
toolName: string;
|
|
349
|
+
toolUseId: string;
|
|
350
|
+
input: unknown;
|
|
351
|
+
output: unknown;
|
|
352
|
+
isError: boolean;
|
|
353
|
+
}
|
|
354
|
+
type VerifyResult = {
|
|
355
|
+
ok: true;
|
|
356
|
+
entries: number;
|
|
357
|
+
} | {
|
|
358
|
+
ok: false;
|
|
359
|
+
brokenAt: number;
|
|
360
|
+
reason: string;
|
|
361
|
+
};
|
|
362
|
+
interface ToolAuditLogOptions {
|
|
363
|
+
/** Directory where `<sessionId>.audit.jsonl` files live. */
|
|
364
|
+
dir: string;
|
|
365
|
+
}
|
|
366
|
+
declare class ToolAuditLog {
|
|
367
|
+
private readonly dir;
|
|
368
|
+
/** In-memory cache of the last entry's hash (per session), to compute chains efficiently. */
|
|
369
|
+
private readonly tailHash;
|
|
370
|
+
private readonly writeChains;
|
|
371
|
+
constructor(opts: ToolAuditLogOptions);
|
|
372
|
+
/**
|
|
373
|
+
* Append a tool call/result pair to the chain. Returns the
|
|
374
|
+
* resulting entry. Idempotency is not guaranteed — if you
|
|
375
|
+
* record the same tool_use twice you get two entries. That's
|
|
376
|
+
* intentional: the audit log is a record, not a cache.
|
|
377
|
+
*/
|
|
378
|
+
record(input: {
|
|
379
|
+
sessionId: string;
|
|
380
|
+
toolName: string;
|
|
381
|
+
toolUseId: string;
|
|
382
|
+
input: unknown;
|
|
383
|
+
output: unknown;
|
|
384
|
+
isError: boolean;
|
|
385
|
+
}): Promise<AuditEntry>;
|
|
386
|
+
/**
|
|
387
|
+
* Walk the chain and verify every entry's hash and prevHash.
|
|
388
|
+
* Returns a structured verdict — never throws.
|
|
389
|
+
*/
|
|
390
|
+
verify(sessionId: string): Promise<VerifyResult>;
|
|
391
|
+
/** All entries for a session, in insertion order. */
|
|
392
|
+
load(sessionId: string): Promise<AuditEntry[]>;
|
|
393
|
+
private filePath;
|
|
394
|
+
private readAll;
|
|
395
|
+
private appendLine;
|
|
396
|
+
private enqueue;
|
|
397
|
+
}
|
|
398
|
+
|
|
15
399
|
interface SessionRewinderOptions {
|
|
16
400
|
sessionsDir: string;
|
|
17
401
|
/** The project root directory; used to validate rewind targets stay inside it. */
|
|
@@ -100,4 +484,4 @@ declare class CloudSync {
|
|
|
100
484
|
private walkDir;
|
|
101
485
|
}
|
|
102
486
|
|
|
103
|
-
export { ALL_SYNC_CATEGORIES, CloudSync, DefaultPromptStore, DefaultSessionRewinder, type PromptEntry, type PromptStore, type SessionRewinderOptions, SyncCategory, SyncConfig, type SyncResult };
|
|
487
|
+
export { ALL_SYNC_CATEGORIES, type Annotation, AnnotationsStore, type AnnotationsStoreOptions, type AuditEntry, CloudSync, DefaultPromptStore, DefaultSessionRewinder, type PromptEntry, type PromptStore, type RecoveryPlan, type ReplayEntry, ReplayLogStore, type ReplayLogStoreOptions, SessionRecovery, type SessionRewinderOptions, type StaleSession, SyncCategory, SyncConfig, type SyncResult, ToolAuditLog, type ToolAuditLogOptions, type VerifyResult };
|