@bernierllc/agent-changeset 0.0.1 → 0.2.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 +7 -0
- package/README.md +159 -43
- package/dist/changeset.d.ts +19 -0
- package/dist/changeset.d.ts.map +1 -0
- package/dist/changeset.js +160 -0
- package/dist/changeset.js.map +1 -0
- package/dist/errors.d.ts +30 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +61 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/recorder.d.ts +13 -0
- package/dist/recorder.d.ts.map +1 -0
- package/dist/recorder.js +82 -0
- package/dist/recorder.js.map +1 -0
- package/dist/types.d.ts +111 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/package.json +53 -7
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2025 Bernier LLC
|
|
3
|
+
|
|
4
|
+
This file is licensed to the client under a limited-use license.
|
|
5
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
6
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
7
|
+
*/
|
package/README.md
CHANGED
|
@@ -1,45 +1,161 @@
|
|
|
1
1
|
# @bernierllc/agent-changeset
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
3
|
+
Records mutations made by agent tools during a conversation and produces structured user-facing change reports. Accumulates `ChangeRecord` entries (dot-notation actions, before/after state) per conversation, renders per-change one-line summaries for tool-result cards, computes field-level diffs, and produces an end-of-conversation rollup summary (markdown + plain text). Serializable via `toJSON()`/`fromJSON()` for persistence alongside conversation messages.
|
|
4
|
+
|
|
5
|
+
Designed to be installed as an `@bernierllc/agent-tool-registry` after-hook.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @bernierllc/agent-changeset
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { createChangesetRecorder } from '@bernierllc/agent-changeset';
|
|
17
|
+
|
|
18
|
+
const recorder = createChangesetRecorder();
|
|
19
|
+
const changeset = recorder.forConversation('conv-abc123');
|
|
20
|
+
|
|
21
|
+
// Record mutations from tool calls
|
|
22
|
+
changeset.record({
|
|
23
|
+
action: 'admin.service.add',
|
|
24
|
+
resourceType: 'service',
|
|
25
|
+
resourceId: 'svc-999',
|
|
26
|
+
after: { name: 'Haircut', price: 45, durationMinutes: 30 },
|
|
27
|
+
summary: 'Added service "Haircut" at $45 (30 min)',
|
|
28
|
+
toolCallId: 'tc-001',
|
|
29
|
+
success: true,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
changeset.record({
|
|
33
|
+
action: 'admin.discount.create',
|
|
34
|
+
resourceType: 'discount',
|
|
35
|
+
resourceId: 'disc-42',
|
|
36
|
+
after: { code: 'SAVE10', percentOff: 10 },
|
|
37
|
+
summary: 'Created discount code "SAVE10" (10% off)',
|
|
38
|
+
toolCallId: 'tc-002',
|
|
39
|
+
success: true,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Per-change line summary (for tool-result cards in the UI)
|
|
43
|
+
const [first] = changeset.changes;
|
|
44
|
+
console.log(changeset.renderLineSummary(first.id));
|
|
45
|
+
// '+ Added service "Haircut" at $45 (30 min)'
|
|
46
|
+
|
|
47
|
+
// End-of-conversation rollup
|
|
48
|
+
const rollup = changeset.renderRollupSummary();
|
|
49
|
+
console.log(rollup.markdown);
|
|
50
|
+
// ### Changes Made This Session
|
|
51
|
+
// **Service** (1 change)
|
|
52
|
+
// - Added service "Haircut" at $45 (30 min)
|
|
53
|
+
//
|
|
54
|
+
// **Discount** (1 change)
|
|
55
|
+
// - Created discount code "SAVE10" (10% off)
|
|
56
|
+
|
|
57
|
+
console.log(rollup.plainText);
|
|
58
|
+
// 'Here is what changed this session: Service: Added service "Haircut" at $45 (30 min). Discount: Created discount code "SAVE10" (10% off).'
|
|
59
|
+
|
|
60
|
+
// Persist alongside conversation messages
|
|
61
|
+
const json = changeset.toJSON();
|
|
62
|
+
// store json to your database / agent-conversation-store ...
|
|
63
|
+
|
|
64
|
+
// Restore from JSON on reconnect
|
|
65
|
+
const restored = recorder.fromJSON(json);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Field-Level Diffs
|
|
69
|
+
|
|
70
|
+
For changes that carry both `before` and `after`, `computeDiff()` reports added keys, removed keys, and modified fields:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
changeset.record({
|
|
74
|
+
action: 'admin.service.update',
|
|
75
|
+
resourceType: 'service',
|
|
76
|
+
resourceId: 'svc-999',
|
|
77
|
+
before: { name: 'Haircut', price: 40, durationMinutes: 30 },
|
|
78
|
+
after: { name: 'Haircut', price: 45, durationMinutes: 30, active: true },
|
|
79
|
+
summary: 'Updated service "Haircut" — price $40 to $45, added active flag',
|
|
80
|
+
toolCallId: 'tc-003',
|
|
81
|
+
success: true,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const updateChange = changeset.changes[2];
|
|
85
|
+
const diff = changeset.computeDiff(updateChange.id);
|
|
86
|
+
// {
|
|
87
|
+
// added: ['active'],
|
|
88
|
+
// removed: [],
|
|
89
|
+
// modified: [{ field: 'price', from: 40, to: 45 }]
|
|
90
|
+
// }
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Returns `null` for create-only (no `before`) or delete-only (no `after`) mutations.
|
|
94
|
+
|
|
95
|
+
## After-Hook Pattern
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import { createChangesetRecorder } from '@bernierllc/agent-changeset';
|
|
99
|
+
|
|
100
|
+
const recorder = createChangesetRecorder();
|
|
101
|
+
const changeset = recorder.forConversation(conversationId);
|
|
102
|
+
|
|
103
|
+
toolRegistry.addHook({
|
|
104
|
+
after: async (record, _ctx) => {
|
|
105
|
+
if (record.state === 'success' && record.output?.success === true) {
|
|
106
|
+
changeset.record({
|
|
107
|
+
action: resolveAction(record.toolName),
|
|
108
|
+
resourceType: record.toolName,
|
|
109
|
+
toolCallId: record.toolCallId,
|
|
110
|
+
summary: String(record.output.message ?? record.toolName),
|
|
111
|
+
success: true,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## API Reference
|
|
119
|
+
|
|
120
|
+
### `createChangesetRecorder(): ChangesetRecorder`
|
|
121
|
+
|
|
122
|
+
Entry point. Returns a recorder that creates and restores `ConversationChangeset` instances.
|
|
123
|
+
|
|
124
|
+
### `ChangesetRecorder`
|
|
125
|
+
|
|
126
|
+
| Method | Description |
|
|
127
|
+
|---|---|
|
|
128
|
+
| `forConversation(id)` | Create a fresh changeset for the conversation. |
|
|
129
|
+
| `fromJSON(data)` | Restore from `SerializedChangeset`. Throws `InvalidChangesetDataError` if malformed. |
|
|
130
|
+
|
|
131
|
+
### `ConversationChangeset`
|
|
132
|
+
|
|
133
|
+
| Method | Description |
|
|
134
|
+
|---|---|
|
|
135
|
+
| `record(entry)` | Add a mutation. Auto-generates `id` (UUID v4) and `timestamp` (ISO 8601). Returns the stored `ChangeRecord`. |
|
|
136
|
+
| `renderLineSummary(changeId)` | One-liner: `"+ summary"` for success, `"- summary"` for failure. Throws `ChangeNotFoundError` if unknown id. |
|
|
137
|
+
| `computeDiff(changeId)` | Field-level diff from `before`/`after`. Returns `null` if either is absent. Throws `ChangeNotFoundError` if unknown id. |
|
|
138
|
+
| `renderRollupSummary()` | End-of-conversation rollup with `totalChanges`, `successfulChanges`, `failedChanges`, `byResourceType`, `markdown`, `plainText`. |
|
|
139
|
+
| `toJSON()` | Plain `SerializedChangeset` for persistence. |
|
|
140
|
+
| `changes` | Read-only ordered array of recorded `ChangeRecord`s. |
|
|
141
|
+
| `conversationId` | The conversation identifier. |
|
|
142
|
+
|
|
143
|
+
### Error Classes
|
|
144
|
+
|
|
145
|
+
| Class | Code | When thrown |
|
|
146
|
+
|---|---|---|
|
|
147
|
+
| `ChangesetError` | `CHANGESET_ERROR` | Base class for all package errors. |
|
|
148
|
+
| `ChangeNotFoundError` | `CHANGE_NOT_FOUND` | `renderLineSummary` or `computeDiff` called with unknown `changeId`. |
|
|
149
|
+
| `InvalidChangesetDataError` | `INVALID_CHANGESET_DATA` | `fromJSON` called with malformed data. |
|
|
150
|
+
|
|
151
|
+
All errors chain the root cause via `Error.cause` and carry a `context` object with debugging details.
|
|
152
|
+
|
|
153
|
+
## Architecture
|
|
154
|
+
|
|
155
|
+
- **Core tier** — pure TypeScript, zero runtime dependencies beyond `@bernierllc/logger`.
|
|
156
|
+
- **No I/O** — pure in-memory computation; runs server-side as part of the agent service layer.
|
|
157
|
+
- **MECE** — owns only mutation recording and diff rendering for agent conversations. Persistence is `agent-conversation-store`'s concern. Database audit logging is `admin-agent-service`'s concern.
|
|
158
|
+
|
|
159
|
+
## License
|
|
160
|
+
|
|
161
|
+
Bernier LLC limited-use license. See [LICENSE](./LICENSE).
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ChangeRecord, ChangesetDiff, ConversationChangeset, RecordInput, RollupSummary, SerializedChangeset } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Implementation of `ConversationChangeset`.
|
|
4
|
+
* Not exported directly — consumers obtain instances via `createChangesetRecorder`.
|
|
5
|
+
*/
|
|
6
|
+
export declare class ConversationChangesetImpl implements ConversationChangeset {
|
|
7
|
+
readonly conversationId: string;
|
|
8
|
+
private readonly _changes;
|
|
9
|
+
get changes(): ReadonlyArray<ChangeRecord>;
|
|
10
|
+
constructor(conversationId: string, initialChanges?: ChangeRecord[]);
|
|
11
|
+
record(entry: RecordInput): ChangeRecord;
|
|
12
|
+
renderLineSummary(changeId: string): string;
|
|
13
|
+
computeDiff(changeId: string): ChangesetDiff | null;
|
|
14
|
+
renderRollupSummary(): RollupSummary;
|
|
15
|
+
toJSON(): SerializedChangeset;
|
|
16
|
+
/** @throws {ChangeNotFoundError} */
|
|
17
|
+
private findChange;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=changeset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changeset.d.ts","sourceRoot":"","sources":["../src/changeset.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,qBAAqB,EACrB,WAAW,EACX,aAAa,EACb,mBAAmB,EACpB,MAAM,SAAS,CAAC;AAejB;;;GAGG;AACH,qBAAa,yBAA0B,YAAW,qBAAqB;IACrE,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;IAE1C,IAAI,OAAO,IAAI,aAAa,CAAC,YAAY,CAAC,CAEzC;gBAEW,cAAc,EAAE,MAAM,EAAE,cAAc,GAAE,YAAY,EAAO;IAKvE,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY;IAiBxC,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAM3C,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IAuCnD,mBAAmB,IAAI,aAAa;IA2DpC,MAAM,IAAI,mBAAmB;IAO7B,oCAAoC;IACpC,OAAO,CAAC,UAAU;CAOnB"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.ConversationChangesetImpl = void 0;
|
|
11
|
+
const crypto_1 = require("crypto");
|
|
12
|
+
const logger_1 = require("@bernierllc/logger");
|
|
13
|
+
const errors_1 = require("./errors");
|
|
14
|
+
const logger = (0, logger_1.createLogger)();
|
|
15
|
+
/**
|
|
16
|
+
* Capitalises the first character of a string.
|
|
17
|
+
* Handles empty strings and `noUncheckedIndexedAccess` safely.
|
|
18
|
+
*/
|
|
19
|
+
function capitaliseFirst(str) {
|
|
20
|
+
if (str.length === 0)
|
|
21
|
+
return str;
|
|
22
|
+
const first = str[0];
|
|
23
|
+
if (first === undefined)
|
|
24
|
+
return str;
|
|
25
|
+
return first.toUpperCase() + str.slice(1);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Implementation of `ConversationChangeset`.
|
|
29
|
+
* Not exported directly — consumers obtain instances via `createChangesetRecorder`.
|
|
30
|
+
*/
|
|
31
|
+
class ConversationChangesetImpl {
|
|
32
|
+
conversationId;
|
|
33
|
+
_changes;
|
|
34
|
+
get changes() {
|
|
35
|
+
return this._changes;
|
|
36
|
+
}
|
|
37
|
+
constructor(conversationId, initialChanges = []) {
|
|
38
|
+
this.conversationId = conversationId;
|
|
39
|
+
this._changes = [...initialChanges];
|
|
40
|
+
}
|
|
41
|
+
record(entry) {
|
|
42
|
+
const change = {
|
|
43
|
+
...entry,
|
|
44
|
+
id: (0, crypto_1.randomUUID)(),
|
|
45
|
+
timestamp: new Date().toISOString(),
|
|
46
|
+
};
|
|
47
|
+
this._changes.push(change);
|
|
48
|
+
logger.debug('agent-changeset: change recorded', {
|
|
49
|
+
changeId: change.id,
|
|
50
|
+
action: change.action,
|
|
51
|
+
resourceType: change.resourceType,
|
|
52
|
+
toolCallId: change.toolCallId,
|
|
53
|
+
success: change.success,
|
|
54
|
+
});
|
|
55
|
+
return change;
|
|
56
|
+
}
|
|
57
|
+
renderLineSummary(changeId) {
|
|
58
|
+
const change = this.findChange(changeId);
|
|
59
|
+
const prefix = change.success ? '+' : '-';
|
|
60
|
+
return `${prefix} ${change.summary}`;
|
|
61
|
+
}
|
|
62
|
+
computeDiff(changeId) {
|
|
63
|
+
const change = this.findChange(changeId);
|
|
64
|
+
if (change.before === undefined || change.after === undefined) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
const before = change.before;
|
|
68
|
+
const after = change.after;
|
|
69
|
+
const beforeKeys = new Set(Object.keys(before));
|
|
70
|
+
const afterKeys = new Set(Object.keys(after));
|
|
71
|
+
const added = [];
|
|
72
|
+
const removed = [];
|
|
73
|
+
const modified = [];
|
|
74
|
+
for (const key of afterKeys) {
|
|
75
|
+
if (!beforeKeys.has(key)) {
|
|
76
|
+
added.push(key);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
for (const key of beforeKeys) {
|
|
80
|
+
if (!afterKeys.has(key)) {
|
|
81
|
+
removed.push(key);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// Both objects have this key; compare values structurally.
|
|
85
|
+
// Use JSON.stringify for deep equality of unknown values.
|
|
86
|
+
const fromVal = before[key];
|
|
87
|
+
const toVal = after[key];
|
|
88
|
+
if (JSON.stringify(fromVal) !== JSON.stringify(toVal)) {
|
|
89
|
+
modified.push({ field: key, from: fromVal, to: toVal });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return { added, removed, modified };
|
|
94
|
+
}
|
|
95
|
+
renderRollupSummary() {
|
|
96
|
+
const totalChanges = this._changes.length;
|
|
97
|
+
const successfulChanges = this._changes.filter((c) => c.success).length;
|
|
98
|
+
const failedChanges = totalChanges - successfulChanges;
|
|
99
|
+
// Group by resourceType using a Map to avoid noUncheckedIndexedAccess issues.
|
|
100
|
+
const groups = new Map();
|
|
101
|
+
for (const change of this._changes) {
|
|
102
|
+
let group = groups.get(change.resourceType);
|
|
103
|
+
if (group === undefined) {
|
|
104
|
+
group = { count: 0, lines: [] };
|
|
105
|
+
groups.set(change.resourceType, group);
|
|
106
|
+
}
|
|
107
|
+
group.count++;
|
|
108
|
+
// Rollup lines strip the +/- prefix and expose only the summary text.
|
|
109
|
+
group.lines.push(change.summary);
|
|
110
|
+
}
|
|
111
|
+
// Build byResourceType plain object for the public interface.
|
|
112
|
+
const byResourceType = {};
|
|
113
|
+
for (const [type, group] of groups) {
|
|
114
|
+
byResourceType[type] = group;
|
|
115
|
+
}
|
|
116
|
+
// Render markdown.
|
|
117
|
+
const markdownSections = ['### Changes Made This Session'];
|
|
118
|
+
const plainTextParts = [];
|
|
119
|
+
for (const [type, group] of groups) {
|
|
120
|
+
const heading = capitaliseFirst(type);
|
|
121
|
+
const countLabel = group.count === 1 ? '1 change' : `${group.count} changes`;
|
|
122
|
+
markdownSections.push(`**${heading}** (${countLabel})`);
|
|
123
|
+
for (const line of group.lines) {
|
|
124
|
+
markdownSections.push(`- ${line}`);
|
|
125
|
+
}
|
|
126
|
+
markdownSections.push('');
|
|
127
|
+
plainTextParts.push(`${heading}: ${group.lines.join('; ')}`);
|
|
128
|
+
}
|
|
129
|
+
const markdown = totalChanges === 0
|
|
130
|
+
? '### Changes Made This Session\n\nNo changes were made this session.'
|
|
131
|
+
: markdownSections.join('\n').trimEnd();
|
|
132
|
+
const plainText = totalChanges === 0
|
|
133
|
+
? 'No changes were made this session.'
|
|
134
|
+
: `Here is what changed this session: ${plainTextParts.join('. ')}.`;
|
|
135
|
+
return {
|
|
136
|
+
totalChanges,
|
|
137
|
+
successfulChanges,
|
|
138
|
+
failedChanges,
|
|
139
|
+
byResourceType,
|
|
140
|
+
plainText,
|
|
141
|
+
markdown,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
toJSON() {
|
|
145
|
+
return {
|
|
146
|
+
conversationId: this.conversationId,
|
|
147
|
+
changes: [...this._changes],
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/** @throws {ChangeNotFoundError} */
|
|
151
|
+
findChange(changeId) {
|
|
152
|
+
const change = this._changes.find((c) => c.id === changeId);
|
|
153
|
+
if (change === undefined) {
|
|
154
|
+
throw new errors_1.ChangeNotFoundError(changeId);
|
|
155
|
+
}
|
|
156
|
+
return change;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
exports.ConversationChangesetImpl = ConversationChangesetImpl;
|
|
160
|
+
//# sourceMappingURL=changeset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changeset.js","sourceRoot":"","sources":["../src/changeset.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error thrown by this package for all exceptional conditions.
|
|
3
|
+
*
|
|
4
|
+
* Uses ES2022 `Error.cause` chaining per the BernierLLC error propagation
|
|
5
|
+
* standard so underlying errors are never swallowed.
|
|
6
|
+
*/
|
|
7
|
+
export declare class ChangesetError extends Error {
|
|
8
|
+
readonly code: string;
|
|
9
|
+
readonly context?: Record<string, unknown>;
|
|
10
|
+
constructor(message: string, options?: {
|
|
11
|
+
cause?: Error;
|
|
12
|
+
code?: string;
|
|
13
|
+
context?: Record<string, unknown>;
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Thrown when a `changeId` passed to `renderLineSummary` or `computeDiff`
|
|
18
|
+
* does not match any recorded change in the changeset.
|
|
19
|
+
*/
|
|
20
|
+
export declare class ChangeNotFoundError extends ChangesetError {
|
|
21
|
+
constructor(changeId: string);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Thrown when `fromJSON` receives data that does not conform to
|
|
25
|
+
* `SerializedChangeset` shape.
|
|
26
|
+
*/
|
|
27
|
+
export declare class InvalidChangesetDataError extends ChangesetError {
|
|
28
|
+
constructor(reason: string, context?: Record<string, unknown>);
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAQA;;;;;GAKG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAGzC,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,KAAK,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACnC;CAUJ;AAED;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,cAAc;gBACzC,QAAQ,EAAE,MAAM;CAQ7B;AAED;;;GAGG;AACH,qBAAa,yBAA0B,SAAQ,cAAc;gBAC/C,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAQ9D"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.InvalidChangesetDataError = exports.ChangeNotFoundError = exports.ChangesetError = void 0;
|
|
11
|
+
/**
|
|
12
|
+
* Error thrown by this package for all exceptional conditions.
|
|
13
|
+
*
|
|
14
|
+
* Uses ES2022 `Error.cause` chaining per the BernierLLC error propagation
|
|
15
|
+
* standard so underlying errors are never swallowed.
|
|
16
|
+
*/
|
|
17
|
+
class ChangesetError extends Error {
|
|
18
|
+
code;
|
|
19
|
+
context;
|
|
20
|
+
constructor(message, options) {
|
|
21
|
+
super(message, { cause: options?.cause });
|
|
22
|
+
this.name = 'ChangesetError';
|
|
23
|
+
this.code = options?.code ?? 'CHANGESET_ERROR';
|
|
24
|
+
if (options?.context !== undefined) {
|
|
25
|
+
this.context = options.context;
|
|
26
|
+
}
|
|
27
|
+
Error.captureStackTrace?.(this, this.constructor);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.ChangesetError = ChangesetError;
|
|
31
|
+
/**
|
|
32
|
+
* Thrown when a `changeId` passed to `renderLineSummary` or `computeDiff`
|
|
33
|
+
* does not match any recorded change in the changeset.
|
|
34
|
+
*/
|
|
35
|
+
class ChangeNotFoundError extends ChangesetError {
|
|
36
|
+
constructor(changeId) {
|
|
37
|
+
super(`Change not found: ${changeId}`, {
|
|
38
|
+
code: 'CHANGE_NOT_FOUND',
|
|
39
|
+
context: { changeId },
|
|
40
|
+
});
|
|
41
|
+
this.name = 'ChangeNotFoundError';
|
|
42
|
+
Error.captureStackTrace?.(this, this.constructor);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.ChangeNotFoundError = ChangeNotFoundError;
|
|
46
|
+
/**
|
|
47
|
+
* Thrown when `fromJSON` receives data that does not conform to
|
|
48
|
+
* `SerializedChangeset` shape.
|
|
49
|
+
*/
|
|
50
|
+
class InvalidChangesetDataError extends ChangesetError {
|
|
51
|
+
constructor(reason, context) {
|
|
52
|
+
super(`Invalid changeset data: ${reason}`, {
|
|
53
|
+
code: 'INVALID_CHANGESET_DATA',
|
|
54
|
+
...(context !== undefined ? { context } : {}),
|
|
55
|
+
});
|
|
56
|
+
this.name = 'InvalidChangesetDataError';
|
|
57
|
+
Error.captureStackTrace?.(this, this.constructor);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.InvalidChangesetDataError = InvalidChangesetDataError;
|
|
61
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":";AAAA;;;;;;EAME;;;AAEF;;;;;GAKG;AACH,MAAa,cAAe,SAAQ,KAAK;IAC9B,IAAI,CAAS;IACb,OAAO,CAA2B;IAE3C,YACE,OAAe,EACf,OAIC;QAED,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QAC1C,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,CAAC,IAAI,GAAG,OAAO,EAAE,IAAI,IAAI,iBAAiB,CAAC;QAC/C,IAAI,OAAO,EAAE,OAAO,KAAK,SAAS,EAAE,CAAC;YACnC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QACjC,CAAC;QACD,KAAK,CAAC,iBAAiB,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACpD,CAAC;CACF;AApBD,wCAoBC;AAED;;;GAGG;AACH,MAAa,mBAAoB,SAAQ,cAAc;IACrD,YAAY,QAAgB;QAC1B,KAAK,CAAC,qBAAqB,QAAQ,EAAE,EAAE;YACrC,IAAI,EAAE,kBAAkB;YACxB,OAAO,EAAE,EAAE,QAAQ,EAAE;SACtB,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;QAClC,KAAK,CAAC,iBAAiB,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACpD,CAAC;CACF;AATD,kDASC;AAED;;;GAGG;AACH,MAAa,yBAA0B,SAAQ,cAAc;IAC3D,YAAY,MAAc,EAAE,OAAiC;QAC3D,KAAK,CAAC,2BAA2B,MAAM,EAAE,EAAE;YACzC,IAAI,EAAE,wBAAwB;YAC9B,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9C,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC;QACxC,KAAK,CAAC,iBAAiB,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACpD,CAAC;CACF;AATD,8DASC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { ChangesetError, ChangeNotFoundError, InvalidChangesetDataError } from './errors';
|
|
2
|
+
export type { ChangeRecord, ChangesetDiff, RollupSummary, SerializedChangeset, RecordInput, ConversationChangeset, ChangesetRecorder, } from './types';
|
|
3
|
+
export { createChangesetRecorder } from './recorder';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAG1F,YAAY,EACV,YAAY,EACZ,aAAa,EACb,aAAa,EACb,mBAAmB,EACnB,WAAW,EACX,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.createChangesetRecorder = exports.InvalidChangesetDataError = exports.ChangeNotFoundError = exports.ChangesetError = void 0;
|
|
11
|
+
// Errors
|
|
12
|
+
var errors_1 = require("./errors");
|
|
13
|
+
Object.defineProperty(exports, "ChangesetError", { enumerable: true, get: function () { return errors_1.ChangesetError; } });
|
|
14
|
+
Object.defineProperty(exports, "ChangeNotFoundError", { enumerable: true, get: function () { return errors_1.ChangeNotFoundError; } });
|
|
15
|
+
Object.defineProperty(exports, "InvalidChangesetDataError", { enumerable: true, get: function () { return errors_1.InvalidChangesetDataError; } });
|
|
16
|
+
// Factory
|
|
17
|
+
var recorder_1 = require("./recorder");
|
|
18
|
+
Object.defineProperty(exports, "createChangesetRecorder", { enumerable: true, get: function () { return recorder_1.createChangesetRecorder; } });
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;EAME;;;AAEF,SAAS;AACT,mCAA0F;AAAjF,wGAAA,cAAc,OAAA;AAAE,6GAAA,mBAAmB,OAAA;AAAE,mHAAA,yBAAyB,OAAA;AAavE,UAAU;AACV,uCAAqD;AAA5C,mHAAA,uBAAuB,OAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ChangesetRecorder } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a `ChangesetRecorder` — the entry point for this package.
|
|
4
|
+
*
|
|
5
|
+
* ```typescript
|
|
6
|
+
* import { createChangesetRecorder } from '@bernierllc/agent-changeset';
|
|
7
|
+
*
|
|
8
|
+
* const recorder = createChangesetRecorder();
|
|
9
|
+
* const changeset = recorder.forConversation('conv-abc123');
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export declare function createChangesetRecorder(): ChangesetRecorder;
|
|
13
|
+
//# sourceMappingURL=recorder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAEV,iBAAiB,EAGlB,MAAM,SAAS,CAAC;AAiGjB;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,IAAI,iBAAiB,CAE3D"}
|
package/dist/recorder.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.createChangesetRecorder = createChangesetRecorder;
|
|
11
|
+
const errors_1 = require("./errors");
|
|
12
|
+
const changeset_1 = require("./changeset");
|
|
13
|
+
/**
|
|
14
|
+
* Validates that a raw value looks like a `ChangeRecord` and returns it typed.
|
|
15
|
+
* Throws `InvalidChangesetDataError` if required fields are missing or wrong type.
|
|
16
|
+
*/
|
|
17
|
+
function validateChangeRecord(item, index) {
|
|
18
|
+
if (typeof item !== 'object' ||
|
|
19
|
+
item === null ||
|
|
20
|
+
Array.isArray(item)) {
|
|
21
|
+
throw new errors_1.InvalidChangesetDataError(`change at index ${index} is not an object`, { index });
|
|
22
|
+
}
|
|
23
|
+
const record = item;
|
|
24
|
+
const requiredStrings = [
|
|
25
|
+
'id',
|
|
26
|
+
'action',
|
|
27
|
+
'resourceType',
|
|
28
|
+
'summary',
|
|
29
|
+
'timestamp',
|
|
30
|
+
'toolCallId',
|
|
31
|
+
];
|
|
32
|
+
for (const field of requiredStrings) {
|
|
33
|
+
if (typeof record[field] !== 'string') {
|
|
34
|
+
throw new errors_1.InvalidChangesetDataError(`change at index ${index} is missing required string field "${field}"`, { index, field });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (typeof record['success'] !== 'boolean') {
|
|
38
|
+
throw new errors_1.InvalidChangesetDataError(`change at index ${index} is missing required boolean field "success"`, { index });
|
|
39
|
+
}
|
|
40
|
+
// Optional fields: resourceId, before, after — presence is acceptable, type checked if present.
|
|
41
|
+
if (record['resourceId'] !== undefined &&
|
|
42
|
+
typeof record['resourceId'] !== 'string') {
|
|
43
|
+
throw new errors_1.InvalidChangesetDataError(`change at index ${index} has non-string "resourceId"`, { index });
|
|
44
|
+
}
|
|
45
|
+
for (const optField of ['before', 'after']) {
|
|
46
|
+
const val = record[optField];
|
|
47
|
+
if (val !== undefined &&
|
|
48
|
+
(typeof val !== 'object' || val === null || Array.isArray(val))) {
|
|
49
|
+
throw new errors_1.InvalidChangesetDataError(`change at index ${index} has invalid "${optField}" (must be a plain object or absent)`, { index, field: optField });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return item;
|
|
53
|
+
}
|
|
54
|
+
class ChangesetRecorderImpl {
|
|
55
|
+
forConversation(conversationId) {
|
|
56
|
+
return new changeset_1.ConversationChangesetImpl(conversationId);
|
|
57
|
+
}
|
|
58
|
+
fromJSON(data) {
|
|
59
|
+
if (typeof data !== 'object' ||
|
|
60
|
+
data === null ||
|
|
61
|
+
typeof data.conversationId !== 'string' ||
|
|
62
|
+
!Array.isArray(data.changes)) {
|
|
63
|
+
throw new errors_1.InvalidChangesetDataError('top-level shape must have string "conversationId" and array "changes"');
|
|
64
|
+
}
|
|
65
|
+
const changes = data.changes.map((item, index) => validateChangeRecord(item, index));
|
|
66
|
+
return new changeset_1.ConversationChangesetImpl(data.conversationId, changes);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Creates a `ChangesetRecorder` — the entry point for this package.
|
|
71
|
+
*
|
|
72
|
+
* ```typescript
|
|
73
|
+
* import { createChangesetRecorder } from '@bernierllc/agent-changeset';
|
|
74
|
+
*
|
|
75
|
+
* const recorder = createChangesetRecorder();
|
|
76
|
+
* const changeset = recorder.forConversation('conv-abc123');
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
function createChangesetRecorder() {
|
|
80
|
+
return new ChangesetRecorderImpl();
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=recorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recorder.js","sourceRoot":"","sources":["../src/recorder.ts"],"names":[],"mappings":";AAAA;;;;;;EAME;;AAoHF,0DAEC;AApHD,qCAAqD;AACrD,2CAAwD;AAQxD;;;GAGG;AACH,SAAS,oBAAoB,CAAC,IAAa,EAAE,KAAa;IACxD,IACE,OAAO,IAAI,KAAK,QAAQ;QACxB,IAAI,KAAK,IAAI;QACb,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EACnB,CAAC;QACD,MAAM,IAAI,kCAAyB,CACjC,mBAAmB,KAAK,mBAAmB,EAC3C,EAAE,KAAK,EAAE,CACV,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,IAA+B,CAAC;IAE/C,MAAM,eAAe,GAA8B;QACjD,IAAI;QACJ,QAAQ;QACR,cAAc;QACd,SAAS;QACT,WAAW;QACX,YAAY;KACb,CAAC;IAEF,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;QACpC,IAAI,OAAO,MAAM,CAAC,KAAK,CAAC,KAAK,QAAQ,EAAE,CAAC;YACtC,MAAM,IAAI,kCAAyB,CACjC,mBAAmB,KAAK,sCAAsC,KAAK,GAAG,EACtE,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,OAAO,MAAM,CAAC,SAAS,CAAC,KAAK,SAAS,EAAE,CAAC;QAC3C,MAAM,IAAI,kCAAyB,CACjC,mBAAmB,KAAK,8CAA8C,EACtE,EAAE,KAAK,EAAE,CACV,CAAC;IACJ,CAAC;IAED,gGAAgG;IAChG,IACE,MAAM,CAAC,YAAY,CAAC,KAAK,SAAS;QAClC,OAAO,MAAM,CAAC,YAAY,CAAC,KAAK,QAAQ,EACxC,CAAC;QACD,MAAM,IAAI,kCAAyB,CACjC,mBAAmB,KAAK,8BAA8B,EACtD,EAAE,KAAK,EAAE,CACV,CAAC;IACJ,CAAC;IAED,KAAK,MAAM,QAAQ,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAU,EAAE,CAAC;QACpD,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC7B,IACE,GAAG,KAAK,SAAS;YACjB,CAAC,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAC/D,CAAC;YACD,MAAM,IAAI,kCAAyB,CACjC,mBAAmB,KAAK,iBAAiB,QAAQ,sCAAsC,EACvF,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,CAC3B,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,IAAoB,CAAC;AAC9B,CAAC;AAED,MAAM,qBAAqB;IACzB,eAAe,CAAC,cAAsB;QACpC,OAAO,IAAI,qCAAyB,CAAC,cAAc,CAAC,CAAC;IACvD,CAAC;IAED,QAAQ,CAAC,IAAyB;QAChC,IACE,OAAO,IAAI,KAAK,QAAQ;YACxB,IAAI,KAAK,IAAI;YACb,OAAO,IAAI,CAAC,cAAc,KAAK,QAAQ;YACvC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAC5B,CAAC;YACD,MAAM,IAAI,kCAAyB,CACjC,uEAAuE,CACxE,CAAC;QACJ,CAAC;QAED,MAAM,OAAO,GAAmB,IAAI,CAAC,OAAO,CAAC,GAAG,CAC9C,CAAC,IAAa,EAAE,KAAa,EAAE,EAAE,CAAC,oBAAoB,CAAC,IAAI,EAAE,KAAK,CAAC,CACpE,CAAC;QAEF,OAAO,IAAI,qCAAyB,CAAC,IAAI,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;IACrE,CAAC;CACF;AAED;;;;;;;;;GASG;AACH,SAAgB,uBAAuB;IACrC,OAAO,IAAI,qBAAqB,EAAE,CAAC;AACrC,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A single mutation recorded during a conversation turn.
|
|
3
|
+
*/
|
|
4
|
+
export interface ChangeRecord {
|
|
5
|
+
/** UUID generated at record time. */
|
|
6
|
+
id: string;
|
|
7
|
+
/** Dot-notation action label, e.g. `admin.service.add`. */
|
|
8
|
+
action: string;
|
|
9
|
+
/** Domain noun the mutation affects, e.g. `service`, `booking`. */
|
|
10
|
+
resourceType: string;
|
|
11
|
+
/** Optional identifier of the specific resource. */
|
|
12
|
+
resourceId?: string;
|
|
13
|
+
/** State of the resource before the mutation. Omit for creates. */
|
|
14
|
+
before?: Record<string, unknown>;
|
|
15
|
+
/** State of the resource after the mutation. Omit for deletes. */
|
|
16
|
+
after?: Record<string, unknown>;
|
|
17
|
+
/** Human-readable one-sentence description of the change. */
|
|
18
|
+
summary: string;
|
|
19
|
+
/** ISO 8601 timestamp generated at record time. */
|
|
20
|
+
timestamp: string;
|
|
21
|
+
/** Correlates this record to the tool call that produced it. */
|
|
22
|
+
toolCallId: string;
|
|
23
|
+
/** Whether the tool call succeeded. */
|
|
24
|
+
success: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Field-level diff between `before` and `after` states of a `ChangeRecord`.
|
|
28
|
+
*/
|
|
29
|
+
export interface ChangesetDiff {
|
|
30
|
+
/** Keys present in `after` but absent in `before`. */
|
|
31
|
+
added: string[];
|
|
32
|
+
/** Keys present in `before` but absent in `after`. */
|
|
33
|
+
removed: string[];
|
|
34
|
+
/** Keys present in both states whose values differ. */
|
|
35
|
+
modified: Array<{
|
|
36
|
+
field: string;
|
|
37
|
+
from: unknown;
|
|
38
|
+
to: unknown;
|
|
39
|
+
}>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* End-of-conversation rollup: totals and per-resource-type groupings.
|
|
43
|
+
*/
|
|
44
|
+
export interface RollupSummary {
|
|
45
|
+
totalChanges: number;
|
|
46
|
+
successfulChanges: number;
|
|
47
|
+
failedChanges: number;
|
|
48
|
+
/** Grouped by `resourceType` — each entry has a count and per-change lines. */
|
|
49
|
+
byResourceType: Record<string, {
|
|
50
|
+
count: number;
|
|
51
|
+
lines: string[];
|
|
52
|
+
}>;
|
|
53
|
+
/** Plain text suitable for injection into an LLM system prompt. */
|
|
54
|
+
plainText: string;
|
|
55
|
+
/** Markdown suitable for rendering in a `<ChangeSummaryCard>` UI component. */
|
|
56
|
+
markdown: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Serializable snapshot for persistence alongside conversation messages.
|
|
60
|
+
*/
|
|
61
|
+
export interface SerializedChangeset {
|
|
62
|
+
conversationId: string;
|
|
63
|
+
changes: ChangeRecord[];
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Input accepted by `ConversationChangeset.record()`.
|
|
67
|
+
* Identical to `ChangeRecord` minus the auto-generated `id` and `timestamp`.
|
|
68
|
+
*/
|
|
69
|
+
export type RecordInput = Omit<ChangeRecord, 'id' | 'timestamp'>;
|
|
70
|
+
/**
|
|
71
|
+
* Accumulates mutation records for a single conversation and renders
|
|
72
|
+
* per-change summaries and end-of-conversation rollups.
|
|
73
|
+
*/
|
|
74
|
+
export interface ConversationChangeset {
|
|
75
|
+
readonly conversationId: string;
|
|
76
|
+
readonly changes: ReadonlyArray<ChangeRecord>;
|
|
77
|
+
/** Record a single mutation; auto-generates `id` and `timestamp`. */
|
|
78
|
+
record(entry: RecordInput): ChangeRecord;
|
|
79
|
+
/**
|
|
80
|
+
* Returns a compact one-liner for the given changeId.
|
|
81
|
+
* Prefix is `+` for success, `-` for failure.
|
|
82
|
+
* @throws {ChangeNotFoundError} if `changeId` is unknown.
|
|
83
|
+
*/
|
|
84
|
+
renderLineSummary(changeId: string): string;
|
|
85
|
+
/**
|
|
86
|
+
* Computes a field-level diff for a change that has both `before` and `after`.
|
|
87
|
+
* Returns `null` when either is absent (create-only or delete-only mutations).
|
|
88
|
+
* @throws {ChangeNotFoundError} if `changeId` is unknown.
|
|
89
|
+
*/
|
|
90
|
+
computeDiff(changeId: string): ChangesetDiff | null;
|
|
91
|
+
/**
|
|
92
|
+
* Produces an end-of-conversation rollup grouped by `resourceType`.
|
|
93
|
+
* Includes both `plainText` (for LLM context injection) and `markdown` (for UI).
|
|
94
|
+
*/
|
|
95
|
+
renderRollupSummary(): RollupSummary;
|
|
96
|
+
/** Serializes the changeset to plain JSON for persistence. */
|
|
97
|
+
toJSON(): SerializedChangeset;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Factory that creates and restores `ConversationChangeset` instances.
|
|
101
|
+
*/
|
|
102
|
+
export interface ChangesetRecorder {
|
|
103
|
+
/** Create a fresh changeset scoped to the given conversation. */
|
|
104
|
+
forConversation(conversationId: string): ConversationChangeset;
|
|
105
|
+
/**
|
|
106
|
+
* Restore a changeset from serialized JSON (e.g. loaded from storage).
|
|
107
|
+
* @throws {InvalidChangesetDataError} if the data is malformed.
|
|
108
|
+
*/
|
|
109
|
+
fromJSON(data: SerializedChangeset): ConversationChangeset;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,qCAAqC;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAC;IACf,mEAAmE;IACnE,YAAY,EAAE,MAAM,CAAC;IACrB,oDAAoD;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,sDAAsD;IACtD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,sDAAsD;IACtD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,uDAAuD;IACvD,QAAQ,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC;QAAC,EAAE,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAChE;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACnE,mEAAmE;IACnE,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,EAAE,IAAI,GAAG,WAAW,CAAC,CAAC;AAEjE;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IAE9C,qEAAqE;IACrE,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,CAAC;IAEzC;;;;OAIG;IACH,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAE5C;;;;OAIG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAAC;IAEpD;;;OAGG;IACH,mBAAmB,IAAI,aAAa,CAAC;IAErC,8DAA8D;IAC9D,MAAM,IAAI,mBAAmB,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,iEAAiE;IACjE,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,qBAAqB,CAAC;IAC/D;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,mBAAmB,GAAG,qBAAqB,CAAC;CAC5D"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;;;;EAME"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,56 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bernierllc/agent-changeset",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Records agent-tool mutations per conversation and renders per-change summaries and end-of-conversation rollups",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist/**/*",
|
|
9
|
+
"README.md",
|
|
10
|
+
"LICENSE"
|
|
11
|
+
],
|
|
5
12
|
"keywords": [
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
|
|
13
|
+
"ai",
|
|
14
|
+
"agent",
|
|
15
|
+
"changeset",
|
|
16
|
+
"audit",
|
|
17
|
+
"diff",
|
|
18
|
+
"bernierllc"
|
|
19
|
+
],
|
|
20
|
+
"author": "Bernier LLC",
|
|
21
|
+
"license": "Bernier LLC",
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "https://github.com/bernierllc/tools.git",
|
|
25
|
+
"directory": "packages/core/agent-changeset"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@bernierllc/logger": "1.7.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/jest": "^29.5.12",
|
|
32
|
+
"@types/node": "^20.11.19",
|
|
33
|
+
"eslint": "^8.0.0",
|
|
34
|
+
"jest": "^29.7.0",
|
|
35
|
+
"rimraf": "^5.0.0",
|
|
36
|
+
"ts-jest": "^29.1.2",
|
|
37
|
+
"typescript": "^5.3.3"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18.0.0"
|
|
41
|
+
},
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public",
|
|
44
|
+
"registry": "https://registry.npmjs.org/"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsc",
|
|
48
|
+
"test": "jest",
|
|
49
|
+
"test:watch": "jest --watch",
|
|
50
|
+
"test:coverage": "jest --coverage",
|
|
51
|
+
"lint": "eslint src/**/*.ts",
|
|
52
|
+
"clean": "rm -rf dist",
|
|
53
|
+
"prebuild": "npm run clean",
|
|
54
|
+
"test:run": "jest"
|
|
55
|
+
}
|
|
56
|
+
}
|