iranti 0.2.51 → 0.3.2
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/README.md +30 -17
- package/dist/scripts/api-key-create.js +1 -1
- package/dist/scripts/api-key-list.js +1 -1
- package/dist/scripts/api-key-revoke.js +1 -1
- package/dist/scripts/claude-code-memory-hook.js +116 -30
- package/dist/scripts/codex-setup.js +86 -4
- package/dist/scripts/iranti-cli.js +1359 -57
- package/dist/scripts/iranti-mcp.js +578 -75
- package/dist/scripts/seed.js +11 -6
- package/dist/scripts/setup.js +1 -1
- package/dist/src/api/healthChecks.d.ts +29 -0
- package/dist/src/api/healthChecks.d.ts.map +1 -0
- package/dist/src/api/healthChecks.js +72 -0
- package/dist/src/api/healthChecks.js.map +1 -0
- package/dist/src/api/middleware/validation.d.ts +22 -0
- package/dist/src/api/middleware/validation.d.ts.map +1 -1
- package/dist/src/api/middleware/validation.js +93 -3
- package/dist/src/api/middleware/validation.js.map +1 -1
- package/dist/src/api/routes/knowledge.d.ts.map +1 -1
- package/dist/src/api/routes/knowledge.js +53 -0
- package/dist/src/api/routes/knowledge.js.map +1 -1
- package/dist/src/api/routes/memory.d.ts.map +1 -1
- package/dist/src/api/routes/memory.js +73 -9
- package/dist/src/api/routes/memory.js.map +1 -1
- package/dist/src/api/server.js +38 -43
- package/dist/src/api/server.js.map +1 -1
- package/dist/src/attendant/AttendantInstance.d.ts +135 -2
- package/dist/src/attendant/AttendantInstance.d.ts.map +1 -1
- package/dist/src/attendant/AttendantInstance.js +1836 -93
- package/dist/src/attendant/AttendantInstance.js.map +1 -1
- package/dist/src/attendant/index.d.ts +1 -1
- package/dist/src/attendant/index.d.ts.map +1 -1
- package/dist/src/attendant/index.js +1 -1
- package/dist/src/attendant/index.js.map +1 -1
- package/dist/src/attendant/registry.d.ts.map +1 -1
- package/dist/src/attendant/registry.js +2 -0
- package/dist/src/attendant/registry.js.map +1 -1
- package/dist/src/chat/index.d.ts +23 -0
- package/dist/src/chat/index.d.ts.map +1 -1
- package/dist/src/chat/index.js +111 -22
- package/dist/src/chat/index.js.map +1 -1
- package/dist/src/generated/prisma/browser.d.ts +5 -0
- package/dist/src/generated/prisma/browser.d.ts.map +1 -1
- package/dist/src/generated/prisma/client.d.ts +5 -0
- package/dist/src/generated/prisma/client.d.ts.map +1 -1
- package/dist/src/generated/prisma/commonInputTypes.d.ts +48 -0
- package/dist/src/generated/prisma/commonInputTypes.d.ts.map +1 -1
- package/dist/src/generated/prisma/internal/class.d.ts +11 -0
- package/dist/src/generated/prisma/internal/class.d.ts.map +1 -1
- package/dist/src/generated/prisma/internal/class.js +4 -4
- package/dist/src/generated/prisma/internal/class.js.map +1 -1
- package/dist/src/generated/prisma/internal/prismaNamespace.d.ts +92 -1
- package/dist/src/generated/prisma/internal/prismaNamespace.d.ts.map +1 -1
- package/dist/src/generated/prisma/internal/prismaNamespace.js +17 -2
- package/dist/src/generated/prisma/internal/prismaNamespace.js.map +1 -1
- package/dist/src/generated/prisma/internal/prismaNamespaceBrowser.d.ts +16 -0
- package/dist/src/generated/prisma/internal/prismaNamespaceBrowser.d.ts.map +1 -1
- package/dist/src/generated/prisma/internal/prismaNamespaceBrowser.js +17 -2
- package/dist/src/generated/prisma/internal/prismaNamespaceBrowser.js.map +1 -1
- package/dist/src/generated/prisma/models/StaffEvent.d.ts +1184 -0
- package/dist/src/generated/prisma/models/StaffEvent.d.ts.map +1 -0
- package/dist/src/generated/prisma/models/StaffEvent.js +3 -0
- package/dist/src/generated/prisma/models/StaffEvent.js.map +1 -0
- package/dist/src/generated/prisma/models.d.ts +1 -0
- package/dist/src/generated/prisma/models.d.ts.map +1 -1
- package/dist/src/lib/assistantCheckpoint.d.ts +21 -0
- package/dist/src/lib/assistantCheckpoint.d.ts.map +1 -0
- package/dist/src/lib/assistantCheckpoint.js +143 -0
- package/dist/src/lib/assistantCheckpoint.js.map +1 -0
- package/dist/src/lib/autoRemember.d.ts +15 -0
- package/dist/src/lib/autoRemember.d.ts.map +1 -1
- package/dist/src/lib/autoRemember.js +433 -71
- package/dist/src/lib/autoRemember.js.map +1 -1
- package/dist/src/lib/cliHelpCatalog.d.ts.map +1 -1
- package/dist/src/lib/cliHelpCatalog.js +23 -11
- package/dist/src/lib/cliHelpCatalog.js.map +1 -1
- package/dist/src/lib/cliHelpRenderer.d.ts +1 -0
- package/dist/src/lib/cliHelpRenderer.d.ts.map +1 -1
- package/dist/src/lib/cliHelpRenderer.js +4 -0
- package/dist/src/lib/cliHelpRenderer.js.map +1 -1
- package/dist/src/lib/commandErrors.d.ts +5 -1
- package/dist/src/lib/commandErrors.d.ts.map +1 -1
- package/dist/src/lib/commandErrors.js +250 -17
- package/dist/src/lib/commandErrors.js.map +1 -1
- package/dist/src/lib/createFirstPartyIranti.d.ts.map +1 -1
- package/dist/src/lib/createFirstPartyIranti.js +1 -0
- package/dist/src/lib/createFirstPartyIranti.js.map +1 -1
- package/dist/src/lib/dbStaffEventEmitter.d.ts +2 -0
- package/dist/src/lib/dbStaffEventEmitter.d.ts.map +1 -1
- package/dist/src/lib/dbStaffEventEmitter.js +15 -0
- package/dist/src/lib/dbStaffEventEmitter.js.map +1 -1
- package/dist/src/lib/hostMemoryFormatting.d.ts +25 -0
- package/dist/src/lib/hostMemoryFormatting.d.ts.map +1 -0
- package/dist/src/lib/hostMemoryFormatting.js +55 -0
- package/dist/src/lib/hostMemoryFormatting.js.map +1 -0
- package/dist/src/lib/issueFacts.d.ts +37 -0
- package/dist/src/lib/issueFacts.d.ts.map +1 -0
- package/dist/src/lib/issueFacts.js +72 -0
- package/dist/src/lib/issueFacts.js.map +1 -0
- package/dist/src/lib/llm.d.ts +8 -0
- package/dist/src/lib/llm.d.ts.map +1 -1
- package/dist/src/lib/llm.js +33 -0
- package/dist/src/lib/llm.js.map +1 -1
- package/dist/src/lib/packageRoot.d.ts +2 -0
- package/dist/src/lib/packageRoot.d.ts.map +1 -0
- package/dist/src/lib/packageRoot.js +22 -0
- package/dist/src/lib/packageRoot.js.map +1 -0
- package/dist/src/lib/projectLearning.d.ts +21 -0
- package/dist/src/lib/projectLearning.d.ts.map +1 -0
- package/dist/src/lib/projectLearning.js +357 -0
- package/dist/src/lib/projectLearning.js.map +1 -0
- package/dist/src/lib/protocolEnforcement.d.ts +29 -0
- package/dist/src/lib/protocolEnforcement.d.ts.map +1 -0
- package/dist/src/lib/protocolEnforcement.js +124 -0
- package/dist/src/lib/protocolEnforcement.js.map +1 -0
- package/dist/src/lib/providers/claude.js +1 -1
- package/dist/src/lib/providers/claude.js.map +1 -1
- package/dist/src/lib/router.js +1 -1
- package/dist/src/lib/router.js.map +1 -1
- package/dist/src/lib/runtimeEnv.d.ts.map +1 -1
- package/dist/src/lib/runtimeEnv.js +8 -3
- package/dist/src/lib/runtimeEnv.js.map +1 -1
- package/dist/src/lib/scaffoldCloseout.d.ts +27 -0
- package/dist/src/lib/scaffoldCloseout.d.ts.map +1 -0
- package/dist/src/lib/scaffoldCloseout.js +139 -0
- package/dist/src/lib/scaffoldCloseout.js.map +1 -0
- package/dist/src/lib/semanticFactTags.d.ts +10 -0
- package/dist/src/lib/semanticFactTags.d.ts.map +1 -0
- package/dist/src/lib/semanticFactTags.js +166 -0
- package/dist/src/lib/semanticFactTags.js.map +1 -0
- package/dist/src/lib/sessionLedger.d.ts +94 -0
- package/dist/src/lib/sessionLedger.d.ts.map +1 -0
- package/dist/src/lib/sessionLedger.js +997 -0
- package/dist/src/lib/sessionLedger.js.map +1 -0
- package/dist/src/lib/sharedStateInvalidation.d.ts +10 -0
- package/dist/src/lib/sharedStateInvalidation.d.ts.map +1 -0
- package/dist/src/lib/sharedStateInvalidation.js +184 -0
- package/dist/src/lib/sharedStateInvalidation.js.map +1 -0
- package/dist/src/lib/staffEventsTable.d.ts +3 -0
- package/dist/src/lib/staffEventsTable.d.ts.map +1 -0
- package/dist/src/lib/staffEventsTable.js +58 -0
- package/dist/src/lib/staffEventsTable.js.map +1 -0
- package/dist/src/librarian/index.d.ts.map +1 -1
- package/dist/src/librarian/index.js +113 -2
- package/dist/src/librarian/index.js.map +1 -1
- package/dist/src/library/client.d.ts +6 -1
- package/dist/src/library/client.d.ts.map +1 -1
- package/dist/src/library/client.js +21 -7
- package/dist/src/library/client.js.map +1 -1
- package/dist/src/library/embeddings.d.ts +9 -1
- package/dist/src/library/embeddings.d.ts.map +1 -1
- package/dist/src/library/embeddings.js +28 -3
- package/dist/src/library/embeddings.js.map +1 -1
- package/dist/src/library/queries.d.ts.map +1 -1
- package/dist/src/library/queries.js +263 -46
- package/dist/src/library/queries.js.map +1 -1
- package/dist/src/sdk/index.d.ts +52 -1
- package/dist/src/sdk/index.d.ts.map +1 -1
- package/dist/src/sdk/index.js +546 -98
- package/dist/src/sdk/index.js.map +1 -1
- package/package.json +24 -3
- package/prisma/migrations/20260331101500_add_staff_events_ledger/migration.sql +24 -0
- package/prisma/schema.prisma +22 -0
|
@@ -0,0 +1,997 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SessionLedgerUnavailableError = void 0;
|
|
4
|
+
exports.querySessionLedger = querySessionLedger;
|
|
5
|
+
exports.summarizeSessionLedgerLearnings = summarizeSessionLedgerLearnings;
|
|
6
|
+
exports.summarizeMemoryAttribution = summarizeMemoryAttribution;
|
|
7
|
+
exports.buildSessionLedgerLearningProfile = buildSessionLedgerLearningProfile;
|
|
8
|
+
const client_1 = require("../generated/prisma/client");
|
|
9
|
+
const client_2 = require("../library/client");
|
|
10
|
+
const staffEventsTable_1 = require("./staffEventsTable");
|
|
11
|
+
class SessionLedgerUnavailableError extends Error {
|
|
12
|
+
constructor(message = 'staff_events table is missing. Create it before querying the session ledger.') {
|
|
13
|
+
super(message);
|
|
14
|
+
this.code = 'SESSION_LEDGER_UNAVAILABLE';
|
|
15
|
+
this.name = 'SessionLedgerUnavailableError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.SessionLedgerUnavailableError = SessionLedgerUnavailableError;
|
|
19
|
+
function isMissingStaffEventsTable(error) {
|
|
20
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
21
|
+
return /relation ["']?staff_events["']? does not exist/i.test(message);
|
|
22
|
+
}
|
|
23
|
+
function normalizeLimit(limit) {
|
|
24
|
+
if (!Number.isFinite(limit))
|
|
25
|
+
return 100;
|
|
26
|
+
return Math.max(1, Math.min(500, Math.floor(limit)));
|
|
27
|
+
}
|
|
28
|
+
function normalizeLearningLimit(limit) {
|
|
29
|
+
if (!Number.isFinite(limit))
|
|
30
|
+
return 4;
|
|
31
|
+
return Math.max(1, Math.min(10, Math.floor(limit)));
|
|
32
|
+
}
|
|
33
|
+
function normalizeProfileLimit(limit) {
|
|
34
|
+
if (!Number.isFinite(limit))
|
|
35
|
+
return 60;
|
|
36
|
+
return Math.max(10, Math.min(200, Math.floor(limit)));
|
|
37
|
+
}
|
|
38
|
+
function rowToEvent(row) {
|
|
39
|
+
return {
|
|
40
|
+
eventId: row.event_id,
|
|
41
|
+
timestamp: row.timestamp instanceof Date ? row.timestamp.toISOString() : String(row.timestamp),
|
|
42
|
+
staffComponent: row.staff_component,
|
|
43
|
+
actionType: row.action_type,
|
|
44
|
+
agentId: row.agent_id,
|
|
45
|
+
source: row.source,
|
|
46
|
+
entityType: row.entity_type ?? null,
|
|
47
|
+
entityId: row.entity_id ?? null,
|
|
48
|
+
key: row.key ?? null,
|
|
49
|
+
reason: row.reason ?? null,
|
|
50
|
+
level: row.level,
|
|
51
|
+
metadata: row.metadata ?? null,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
async function querySessionLedger(input = {}) {
|
|
55
|
+
const clauses = [];
|
|
56
|
+
if (input.agentId?.trim()) {
|
|
57
|
+
clauses.push(client_1.Prisma.sql `agent_id = ${input.agentId.trim()}`);
|
|
58
|
+
}
|
|
59
|
+
if (input.sessionId?.trim()) {
|
|
60
|
+
clauses.push(client_1.Prisma.sql `metadata->>'sessionId' = ${input.sessionId.trim()}`);
|
|
61
|
+
}
|
|
62
|
+
if (input.actionType?.trim()) {
|
|
63
|
+
clauses.push(client_1.Prisma.sql `action_type = ${input.actionType.trim()}`);
|
|
64
|
+
}
|
|
65
|
+
if (Array.isArray(input.actionTypes)) {
|
|
66
|
+
const actionTypes = input.actionTypes
|
|
67
|
+
.map((value) => value.trim())
|
|
68
|
+
.filter(Boolean);
|
|
69
|
+
if (actionTypes.length > 0) {
|
|
70
|
+
clauses.push(client_1.Prisma.sql `action_type IN (${client_1.Prisma.join(actionTypes)})`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (input.source?.trim()) {
|
|
74
|
+
clauses.push(client_1.Prisma.sql `source = ${input.source.trim()}`);
|
|
75
|
+
}
|
|
76
|
+
if (input.host?.trim()) {
|
|
77
|
+
clauses.push(client_1.Prisma.sql `metadata->>'host' = ${input.host.trim()}`);
|
|
78
|
+
}
|
|
79
|
+
if (input.level) {
|
|
80
|
+
clauses.push(client_1.Prisma.sql `level = ${input.level}`);
|
|
81
|
+
}
|
|
82
|
+
if (input.since) {
|
|
83
|
+
clauses.push(client_1.Prisma.sql `timestamp > ${input.since.toISOString()}::timestamptz`);
|
|
84
|
+
}
|
|
85
|
+
if (input.until) {
|
|
86
|
+
clauses.push(client_1.Prisma.sql `timestamp <= ${input.until.toISOString()}::timestamptz`);
|
|
87
|
+
}
|
|
88
|
+
const whereClause = clauses.length > 0
|
|
89
|
+
? client_1.Prisma.sql `WHERE ${client_1.Prisma.join(clauses, ' AND ')}`
|
|
90
|
+
: client_1.Prisma.empty;
|
|
91
|
+
try {
|
|
92
|
+
const rows = await runLedgerQuery(whereClause, normalizeLimit(input.limit));
|
|
93
|
+
return rows.map(rowToEvent);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
if (isMissingStaffEventsTable(error)) {
|
|
97
|
+
try {
|
|
98
|
+
await (0, staffEventsTable_1.ensureStaffEventsTable)();
|
|
99
|
+
const rows = await runLedgerQuery(whereClause, normalizeLimit(input.limit));
|
|
100
|
+
return rows.map(rowToEvent);
|
|
101
|
+
}
|
|
102
|
+
catch (ensureError) {
|
|
103
|
+
if (isMissingStaffEventsTable(ensureError)) {
|
|
104
|
+
throw new SessionLedgerUnavailableError();
|
|
105
|
+
}
|
|
106
|
+
throw ensureError;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async function runLedgerQuery(whereClause, limit) {
|
|
113
|
+
return (0, client_2.getDb)().$queryRaw(client_1.Prisma.sql `
|
|
114
|
+
SELECT
|
|
115
|
+
event_id,
|
|
116
|
+
timestamp,
|
|
117
|
+
staff_component,
|
|
118
|
+
action_type,
|
|
119
|
+
agent_id,
|
|
120
|
+
source,
|
|
121
|
+
entity_type,
|
|
122
|
+
entity_id,
|
|
123
|
+
key,
|
|
124
|
+
reason,
|
|
125
|
+
level,
|
|
126
|
+
metadata
|
|
127
|
+
FROM staff_events
|
|
128
|
+
${whereClause}
|
|
129
|
+
ORDER BY timestamp DESC
|
|
130
|
+
LIMIT ${limit}
|
|
131
|
+
`);
|
|
132
|
+
}
|
|
133
|
+
const LEDGER_LEARNING_ACTIONS = new Set([
|
|
134
|
+
'host_failure',
|
|
135
|
+
'provider_fallback_used',
|
|
136
|
+
'integration_probe_failed',
|
|
137
|
+
'mandatory_recall_forced',
|
|
138
|
+
'memory_injected',
|
|
139
|
+
'memory_evidence_observed',
|
|
140
|
+
'memory_injection_scored',
|
|
141
|
+
'query_executed',
|
|
142
|
+
'search_executed',
|
|
143
|
+
'related_executed',
|
|
144
|
+
'related_deep_executed',
|
|
145
|
+
'whoknows_executed',
|
|
146
|
+
'checkpoint_written',
|
|
147
|
+
'checkpoint_availability_failed',
|
|
148
|
+
'summary_written',
|
|
149
|
+
'write_available',
|
|
150
|
+
'write_availability_failed',
|
|
151
|
+
'write_rejected',
|
|
152
|
+
'write_escalated',
|
|
153
|
+
'write_updated',
|
|
154
|
+
'write_replaced',
|
|
155
|
+
'write_created',
|
|
156
|
+
'checkpoint_shared_breadcrumb_failed',
|
|
157
|
+
]);
|
|
158
|
+
const LEDGER_SUCCESS_ACTIONS = new Set([
|
|
159
|
+
'memory_injected',
|
|
160
|
+
'memory_injection_scored',
|
|
161
|
+
'query_executed',
|
|
162
|
+
'search_executed',
|
|
163
|
+
'related_executed',
|
|
164
|
+
'related_deep_executed',
|
|
165
|
+
'whoknows_executed',
|
|
166
|
+
'checkpoint_written',
|
|
167
|
+
'write_available',
|
|
168
|
+
'summary_written',
|
|
169
|
+
'write_created',
|
|
170
|
+
'write_updated',
|
|
171
|
+
'write_replaced',
|
|
172
|
+
]);
|
|
173
|
+
const LEDGER_ACTIVITY_ACTIONS = new Set([
|
|
174
|
+
'query_executed',
|
|
175
|
+
'search_executed',
|
|
176
|
+
'related_executed',
|
|
177
|
+
'related_deep_executed',
|
|
178
|
+
'whoknows_executed',
|
|
179
|
+
'observe_completed',
|
|
180
|
+
'attend_completed',
|
|
181
|
+
'memory_injected',
|
|
182
|
+
'memory_not_injected',
|
|
183
|
+
'memory_evidence_observed',
|
|
184
|
+
'memory_injection_scored',
|
|
185
|
+
'mandatory_recall_forced',
|
|
186
|
+
]);
|
|
187
|
+
const LEDGER_PERSISTENCE_ACTIONS = new Set([
|
|
188
|
+
'checkpoint_written',
|
|
189
|
+
'write_available',
|
|
190
|
+
'summary_written',
|
|
191
|
+
'write_created',
|
|
192
|
+
'write_updated',
|
|
193
|
+
'write_replaced',
|
|
194
|
+
]);
|
|
195
|
+
const LEDGER_FAILURE_ACTIONS = new Set([
|
|
196
|
+
'host_failure',
|
|
197
|
+
'integration_probe_failed',
|
|
198
|
+
'provider_fallback_used',
|
|
199
|
+
'checkpoint_availability_failed',
|
|
200
|
+
'write_availability_failed',
|
|
201
|
+
'write_rejected',
|
|
202
|
+
'write_escalated',
|
|
203
|
+
'checkpoint_shared_breadcrumb_failed',
|
|
204
|
+
]);
|
|
205
|
+
const LEDGER_DISCOVERY_ACTIONS = new Set([
|
|
206
|
+
'query_executed',
|
|
207
|
+
'search_executed',
|
|
208
|
+
'related_executed',
|
|
209
|
+
'related_deep_executed',
|
|
210
|
+
'whoknows_executed',
|
|
211
|
+
'observe_completed',
|
|
212
|
+
'memory_injected',
|
|
213
|
+
'memory_evidence_observed',
|
|
214
|
+
'memory_injection_scored',
|
|
215
|
+
'mandatory_recall_forced',
|
|
216
|
+
]);
|
|
217
|
+
const LEDGER_REQUIRED_WRITE_CATEGORY_LABELS = {
|
|
218
|
+
findings: 'what you found',
|
|
219
|
+
validated_results: 'what worked',
|
|
220
|
+
failed_paths: 'what failed',
|
|
221
|
+
file_changes: 'what changed',
|
|
222
|
+
risks_and_next_steps: 'what remains risky and what happens next',
|
|
223
|
+
};
|
|
224
|
+
const TASK_TOKEN_STOPWORDS = new Set([
|
|
225
|
+
'the',
|
|
226
|
+
'and',
|
|
227
|
+
'for',
|
|
228
|
+
'with',
|
|
229
|
+
'from',
|
|
230
|
+
'into',
|
|
231
|
+
'that',
|
|
232
|
+
'this',
|
|
233
|
+
'what',
|
|
234
|
+
'when',
|
|
235
|
+
'where',
|
|
236
|
+
'which',
|
|
237
|
+
'while',
|
|
238
|
+
'about',
|
|
239
|
+
'need',
|
|
240
|
+
'needs',
|
|
241
|
+
'work',
|
|
242
|
+
'working',
|
|
243
|
+
'task',
|
|
244
|
+
'tasks',
|
|
245
|
+
'project',
|
|
246
|
+
'agent',
|
|
247
|
+
'session',
|
|
248
|
+
'continue',
|
|
249
|
+
'continuing',
|
|
250
|
+
]);
|
|
251
|
+
// Maps common engineering task vocabulary to canonical forms so that synonymous
|
|
252
|
+
// task descriptions score well even when they share no raw tokens.
|
|
253
|
+
// Keys must be 4+ characters (matching the tokenizeTaskText length filter).
|
|
254
|
+
const TASK_TOKEN_SYNONYMS = {
|
|
255
|
+
// Bug resolution
|
|
256
|
+
'fixing': 'repair',
|
|
257
|
+
'fixed': 'repair',
|
|
258
|
+
'resolve': 'repair',
|
|
259
|
+
'resolving': 'repair',
|
|
260
|
+
'resolved': 'repair',
|
|
261
|
+
'debug': 'repair',
|
|
262
|
+
'debugging': 'repair',
|
|
263
|
+
'debugged': 'repair',
|
|
264
|
+
'patch': 'repair',
|
|
265
|
+
'patching': 'repair',
|
|
266
|
+
'repair': 'repair',
|
|
267
|
+
'repairing': 'repair',
|
|
268
|
+
'troubleshoot': 'repair',
|
|
269
|
+
'troubleshooting': 'repair',
|
|
270
|
+
// Container / Docker
|
|
271
|
+
'docker': 'container',
|
|
272
|
+
'container': 'container',
|
|
273
|
+
'containers': 'container',
|
|
274
|
+
// Startup / init / launch
|
|
275
|
+
'startup': 'startup',
|
|
276
|
+
'boot': 'startup',
|
|
277
|
+
'init': 'startup',
|
|
278
|
+
'initialize': 'startup',
|
|
279
|
+
'initialise': 'startup',
|
|
280
|
+
'initializing': 'startup',
|
|
281
|
+
'launch': 'startup',
|
|
282
|
+
'launching': 'startup',
|
|
283
|
+
// Authentication
|
|
284
|
+
'auth': 'auth',
|
|
285
|
+
'authentication': 'auth',
|
|
286
|
+
'authenticate': 'auth',
|
|
287
|
+
'login': 'auth',
|
|
288
|
+
'signin': 'auth',
|
|
289
|
+
'authorization': 'auth',
|
|
290
|
+
'authorize': 'auth',
|
|
291
|
+
// Database
|
|
292
|
+
'database': 'database',
|
|
293
|
+
'postgres': 'database',
|
|
294
|
+
'postgresql': 'database',
|
|
295
|
+
'prisma': 'database',
|
|
296
|
+
// Errors / failures
|
|
297
|
+
'error': 'error',
|
|
298
|
+
'errors': 'error',
|
|
299
|
+
'bugs': 'error',
|
|
300
|
+
'failure': 'error',
|
|
301
|
+
'failures': 'error',
|
|
302
|
+
'problem': 'error',
|
|
303
|
+
'problems': 'error',
|
|
304
|
+
'issue': 'error',
|
|
305
|
+
'issues': 'error',
|
|
306
|
+
'fault': 'error',
|
|
307
|
+
// Memory / injection (Iranti-domain)
|
|
308
|
+
'memory': 'memory',
|
|
309
|
+
'injection': 'memory',
|
|
310
|
+
'inject': 'memory',
|
|
311
|
+
'injecting': 'memory',
|
|
312
|
+
// Performance
|
|
313
|
+
'performance': 'performance',
|
|
314
|
+
'optimize': 'performance',
|
|
315
|
+
'optimise': 'performance',
|
|
316
|
+
'optimizing': 'performance',
|
|
317
|
+
'latency': 'performance',
|
|
318
|
+
'slow': 'performance',
|
|
319
|
+
'speed': 'performance',
|
|
320
|
+
};
|
|
321
|
+
function describeEvent(event) {
|
|
322
|
+
const host = typeof event.metadata?.host === 'string' ? event.metadata.host : null;
|
|
323
|
+
const entityKey = event.entityType && event.entityId && event.key
|
|
324
|
+
? `${event.entityType}/${event.entityId}/${event.key}`
|
|
325
|
+
: event.entityType && event.entityId
|
|
326
|
+
? `${event.entityType}/${event.entityId}`
|
|
327
|
+
: null;
|
|
328
|
+
const error = typeof event.metadata?.error === 'string' ? event.metadata.error : null;
|
|
329
|
+
const injectedKeys = Array.isArray(event.metadata?.injectedKeys)
|
|
330
|
+
? event.metadata.injectedKeys.map((value) => String(value)).filter(Boolean)
|
|
331
|
+
: [];
|
|
332
|
+
switch (event.actionType) {
|
|
333
|
+
case 'host_failure':
|
|
334
|
+
case 'integration_probe_failed':
|
|
335
|
+
case 'provider_fallback_used':
|
|
336
|
+
return `${host ?? event.source} failure: ${event.reason ?? error ?? 'host error'}`;
|
|
337
|
+
case 'mandatory_recall_forced':
|
|
338
|
+
return event.key
|
|
339
|
+
? `recall policy forced a lookup for ${event.key}`
|
|
340
|
+
: 'recall policy forced a memory lookup';
|
|
341
|
+
case 'memory_injected':
|
|
342
|
+
return injectedKeys.length > 0
|
|
343
|
+
? `memory injected from ${host ?? event.source}: ${injectedKeys.slice(0, 3).join(', ')}`
|
|
344
|
+
: `memory injected from ${host ?? event.source}`;
|
|
345
|
+
case 'memory_evidence_observed': {
|
|
346
|
+
const evidenceKind = typeof event.metadata?.evidenceKind === 'string' ? event.metadata.evidenceKind : 'memory_evidence';
|
|
347
|
+
return `memory evidence observed from ${host ?? event.source}: ${evidenceKind}`;
|
|
348
|
+
}
|
|
349
|
+
case 'memory_injection_scored': {
|
|
350
|
+
const used = event.metadata?.used === true;
|
|
351
|
+
const helpful = event.metadata?.helpful === true;
|
|
352
|
+
return `memory injection scored from ${host ?? event.source}: used=${used} helpful=${helpful}`;
|
|
353
|
+
}
|
|
354
|
+
case 'checkpoint_written':
|
|
355
|
+
return entityKey
|
|
356
|
+
? `shared checkpoint written for ${entityKey}`
|
|
357
|
+
: 'shared checkpoint written';
|
|
358
|
+
case 'checkpoint_availability_failed':
|
|
359
|
+
return entityKey
|
|
360
|
+
? `checkpoint availability failed for ${entityKey}`
|
|
361
|
+
: `checkpoint availability failed${error ? `: ${error}` : ''}`;
|
|
362
|
+
case 'summary_written':
|
|
363
|
+
return entityKey
|
|
364
|
+
? `strict summary written for ${entityKey}`
|
|
365
|
+
: 'strict summary written';
|
|
366
|
+
case 'checkpoint_shared_breadcrumb_failed':
|
|
367
|
+
return entityKey
|
|
368
|
+
? `shared checkpoint breadcrumb failed for ${entityKey}`
|
|
369
|
+
: `shared checkpoint breadcrumb failed${error ? `: ${error}` : ''}`;
|
|
370
|
+
case 'write_created':
|
|
371
|
+
return entityKey ? `created ${entityKey}` : 'created durable fact';
|
|
372
|
+
case 'write_available':
|
|
373
|
+
return entityKey ? `write availability verified for ${entityKey}` : 'write availability verified';
|
|
374
|
+
case 'write_availability_failed':
|
|
375
|
+
return entityKey
|
|
376
|
+
? `write availability failed for ${entityKey}`
|
|
377
|
+
: `write availability failed${error ? `: ${error}` : ''}`;
|
|
378
|
+
case 'write_updated':
|
|
379
|
+
return entityKey ? `updated ${entityKey}` : 'updated durable fact';
|
|
380
|
+
case 'write_replaced':
|
|
381
|
+
return entityKey ? `replaced ${entityKey}` : 'replaced durable fact';
|
|
382
|
+
case 'write_rejected':
|
|
383
|
+
return entityKey
|
|
384
|
+
? `rejected write for ${entityKey}${event.reason ? `: ${event.reason}` : ''}`
|
|
385
|
+
: 'rejected write';
|
|
386
|
+
case 'write_escalated':
|
|
387
|
+
return entityKey
|
|
388
|
+
? `escalated conflict for ${entityKey}`
|
|
389
|
+
: 'escalated write conflict';
|
|
390
|
+
default:
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
function eventToLearning(event) {
|
|
395
|
+
if (!LEDGER_LEARNING_ACTIONS.has(event.actionType)) {
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
398
|
+
const summary = describeEvent(event);
|
|
399
|
+
if (!summary)
|
|
400
|
+
return null;
|
|
401
|
+
return {
|
|
402
|
+
actionType: event.actionType,
|
|
403
|
+
summary,
|
|
404
|
+
timestamp: event.timestamp,
|
|
405
|
+
source: event.source,
|
|
406
|
+
host: typeof event.metadata?.host === 'string' ? event.metadata.host : null,
|
|
407
|
+
sessionId: typeof event.metadata?.sessionId === 'string' ? event.metadata.sessionId : null,
|
|
408
|
+
entityKey: event.entityType && event.entityId && event.key ? `${event.entityType}/${event.entityId}/${event.key}` : null,
|
|
409
|
+
reason: event.reason ?? null,
|
|
410
|
+
category: 'event',
|
|
411
|
+
evidenceActionTypes: [event.actionType],
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
function latestTimestamp(events) {
|
|
415
|
+
return events
|
|
416
|
+
.map((event) => event.timestamp)
|
|
417
|
+
.sort((left, right) => right.localeCompare(left))[0] ?? new Date(0).toISOString();
|
|
418
|
+
}
|
|
419
|
+
function distinct(values) {
|
|
420
|
+
return Array.from(new Set(values));
|
|
421
|
+
}
|
|
422
|
+
function inferMissingWriteCategories(sessionEvents) {
|
|
423
|
+
const categories = new Set();
|
|
424
|
+
const activityCount = sessionEvents.filter((event) => LEDGER_ACTIVITY_ACTIONS.has(event.actionType)).length;
|
|
425
|
+
const discoveryCount = sessionEvents.filter((event) => LEDGER_DISCOVERY_ACTIONS.has(event.actionType)).length;
|
|
426
|
+
const failureCount = sessionEvents.filter((event) => LEDGER_FAILURE_ACTIONS.has(event.actionType)).length;
|
|
427
|
+
const successCount = sessionEvents.filter((event) => LEDGER_SUCCESS_ACTIONS.has(event.actionType)).length;
|
|
428
|
+
if (discoveryCount > 0 || activityCount >= 4) {
|
|
429
|
+
categories.add('findings');
|
|
430
|
+
categories.add('risks_and_next_steps');
|
|
431
|
+
}
|
|
432
|
+
if (successCount > 0) {
|
|
433
|
+
categories.add('validated_results');
|
|
434
|
+
}
|
|
435
|
+
if (failureCount > 0) {
|
|
436
|
+
categories.add('failed_paths');
|
|
437
|
+
}
|
|
438
|
+
if (activityCount >= 8) {
|
|
439
|
+
categories.add('file_changes');
|
|
440
|
+
}
|
|
441
|
+
return Array.from(categories);
|
|
442
|
+
}
|
|
443
|
+
function formatWriteCategoryList(categories) {
|
|
444
|
+
const labels = categories
|
|
445
|
+
.map((category) => LEDGER_REQUIRED_WRITE_CATEGORY_LABELS[category] ?? category)
|
|
446
|
+
.filter(Boolean);
|
|
447
|
+
if (labels.length === 0) {
|
|
448
|
+
return 'what you found and what happens next';
|
|
449
|
+
}
|
|
450
|
+
if (labels.length === 1)
|
|
451
|
+
return labels[0];
|
|
452
|
+
if (labels.length === 2)
|
|
453
|
+
return `${labels[0]} and ${labels[1]}`;
|
|
454
|
+
return `${labels.slice(0, -1).join(', ')}, and ${labels[labels.length - 1]}`;
|
|
455
|
+
}
|
|
456
|
+
function tokenizeTaskText(text) {
|
|
457
|
+
if (!text)
|
|
458
|
+
return [];
|
|
459
|
+
return distinct(text
|
|
460
|
+
.toLowerCase()
|
|
461
|
+
.split(/[^a-z0-9_]+/)
|
|
462
|
+
.map((token) => token.trim())
|
|
463
|
+
.filter((token) => token.length >= 4 && !TASK_TOKEN_STOPWORDS.has(token)));
|
|
464
|
+
}
|
|
465
|
+
function expandTokenSynonym(token) {
|
|
466
|
+
return TASK_TOKEN_SYNONYMS[token] ?? token;
|
|
467
|
+
}
|
|
468
|
+
function scoreTaskSimilarity(left, right) {
|
|
469
|
+
const leftTokens = tokenizeTaskText(left);
|
|
470
|
+
const rightTokens = tokenizeTaskText(right);
|
|
471
|
+
if (leftTokens.length === 0 || rightTokens.length === 0)
|
|
472
|
+
return 0;
|
|
473
|
+
// Raw Jaccard on original tokens.
|
|
474
|
+
const leftSet = new Set(leftTokens);
|
|
475
|
+
const rightSet = new Set(rightTokens);
|
|
476
|
+
const overlap = leftTokens.filter((token) => rightSet.has(token)).length;
|
|
477
|
+
const union = new Set([...leftSet, ...rightSet]).size;
|
|
478
|
+
const rawScore = union === 0 ? 0 : overlap / union;
|
|
479
|
+
// Synonym-expanded Jaccard: canonicalize each token then recompute.
|
|
480
|
+
// This catches synonymous task descriptions that share no raw tokens
|
|
481
|
+
// (e.g. "fix docker bug" vs "repair container startup issue").
|
|
482
|
+
const leftExpanded = distinct(leftTokens.map(expandTokenSynonym));
|
|
483
|
+
const rightExpanded = distinct(rightTokens.map(expandTokenSynonym));
|
|
484
|
+
const leftExpandedSet = new Set(leftExpanded);
|
|
485
|
+
const rightExpandedSet = new Set(rightExpanded);
|
|
486
|
+
const expandedOverlap = leftExpanded.filter((token) => rightExpandedSet.has(token)).length;
|
|
487
|
+
const expandedUnion = new Set([...leftExpandedSet, ...rightExpandedSet]).size;
|
|
488
|
+
const expandedScore = expandedUnion === 0 ? 0 : expandedOverlap / expandedUnion;
|
|
489
|
+
return Math.max(rawScore, expandedScore);
|
|
490
|
+
}
|
|
491
|
+
function taskSummaryForEvent(event) {
|
|
492
|
+
const directTask = typeof event.metadata?.task === 'string' ? event.metadata.task : null;
|
|
493
|
+
const summarizedTask = typeof event.metadata?.taskSummary === 'string' ? event.metadata.taskSummary : null;
|
|
494
|
+
return directTask ?? summarizedTask ?? null;
|
|
495
|
+
}
|
|
496
|
+
function scopeEventsByTaskType(events, taskType) {
|
|
497
|
+
if (!taskType?.trim()) {
|
|
498
|
+
return {
|
|
499
|
+
matchedTaskType: null,
|
|
500
|
+
events: [],
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
const scored = events
|
|
504
|
+
.map((event) => ({
|
|
505
|
+
event,
|
|
506
|
+
taskSummary: taskSummaryForEvent(event),
|
|
507
|
+
score: scoreTaskSimilarity(taskSummaryForEvent(event), taskType),
|
|
508
|
+
}))
|
|
509
|
+
.filter((entry) => entry.taskSummary && entry.score >= 0.34)
|
|
510
|
+
.sort((left, right) => right.score - left.score);
|
|
511
|
+
if (scored.length === 0) {
|
|
512
|
+
return {
|
|
513
|
+
matchedTaskType: null,
|
|
514
|
+
events: [],
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
const matchingSessionIds = new Set(scored
|
|
518
|
+
.map((entry) => typeof entry.event.metadata?.sessionId === 'string' ? entry.event.metadata.sessionId : null)
|
|
519
|
+
.filter((value) => Boolean(value)));
|
|
520
|
+
const scoped = matchingSessionIds.size === 0
|
|
521
|
+
? scored.map((entry) => entry.event)
|
|
522
|
+
: events.filter((event) => {
|
|
523
|
+
const sessionId = typeof event.metadata?.sessionId === 'string' ? event.metadata.sessionId : null;
|
|
524
|
+
return sessionId ? matchingSessionIds.has(sessionId) : false;
|
|
525
|
+
});
|
|
526
|
+
return {
|
|
527
|
+
matchedTaskType: scored[0]?.taskSummary ?? null,
|
|
528
|
+
events: scoped,
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
function extractEventEntityKeys(event) {
|
|
532
|
+
const directEntityKey = event.entityType && event.entityId && event.key
|
|
533
|
+
? [`${event.entityType}/${event.entityId}/${event.key}`]
|
|
534
|
+
: [];
|
|
535
|
+
const injectedKeys = Array.isArray(event.metadata?.injectedKeys)
|
|
536
|
+
? event.metadata.injectedKeys.map((value) => String(value)).filter(Boolean)
|
|
537
|
+
: [];
|
|
538
|
+
return distinct([...directEntityKey, ...injectedKeys]);
|
|
539
|
+
}
|
|
540
|
+
function buildPriorityKeys(events) {
|
|
541
|
+
const counts = new Map();
|
|
542
|
+
const lastSeen = new Map();
|
|
543
|
+
for (const event of events) {
|
|
544
|
+
if (!LEDGER_SUCCESS_ACTIONS.has(event.actionType) && event.actionType !== 'mandatory_recall_forced') {
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
for (const entityKey of extractEventEntityKeys(event)) {
|
|
548
|
+
const parts = entityKey.split('/');
|
|
549
|
+
const key = parts.length >= 3 ? parts.slice(2).join('/') : null;
|
|
550
|
+
if (!key || key === 'attendant_state')
|
|
551
|
+
continue;
|
|
552
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
553
|
+
lastSeen.set(key, event.timestamp);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
return Array.from(counts.entries())
|
|
557
|
+
.sort((left, right) => ((right[1] - left[1])
|
|
558
|
+
|| (lastSeen.get(right[0]) ?? '').localeCompare(lastSeen.get(left[0]) ?? '')
|
|
559
|
+
|| left[0].localeCompare(right[0])))
|
|
560
|
+
.slice(0, 6)
|
|
561
|
+
.map(([key]) => key);
|
|
562
|
+
}
|
|
563
|
+
function scopeSummaries(events) {
|
|
564
|
+
return [
|
|
565
|
+
...buildHostLearnings(events),
|
|
566
|
+
...buildRecallLearnings(events),
|
|
567
|
+
...buildPersistenceLearnings(events),
|
|
568
|
+
...buildComplianceLearnings(events),
|
|
569
|
+
]
|
|
570
|
+
.sort((left, right) => right.timestamp.localeCompare(left.timestamp))
|
|
571
|
+
.map((learning) => learning.summary)
|
|
572
|
+
.slice(0, 2);
|
|
573
|
+
}
|
|
574
|
+
function scopeSupportsAmbiguousMemory(events) {
|
|
575
|
+
// Strongest signal: a memory_injected event followed by a persistence event
|
|
576
|
+
// in the same session — the agent received injected context and then wrote
|
|
577
|
+
// something durable, indicating the injection was productive.
|
|
578
|
+
const sessions = new Map();
|
|
579
|
+
for (const event of events) {
|
|
580
|
+
const sessionId = typeof event.metadata?.sessionId === 'string' ? event.metadata.sessionId : '_unknown';
|
|
581
|
+
const bucket = sessions.get(sessionId) ?? [];
|
|
582
|
+
bucket.push(event);
|
|
583
|
+
sessions.set(sessionId, bucket);
|
|
584
|
+
}
|
|
585
|
+
for (const sessionEvents of sessions.values()) {
|
|
586
|
+
const sorted = sessionEvents.slice().sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
587
|
+
let seenInjection = false;
|
|
588
|
+
for (const event of sorted) {
|
|
589
|
+
if (event.actionType === 'memory_injected') {
|
|
590
|
+
seenInjection = true;
|
|
591
|
+
}
|
|
592
|
+
else if (seenInjection && LEDGER_PERSISTENCE_ACTIONS.has(event.actionType)) {
|
|
593
|
+
return true;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
// Advisory-first fallback: injection was used in this scope even if no downstream
|
|
598
|
+
// persistence was confirmed — the pattern still signals this host uses injection.
|
|
599
|
+
if (events.some((event) => event.actionType === 'memory_injected')) {
|
|
600
|
+
return true;
|
|
601
|
+
}
|
|
602
|
+
// No injection history: fall back to whether the scope produced any durable
|
|
603
|
+
// output at all. Retrieval-only sessions (query/search without writes) do not
|
|
604
|
+
// qualify — they carry no signal that ambiguous injection would be useful.
|
|
605
|
+
return events.some((event) => LEDGER_PERSISTENCE_ACTIONS.has(event.actionType));
|
|
606
|
+
}
|
|
607
|
+
function buildComplianceLearnings(events) {
|
|
608
|
+
const sessions = new Map();
|
|
609
|
+
for (const event of events) {
|
|
610
|
+
const sessionId = typeof event.metadata?.sessionId === 'string' ? event.metadata.sessionId : null;
|
|
611
|
+
if (!sessionId)
|
|
612
|
+
continue;
|
|
613
|
+
const bucket = sessions.get(sessionId) ?? [];
|
|
614
|
+
bucket.push(event);
|
|
615
|
+
sessions.set(sessionId, bucket);
|
|
616
|
+
}
|
|
617
|
+
const out = [];
|
|
618
|
+
for (const [sessionId, sessionEvents] of sessions.entries()) {
|
|
619
|
+
const activityCount = sessionEvents.filter((event) => LEDGER_ACTIVITY_ACTIONS.has(event.actionType)).length;
|
|
620
|
+
const hasPersistence = sessionEvents.some((event) => LEDGER_PERSISTENCE_ACTIONS.has(event.actionType));
|
|
621
|
+
if (activityCount < 4 || hasPersistence) {
|
|
622
|
+
continue;
|
|
623
|
+
}
|
|
624
|
+
const missingWriteCategories = inferMissingWriteCategories(sessionEvents);
|
|
625
|
+
const host = typeof sessionEvents[0]?.metadata?.host === 'string'
|
|
626
|
+
? String(sessionEvents[0].metadata?.host)
|
|
627
|
+
: (sessionEvents[0]?.source ?? null);
|
|
628
|
+
const summary = `Recent compliance lesson: ${host ?? 'this host'} left an under-logged run after substantial retrieval work without a checkpoint or durable write. This is non-compliant for Iranti; before the next pause, write ${formatWriteCategoryList(missingWriteCategories)} and checkpoint shared progress.`;
|
|
629
|
+
out.push({
|
|
630
|
+
actionType: 'checkpoint_discipline_lesson',
|
|
631
|
+
summary,
|
|
632
|
+
timestamp: latestTimestamp(sessionEvents),
|
|
633
|
+
source: sessionEvents[0]?.source ?? 'internal',
|
|
634
|
+
host: host || null,
|
|
635
|
+
sessionId,
|
|
636
|
+
entityKey: null,
|
|
637
|
+
reason: 'missing_checkpoint_after_activity',
|
|
638
|
+
category: 'compliance',
|
|
639
|
+
evidenceActionTypes: distinct(sessionEvents.map((event) => event.actionType)),
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
return out;
|
|
643
|
+
}
|
|
644
|
+
function describeHostCapabilities(events) {
|
|
645
|
+
const capabilities = distinct(events.flatMap((event) => {
|
|
646
|
+
switch (event.actionType) {
|
|
647
|
+
case 'memory_injected':
|
|
648
|
+
return ['memory injection'];
|
|
649
|
+
case 'query_executed':
|
|
650
|
+
return ['exact lookup'];
|
|
651
|
+
case 'search_executed':
|
|
652
|
+
return ['search'];
|
|
653
|
+
case 'related_executed':
|
|
654
|
+
case 'related_deep_executed':
|
|
655
|
+
return ['relationship lookup'];
|
|
656
|
+
case 'whoknows_executed':
|
|
657
|
+
return ['agent lookup'];
|
|
658
|
+
case 'checkpoint_written':
|
|
659
|
+
return ['shared checkpoints'];
|
|
660
|
+
case 'summary_written':
|
|
661
|
+
return ['strict summaries'];
|
|
662
|
+
case 'write_available':
|
|
663
|
+
case 'write_created':
|
|
664
|
+
case 'write_updated':
|
|
665
|
+
case 'write_replaced':
|
|
666
|
+
return ['durable writes'];
|
|
667
|
+
default:
|
|
668
|
+
return [];
|
|
669
|
+
}
|
|
670
|
+
}));
|
|
671
|
+
if (capabilities.length === 0)
|
|
672
|
+
return null;
|
|
673
|
+
if (capabilities.length === 1)
|
|
674
|
+
return capabilities[0];
|
|
675
|
+
if (capabilities.length === 2)
|
|
676
|
+
return `${capabilities[0]} and ${capabilities[1]}`;
|
|
677
|
+
return `${capabilities.slice(0, -1).join(', ')}, and ${capabilities[capabilities.length - 1]}`;
|
|
678
|
+
}
|
|
679
|
+
function summarizeFailureReasons(events) {
|
|
680
|
+
const reasons = distinct(events.map((event) => event.reason ?? 'host error').filter(Boolean));
|
|
681
|
+
return reasons.slice(0, 2).join('; ');
|
|
682
|
+
}
|
|
683
|
+
function buildHostLearnings(events) {
|
|
684
|
+
const byHost = new Map();
|
|
685
|
+
for (const event of events) {
|
|
686
|
+
const host = typeof event.metadata?.host === 'string' ? event.metadata.host : event.source;
|
|
687
|
+
const key = `${event.source}|${host}`;
|
|
688
|
+
const bucket = byHost.get(key) ?? [];
|
|
689
|
+
bucket.push(event);
|
|
690
|
+
byHost.set(key, bucket);
|
|
691
|
+
}
|
|
692
|
+
const out = [];
|
|
693
|
+
for (const [identity, hostEvents] of byHost.entries()) {
|
|
694
|
+
const [source, hostName] = identity.split('|');
|
|
695
|
+
const failures = hostEvents.filter((event) => (event.actionType === 'host_failure'
|
|
696
|
+
|| event.actionType === 'integration_probe_failed'
|
|
697
|
+
|| event.actionType === 'provider_fallback_used'));
|
|
698
|
+
const successes = hostEvents.filter((event) => !failures.includes(event));
|
|
699
|
+
if (failures.length === 0 && successes.length === 0)
|
|
700
|
+
continue;
|
|
701
|
+
const capabilitySummary = describeHostCapabilities(successes);
|
|
702
|
+
let summary = null;
|
|
703
|
+
if (failures.length > 0 && capabilitySummary) {
|
|
704
|
+
summary = `Recent host lesson: ${hostName} hit ${summarizeFailureReasons(failures)}, but ${source} still completed ${capabilitySummary}.`;
|
|
705
|
+
}
|
|
706
|
+
else if (failures.length > 0) {
|
|
707
|
+
summary = `Recent host lesson: ${hostName} hit ${summarizeFailureReasons(failures)}.`;
|
|
708
|
+
}
|
|
709
|
+
else if (capabilitySummary) {
|
|
710
|
+
summary = `Recent host lesson: ${hostName} successfully completed ${capabilitySummary}.`;
|
|
711
|
+
}
|
|
712
|
+
if (!summary)
|
|
713
|
+
continue;
|
|
714
|
+
out.push({
|
|
715
|
+
actionType: 'host_lesson',
|
|
716
|
+
summary,
|
|
717
|
+
timestamp: latestTimestamp(hostEvents),
|
|
718
|
+
source,
|
|
719
|
+
host: hostName || null,
|
|
720
|
+
sessionId: typeof hostEvents[0]?.metadata?.sessionId === 'string' ? String(hostEvents[0].metadata?.sessionId) : null,
|
|
721
|
+
entityKey: null,
|
|
722
|
+
reason: failures[0]?.reason ?? null,
|
|
723
|
+
category: 'host',
|
|
724
|
+
evidenceActionTypes: distinct(hostEvents.map((event) => event.actionType)),
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
return out;
|
|
728
|
+
}
|
|
729
|
+
function buildRecallLearnings(events) {
|
|
730
|
+
const recalls = events.filter((event) => event.actionType === 'mandatory_recall_forced');
|
|
731
|
+
const injections = events.filter((event) => event.actionType === 'memory_injected');
|
|
732
|
+
const out = [];
|
|
733
|
+
for (const recall of recalls) {
|
|
734
|
+
const entityKey = recall.entityType && recall.entityId && recall.key
|
|
735
|
+
? `${recall.entityType}/${recall.entityId}/${recall.key}`
|
|
736
|
+
: null;
|
|
737
|
+
const matchingInjection = injections.find((event) => {
|
|
738
|
+
const injectedKeys = Array.isArray(event.metadata?.injectedKeys)
|
|
739
|
+
? event.metadata.injectedKeys.map((value) => String(value))
|
|
740
|
+
: [];
|
|
741
|
+
return entityKey ? injectedKeys.includes(entityKey) : event.reason === recall.reason;
|
|
742
|
+
});
|
|
743
|
+
if (!matchingInjection)
|
|
744
|
+
continue;
|
|
745
|
+
out.push({
|
|
746
|
+
actionType: 'recall_lesson',
|
|
747
|
+
summary: entityKey
|
|
748
|
+
? `Recent recall lesson: policy-forced recall successfully injected ${entityKey}.`
|
|
749
|
+
: 'Recent recall lesson: policy-forced recall successfully injected needed memory.',
|
|
750
|
+
timestamp: latestTimestamp([recall, matchingInjection]),
|
|
751
|
+
source: recall.source,
|
|
752
|
+
host: typeof recall.metadata?.host === 'string' ? recall.metadata.host : null,
|
|
753
|
+
sessionId: typeof recall.metadata?.sessionId === 'string' ? recall.metadata.sessionId : null,
|
|
754
|
+
entityKey,
|
|
755
|
+
reason: recall.reason ?? null,
|
|
756
|
+
category: 'recall',
|
|
757
|
+
evidenceActionTypes: ['mandatory_recall_forced', 'memory_injected'],
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
return out;
|
|
761
|
+
}
|
|
762
|
+
function buildPersistenceLearnings(events) {
|
|
763
|
+
const persistenceEvents = events.filter((event) => (event.actionType === 'checkpoint_written'
|
|
764
|
+
|| event.actionType === 'summary_written'
|
|
765
|
+
|| event.actionType === 'write_available'
|
|
766
|
+
|| event.actionType === 'write_created'
|
|
767
|
+
|| event.actionType === 'write_updated'
|
|
768
|
+
|| event.actionType === 'write_replaced'));
|
|
769
|
+
const byEntity = new Map();
|
|
770
|
+
for (const event of persistenceEvents) {
|
|
771
|
+
const entityKey = event.entityType && event.entityId && event.key
|
|
772
|
+
? `${event.entityType}/${event.entityId}/${event.key}`
|
|
773
|
+
: null;
|
|
774
|
+
if (!entityKey)
|
|
775
|
+
continue;
|
|
776
|
+
const bucket = byEntity.get(entityKey) ?? [];
|
|
777
|
+
bucket.push(event);
|
|
778
|
+
byEntity.set(entityKey, bucket);
|
|
779
|
+
}
|
|
780
|
+
const out = [];
|
|
781
|
+
for (const [entityKey, entityEvents] of byEntity.entries()) {
|
|
782
|
+
const hasCheckpoint = entityEvents.some((event) => event.actionType === 'checkpoint_written');
|
|
783
|
+
const hasDurable = entityEvents.some((event) => (event.actionType === 'summary_written'
|
|
784
|
+
|| event.actionType === 'write_available'
|
|
785
|
+
|| event.actionType === 'write_created'
|
|
786
|
+
|| event.actionType === 'write_updated'
|
|
787
|
+
|| event.actionType === 'write_replaced'));
|
|
788
|
+
if (!hasCheckpoint && !hasDurable)
|
|
789
|
+
continue;
|
|
790
|
+
let summary;
|
|
791
|
+
if (hasCheckpoint && hasDurable) {
|
|
792
|
+
summary = `Recent persistence lesson: ${entityKey} is being kept current through shared checkpoints and durable writes.`;
|
|
793
|
+
}
|
|
794
|
+
else if (hasCheckpoint) {
|
|
795
|
+
summary = `Recent persistence lesson: ${entityKey} is being handed off through shared checkpoints.`;
|
|
796
|
+
}
|
|
797
|
+
else {
|
|
798
|
+
summary = `Recent persistence lesson: ${entityKey} is being maintained through durable writes.`;
|
|
799
|
+
}
|
|
800
|
+
out.push({
|
|
801
|
+
actionType: 'persistence_lesson',
|
|
802
|
+
summary,
|
|
803
|
+
timestamp: latestTimestamp(entityEvents),
|
|
804
|
+
source: entityEvents[0]?.source ?? 'internal',
|
|
805
|
+
host: typeof entityEvents[0]?.metadata?.host === 'string' ? String(entityEvents[0].metadata?.host) : null,
|
|
806
|
+
sessionId: typeof entityEvents[0]?.metadata?.sessionId === 'string' ? String(entityEvents[0].metadata?.sessionId) : null,
|
|
807
|
+
entityKey,
|
|
808
|
+
reason: null,
|
|
809
|
+
category: 'persistence',
|
|
810
|
+
evidenceActionTypes: distinct(entityEvents.map((event) => event.actionType)),
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
return out;
|
|
814
|
+
}
|
|
815
|
+
async function summarizeSessionLedgerLearnings(input = {}) {
|
|
816
|
+
const events = await querySessionLedger({
|
|
817
|
+
agentId: input.agentId,
|
|
818
|
+
sessionId: input.sessionId,
|
|
819
|
+
source: input.source,
|
|
820
|
+
host: input.host,
|
|
821
|
+
since: input.since,
|
|
822
|
+
until: input.until,
|
|
823
|
+
limit: normalizeLimit(input.limit ?? 40),
|
|
824
|
+
});
|
|
825
|
+
const out = [];
|
|
826
|
+
const seen = new Set();
|
|
827
|
+
const synthesized = [
|
|
828
|
+
...buildHostLearnings(events),
|
|
829
|
+
...buildRecallLearnings(events),
|
|
830
|
+
...buildPersistenceLearnings(events),
|
|
831
|
+
...buildComplianceLearnings(events),
|
|
832
|
+
].sort((left, right) => right.timestamp.localeCompare(left.timestamp));
|
|
833
|
+
for (const learning of synthesized) {
|
|
834
|
+
const identity = [
|
|
835
|
+
learning.actionType,
|
|
836
|
+
learning.entityKey ?? '',
|
|
837
|
+
learning.reason ?? '',
|
|
838
|
+
learning.summary,
|
|
839
|
+
].join('|');
|
|
840
|
+
if (seen.has(identity))
|
|
841
|
+
continue;
|
|
842
|
+
seen.add(identity);
|
|
843
|
+
out.push(learning);
|
|
844
|
+
if (out.length >= normalizeLearningLimit(input.maxLearnings)) {
|
|
845
|
+
return out;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
for (const event of events) {
|
|
849
|
+
const learning = eventToLearning(event);
|
|
850
|
+
if (!learning)
|
|
851
|
+
continue;
|
|
852
|
+
const identity = [
|
|
853
|
+
learning.actionType,
|
|
854
|
+
learning.entityKey ?? '',
|
|
855
|
+
learning.reason ?? '',
|
|
856
|
+
learning.summary,
|
|
857
|
+
].join('|');
|
|
858
|
+
if (seen.has(identity))
|
|
859
|
+
continue;
|
|
860
|
+
seen.add(identity);
|
|
861
|
+
out.push(learning);
|
|
862
|
+
if (out.length >= normalizeLearningLimit(input.maxLearnings)) {
|
|
863
|
+
break;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
return out;
|
|
867
|
+
}
|
|
868
|
+
async function summarizeMemoryAttribution(input = {}) {
|
|
869
|
+
const events = await querySessionLedger({
|
|
870
|
+
...input,
|
|
871
|
+
actionTypes: ['memory_injected', 'memory_injection_scored'],
|
|
872
|
+
limit: normalizeLimit(input.limit ?? 200),
|
|
873
|
+
});
|
|
874
|
+
const byInjectionId = new Map();
|
|
875
|
+
for (const event of events.slice().sort((left, right) => left.timestamp.localeCompare(right.timestamp))) {
|
|
876
|
+
const injectionId = typeof event.metadata?.injectionId === 'string'
|
|
877
|
+
? event.metadata.injectionId
|
|
878
|
+
: null;
|
|
879
|
+
if (!injectionId) {
|
|
880
|
+
continue;
|
|
881
|
+
}
|
|
882
|
+
const existing = byInjectionId.get(injectionId);
|
|
883
|
+
const injectedKeys = Array.isArray(event.metadata?.injectedKeys)
|
|
884
|
+
? event.metadata.injectedKeys.map((value) => String(value)).filter(Boolean)
|
|
885
|
+
: existing?.injectedKeys ?? [];
|
|
886
|
+
const injectedEntryIds = Array.isArray(event.metadata?.injectedEntryIds)
|
|
887
|
+
? event.metadata.injectedEntryIds
|
|
888
|
+
.map((value) => Number(value))
|
|
889
|
+
.filter((value) => Number.isInteger(value))
|
|
890
|
+
: existing?.injectedEntryIds ?? [];
|
|
891
|
+
const evidenceKinds = Array.isArray(event.metadata?.evidenceKinds)
|
|
892
|
+
? event.metadata.evidenceKinds.map((value) => String(value)).filter(Boolean)
|
|
893
|
+
: existing?.evidenceKinds ?? [];
|
|
894
|
+
const sessionId = typeof event.metadata?.sessionId === 'string'
|
|
895
|
+
? event.metadata.sessionId
|
|
896
|
+
: existing?.sessionId ?? null;
|
|
897
|
+
const host = typeof event.metadata?.host === 'string'
|
|
898
|
+
? event.metadata.host
|
|
899
|
+
: existing?.host ?? null;
|
|
900
|
+
const phase = typeof event.metadata?.phase === 'string'
|
|
901
|
+
? event.metadata.phase
|
|
902
|
+
: existing?.phase ?? null;
|
|
903
|
+
byInjectionId.set(injectionId, {
|
|
904
|
+
injectionId,
|
|
905
|
+
sessionId,
|
|
906
|
+
agentId: event.agentId,
|
|
907
|
+
source: event.source,
|
|
908
|
+
host,
|
|
909
|
+
surfacedAt: existing?.surfacedAt ?? event.timestamp,
|
|
910
|
+
scoredAt: event.actionType === 'memory_injection_scored'
|
|
911
|
+
? event.timestamp
|
|
912
|
+
: (existing?.scoredAt ?? null),
|
|
913
|
+
surfaced: true,
|
|
914
|
+
used: event.actionType === 'memory_injection_scored'
|
|
915
|
+
? event.metadata?.used === true
|
|
916
|
+
: (existing?.used ?? false),
|
|
917
|
+
helpful: event.actionType === 'memory_injection_scored'
|
|
918
|
+
? event.metadata?.helpful === true
|
|
919
|
+
: (existing?.helpful ?? false),
|
|
920
|
+
injectedKeys,
|
|
921
|
+
injectedEntryIds,
|
|
922
|
+
evidenceKinds,
|
|
923
|
+
phase,
|
|
924
|
+
reason: event.reason ?? existing?.reason ?? null,
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
return Array.from(byInjectionId.values()).sort((left, right) => right.surfacedAt.localeCompare(left.surfacedAt));
|
|
928
|
+
}
|
|
929
|
+
async function buildSessionLedgerLearningProfile(input = {}) {
|
|
930
|
+
const events = await querySessionLedger({
|
|
931
|
+
agentId: input.agentId,
|
|
932
|
+
source: input.source,
|
|
933
|
+
since: input.since,
|
|
934
|
+
until: input.until,
|
|
935
|
+
limit: normalizeProfileLimit(input.limit),
|
|
936
|
+
});
|
|
937
|
+
if (events.length === 0) {
|
|
938
|
+
return null;
|
|
939
|
+
}
|
|
940
|
+
const hostEvents = input.host?.trim()
|
|
941
|
+
? events.filter((event) => {
|
|
942
|
+
const host = typeof event.metadata?.host === 'string' ? event.metadata.host : null;
|
|
943
|
+
return host === input.host;
|
|
944
|
+
})
|
|
945
|
+
: [];
|
|
946
|
+
const taskScope = scopeEventsByTaskType(events, input.taskType);
|
|
947
|
+
const taskEvents = taskScope.events;
|
|
948
|
+
const globalEvents = events;
|
|
949
|
+
const scopes = [
|
|
950
|
+
{ name: 'host', events: hostEvents },
|
|
951
|
+
{ name: 'task', events: taskEvents },
|
|
952
|
+
{ name: 'global', events: globalEvents },
|
|
953
|
+
];
|
|
954
|
+
const scopesUsed = [];
|
|
955
|
+
const summaries = [];
|
|
956
|
+
const priorityKeys = [];
|
|
957
|
+
let preferMemoryForAmbiguousTurns = false;
|
|
958
|
+
let checkpointReminder = null;
|
|
959
|
+
let missingWriteCategories = [];
|
|
960
|
+
for (const scope of scopes) {
|
|
961
|
+
if (scope.events.length === 0)
|
|
962
|
+
continue;
|
|
963
|
+
scopesUsed.push(scope.name);
|
|
964
|
+
if (scopeSupportsAmbiguousMemory(scope.events)) {
|
|
965
|
+
preferMemoryForAmbiguousTurns = true;
|
|
966
|
+
}
|
|
967
|
+
if (!checkpointReminder) {
|
|
968
|
+
const complianceLearning = buildComplianceLearnings(scope.events)[0] ?? null;
|
|
969
|
+
checkpointReminder = complianceLearning?.summary ?? null;
|
|
970
|
+
missingWriteCategories = complianceLearning ? inferMissingWriteCategories(scope.events) : [];
|
|
971
|
+
}
|
|
972
|
+
for (const summary of scopeSummaries(scope.events)) {
|
|
973
|
+
if (!summaries.includes(summary)) {
|
|
974
|
+
summaries.push(summary);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
for (const key of buildPriorityKeys(scope.events)) {
|
|
978
|
+
if (!priorityKeys.includes(key)) {
|
|
979
|
+
priorityKeys.push(key);
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
if (!preferMemoryForAmbiguousTurns && summaries.length === 0 && priorityKeys.length === 0) {
|
|
984
|
+
return null;
|
|
985
|
+
}
|
|
986
|
+
return {
|
|
987
|
+
scopesUsed,
|
|
988
|
+
preferMemoryForAmbiguousTurns,
|
|
989
|
+
priorityKeys: priorityKeys.slice(0, 6),
|
|
990
|
+
summaries: summaries.slice(0, 4),
|
|
991
|
+
matchedTaskType: taskScope.matchedTaskType,
|
|
992
|
+
needsCheckpointReminder: Boolean(checkpointReminder),
|
|
993
|
+
checkpointReminder,
|
|
994
|
+
missingWriteCategories,
|
|
995
|
+
};
|
|
996
|
+
}
|
|
997
|
+
//# sourceMappingURL=sessionLedger.js.map
|