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
|
@@ -17,6 +17,8 @@ exports.autoRememberAssistantFacts = autoRememberAssistantFacts;
|
|
|
17
17
|
exports.rememberAssistantResponseFacts = rememberAssistantResponseFacts;
|
|
18
18
|
exports.parseBackfillChatTranscript = parseBackfillChatTranscript;
|
|
19
19
|
exports.backfillChatHistory = backfillChatHistory;
|
|
20
|
+
const staffEventRegistry_1 = require("./staffEventRegistry");
|
|
21
|
+
const semanticFactTags_1 = require("./semanticFactTags");
|
|
20
22
|
exports.USER_PROMPT_AUTO_REMEMBER_SOURCE = 'UserPromptAutoRemember';
|
|
21
23
|
const PERSONAL_MEMORY_KEYS = new Set([
|
|
22
24
|
'name',
|
|
@@ -69,8 +71,380 @@ function buildTextFact(key, value, scope = 'personal') {
|
|
|
69
71
|
key: cleanKey,
|
|
70
72
|
value: { text: cleanValue },
|
|
71
73
|
summary: `${cleanKey.replace(/_/g, ' ')} is ${cleanValue}`,
|
|
74
|
+
durableClass: cleanKey.startsWith('favorite_') ? 'preference' : 'profile',
|
|
72
75
|
};
|
|
73
76
|
}
|
|
77
|
+
function dedupeStrings(values) {
|
|
78
|
+
const seen = new Set();
|
|
79
|
+
const deduped = [];
|
|
80
|
+
for (const value of values) {
|
|
81
|
+
const trimmed = value.trim();
|
|
82
|
+
if (!trimmed)
|
|
83
|
+
continue;
|
|
84
|
+
const normalized = trimmed.toLowerCase();
|
|
85
|
+
if (seen.has(normalized))
|
|
86
|
+
continue;
|
|
87
|
+
seen.add(normalized);
|
|
88
|
+
deduped.push(trimmed);
|
|
89
|
+
}
|
|
90
|
+
return deduped;
|
|
91
|
+
}
|
|
92
|
+
function dedupeObjects(values) {
|
|
93
|
+
const seen = new Set();
|
|
94
|
+
const deduped = [];
|
|
95
|
+
for (const value of values) {
|
|
96
|
+
const identity = JSON.stringify(value);
|
|
97
|
+
if (seen.has(identity))
|
|
98
|
+
continue;
|
|
99
|
+
seen.add(identity);
|
|
100
|
+
deduped.push(value);
|
|
101
|
+
}
|
|
102
|
+
return deduped;
|
|
103
|
+
}
|
|
104
|
+
function normalizeListItems(raw) {
|
|
105
|
+
return raw
|
|
106
|
+
.split(/(?:,|;|\band\b)/i)
|
|
107
|
+
.map((item) => item.trim())
|
|
108
|
+
.filter(Boolean);
|
|
109
|
+
}
|
|
110
|
+
function trimSummary(text, max = 220) {
|
|
111
|
+
const normalized = text.trim();
|
|
112
|
+
if (normalized.length <= max)
|
|
113
|
+
return normalized;
|
|
114
|
+
return `${normalized.slice(0, max - 3).trimEnd()}...`;
|
|
115
|
+
}
|
|
116
|
+
function readStringArray(value, preferredKey) {
|
|
117
|
+
if (Array.isArray(value)) {
|
|
118
|
+
return value.map((item) => String(item ?? '').trim()).filter(Boolean);
|
|
119
|
+
}
|
|
120
|
+
if (typeof value !== 'object' || value === null)
|
|
121
|
+
return [];
|
|
122
|
+
const record = value;
|
|
123
|
+
const direct = record[preferredKey];
|
|
124
|
+
if (Array.isArray(direct)) {
|
|
125
|
+
return direct.map((item) => String(item ?? '').trim()).filter(Boolean);
|
|
126
|
+
}
|
|
127
|
+
if (Array.isArray(record.items)) {
|
|
128
|
+
return record.items.map((item) => String(item ?? '').trim()).filter(Boolean);
|
|
129
|
+
}
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
function readFileChangeItems(value) {
|
|
133
|
+
if (typeof value !== 'object' || value === null)
|
|
134
|
+
return [];
|
|
135
|
+
const record = value;
|
|
136
|
+
const items = Array.isArray(record.items) ? record.items : [];
|
|
137
|
+
return items.filter((item) => typeof item === 'object' && item !== null);
|
|
138
|
+
}
|
|
139
|
+
function readActionItems(value) {
|
|
140
|
+
if (typeof value !== 'object' || value === null)
|
|
141
|
+
return [];
|
|
142
|
+
const record = value;
|
|
143
|
+
const items = Array.isArray(record.items) ? record.items : [];
|
|
144
|
+
return items.filter((item) => typeof item === 'object' && item !== null);
|
|
145
|
+
}
|
|
146
|
+
function mergeFactValue(key, existing, incoming) {
|
|
147
|
+
if (key === 'open_risks') {
|
|
148
|
+
return { items: dedupeStrings([...readStringArray(existing, 'items'), ...readStringArray(incoming, 'items')]) };
|
|
149
|
+
}
|
|
150
|
+
if (key === 'important_artifacts') {
|
|
151
|
+
return { items: dedupeStrings([...readStringArray(existing, 'items'), ...readStringArray(incoming, 'items'), ...readStringArray(existing, 'files'), ...readStringArray(incoming, 'files')]) };
|
|
152
|
+
}
|
|
153
|
+
if (key === 'failed_paths' || key === 'alternative_routes') {
|
|
154
|
+
return { items: dedupeStrings([...readStringArray(existing, 'items'), ...readStringArray(incoming, 'items')]) };
|
|
155
|
+
}
|
|
156
|
+
if (key === 'recent_file_changes') {
|
|
157
|
+
return { items: dedupeObjects([...readFileChangeItems(existing), ...readFileChangeItems(incoming)]) };
|
|
158
|
+
}
|
|
159
|
+
if (key === 'recent_actions') {
|
|
160
|
+
return { items: dedupeObjects([...readActionItems(existing), ...readActionItems(incoming)]) };
|
|
161
|
+
}
|
|
162
|
+
return incoming;
|
|
163
|
+
}
|
|
164
|
+
function summarizeMergedFact(key, value, fallback) {
|
|
165
|
+
if (key === 'open_risks') {
|
|
166
|
+
const items = readStringArray(value, 'items');
|
|
167
|
+
return items.length > 0 ? trimSummary(`open risks include ${items.join('; ')}`) : fallback;
|
|
168
|
+
}
|
|
169
|
+
if (key === 'important_artifacts') {
|
|
170
|
+
const items = readStringArray(value, 'items');
|
|
171
|
+
return items.length > 0 ? trimSummary(`important artifacts include ${items.join('; ')}`) : fallback;
|
|
172
|
+
}
|
|
173
|
+
if (key === 'failed_paths') {
|
|
174
|
+
const items = readStringArray(value, 'items');
|
|
175
|
+
return items.length > 0 ? trimSummary(`failed paths include ${items.join('; ')}`) : fallback;
|
|
176
|
+
}
|
|
177
|
+
if (key === 'alternative_routes') {
|
|
178
|
+
const items = readStringArray(value, 'items');
|
|
179
|
+
return items.length > 0 ? trimSummary(`alternative routes include ${items.join('; ')}`) : fallback;
|
|
180
|
+
}
|
|
181
|
+
if (key === 'recent_file_changes') {
|
|
182
|
+
const items = readFileChangeItems(value).map((item) => {
|
|
183
|
+
const action = String(item.action ?? 'updated').trim();
|
|
184
|
+
const path = String(item.path ?? '').trim();
|
|
185
|
+
const toPath = String(item.toPath ?? '').trim();
|
|
186
|
+
const purpose = String(item.purpose ?? '').trim();
|
|
187
|
+
if (!path)
|
|
188
|
+
return '';
|
|
189
|
+
if (toPath)
|
|
190
|
+
return `${action} ${path} -> ${toPath}${purpose ? ` (${purpose})` : ''}`;
|
|
191
|
+
return `${action} ${path}${purpose ? ` (${purpose})` : ''}`;
|
|
192
|
+
}).filter(Boolean);
|
|
193
|
+
return items.length > 0 ? trimSummary(`recent file changes include ${items.join('; ')}`) : fallback;
|
|
194
|
+
}
|
|
195
|
+
if (key === 'recent_actions') {
|
|
196
|
+
const items = readActionItems(value).map((item) => {
|
|
197
|
+
const kind = String(item.kind ?? 'action').trim();
|
|
198
|
+
const summary = String(item.summary ?? '').trim();
|
|
199
|
+
const status = String(item.status ?? '').trim();
|
|
200
|
+
if (!summary)
|
|
201
|
+
return '';
|
|
202
|
+
return status ? `[${status}] ${kind}: ${summary}` : `${kind}: ${summary}`;
|
|
203
|
+
}).filter(Boolean);
|
|
204
|
+
return items.length > 0 ? trimSummary(`recent actions include ${items.join('; ')}`) : fallback;
|
|
205
|
+
}
|
|
206
|
+
return fallback;
|
|
207
|
+
}
|
|
208
|
+
function buildFactProperties(fact, phase) {
|
|
209
|
+
const mergeStrategy = ['open_risks', 'important_artifacts', 'recent_file_changes', 'recent_actions', 'failed_paths', 'alternative_routes'].includes(fact.key)
|
|
210
|
+
? 'append_dedupe'
|
|
211
|
+
: 'replace';
|
|
212
|
+
return {
|
|
213
|
+
memoryScope: fact.scope,
|
|
214
|
+
capturePhase: phase,
|
|
215
|
+
durableClass: fact.durableClass,
|
|
216
|
+
canonicalKey: fact.key,
|
|
217
|
+
mergeStrategy,
|
|
218
|
+
...(0, semanticFactTags_1.buildSemanticFactTags)({
|
|
219
|
+
memoryScope: fact.scope,
|
|
220
|
+
durableClass: fact.durableClass,
|
|
221
|
+
mergeStrategy,
|
|
222
|
+
}),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function extractProjectOperationalFacts(text) {
|
|
226
|
+
const facts = [];
|
|
227
|
+
const decisionMatch = text.match(/^we decided(?: that)? (.+)$/i);
|
|
228
|
+
if (decisionMatch) {
|
|
229
|
+
facts.push({
|
|
230
|
+
scope: 'project',
|
|
231
|
+
key: 'decision',
|
|
232
|
+
value: { text: decisionMatch[1].trim() },
|
|
233
|
+
summary: `decision is ${decisionMatch[1].trim()}`,
|
|
234
|
+
durableClass: 'decision',
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
const nextStepMatch = text.match(/^(?:the )?next step is (.+)$/i);
|
|
238
|
+
if (nextStepMatch) {
|
|
239
|
+
facts.push({
|
|
240
|
+
scope: 'project',
|
|
241
|
+
key: 'next_step',
|
|
242
|
+
value: { instruction: nextStepMatch[1].trim() },
|
|
243
|
+
summary: `next step is ${nextStepMatch[1].trim()}`,
|
|
244
|
+
durableClass: 'next_step',
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
const currentStepMatch = text.match(/^(?:the )?current step is (.+)$/i);
|
|
248
|
+
if (currentStepMatch) {
|
|
249
|
+
facts.push({
|
|
250
|
+
scope: 'project',
|
|
251
|
+
key: 'current_step',
|
|
252
|
+
value: { text: currentStepMatch[1].trim() },
|
|
253
|
+
summary: `current step is ${currentStepMatch[1].trim()}`,
|
|
254
|
+
durableClass: 'current_step',
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
const blockerMatch = text.match(/^(?:the )?blocker is (.+)$/i);
|
|
258
|
+
if (blockerMatch) {
|
|
259
|
+
facts.push({
|
|
260
|
+
scope: 'project',
|
|
261
|
+
key: 'blocker',
|
|
262
|
+
value: { text: blockerMatch[1].trim() },
|
|
263
|
+
summary: `blocker is ${blockerMatch[1].trim()}`,
|
|
264
|
+
durableClass: 'blocker',
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
const ownerMatch = text.match(/^(?:the )?current owner is (.+)$/i);
|
|
268
|
+
if (ownerMatch) {
|
|
269
|
+
facts.push({
|
|
270
|
+
scope: 'project',
|
|
271
|
+
key: 'current_owner',
|
|
272
|
+
value: { text: ownerMatch[1].trim() },
|
|
273
|
+
summary: `current owner is ${ownerMatch[1].trim()}`,
|
|
274
|
+
durableClass: 'owner',
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
const openRisksMatch = text.match(/^(?:the )?(?:open risks?|risks?) (?:are|include) (.+)$/i);
|
|
278
|
+
if (openRisksMatch) {
|
|
279
|
+
const items = dedupeStrings(normalizeListItems(openRisksMatch[1]));
|
|
280
|
+
if (items.length > 0) {
|
|
281
|
+
facts.push({
|
|
282
|
+
scope: 'project',
|
|
283
|
+
key: 'open_risks',
|
|
284
|
+
value: { items },
|
|
285
|
+
summary: trimSummary(`open risks include ${items.join('; ')}`),
|
|
286
|
+
durableClass: 'open_risks',
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
const artifactsMatch = text.match(/^(?:important )?artifacts? (?:are|include) (.+)$/i);
|
|
291
|
+
if (artifactsMatch) {
|
|
292
|
+
const items = dedupeStrings(normalizeListItems(artifactsMatch[1]));
|
|
293
|
+
if (items.length > 0) {
|
|
294
|
+
facts.push({
|
|
295
|
+
scope: 'project',
|
|
296
|
+
key: 'important_artifacts',
|
|
297
|
+
value: { items },
|
|
298
|
+
summary: trimSummary(`important artifacts include ${items.join('; ')}`),
|
|
299
|
+
durableClass: 'artifact',
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
const failedPathMatch = text.match(/^(?:the )?failed path (?:is|was) (.+)$/i);
|
|
304
|
+
if (failedPathMatch) {
|
|
305
|
+
const items = dedupeStrings(normalizeListItems(failedPathMatch[1]));
|
|
306
|
+
if (items.length > 0) {
|
|
307
|
+
facts.push({
|
|
308
|
+
scope: 'project',
|
|
309
|
+
key: 'failed_paths',
|
|
310
|
+
value: { items },
|
|
311
|
+
summary: trimSummary(`failed paths include ${items.join('; ')}`),
|
|
312
|
+
durableClass: 'failed_path',
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
const alternativeRouteMatch = text.match(/^(?:the )?alternative route (?:is|was) (.+)$/i);
|
|
317
|
+
if (alternativeRouteMatch) {
|
|
318
|
+
const items = dedupeStrings(normalizeListItems(alternativeRouteMatch[1]));
|
|
319
|
+
if (items.length > 0) {
|
|
320
|
+
facts.push({
|
|
321
|
+
scope: 'project',
|
|
322
|
+
key: 'alternative_routes',
|
|
323
|
+
value: { items },
|
|
324
|
+
summary: trimSummary(`alternative routes include ${items.join('; ')}`),
|
|
325
|
+
durableClass: 'alternative_route',
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
const createdFileMatch = text.match(/^file created (\S+)(?: for (.+))?$/i);
|
|
330
|
+
if (createdFileMatch) {
|
|
331
|
+
facts.push({
|
|
332
|
+
scope: 'project',
|
|
333
|
+
key: 'recent_file_changes',
|
|
334
|
+
value: { items: [{ action: 'created', path: createdFileMatch[1].trim(), purpose: createdFileMatch[2]?.trim() || undefined }] },
|
|
335
|
+
summary: trimSummary(`recent file changes include created ${createdFileMatch[1].trim()}${createdFileMatch[2]?.trim() ? ` (${createdFileMatch[2].trim()})` : ''}`),
|
|
336
|
+
durableClass: 'file_change',
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
const movedFileMatch = text.match(/^file moved (\S+) to (\S+)(?: for (.+))?$/i);
|
|
340
|
+
if (movedFileMatch) {
|
|
341
|
+
facts.push({
|
|
342
|
+
scope: 'project',
|
|
343
|
+
key: 'recent_file_changes',
|
|
344
|
+
value: { items: [{ action: 'moved', path: movedFileMatch[1].trim(), toPath: movedFileMatch[2].trim(), purpose: movedFileMatch[3]?.trim() || undefined }] },
|
|
345
|
+
summary: trimSummary(`recent file changes include moved ${movedFileMatch[1].trim()} to ${movedFileMatch[2].trim()}${movedFileMatch[3]?.trim() ? ` (${movedFileMatch[3].trim()})` : ''}`),
|
|
346
|
+
durableClass: 'file_change',
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
const renamedFileMatch = text.match(/^file renamed (\S+) to (\S+)(?: for (.+))?$/i);
|
|
350
|
+
if (renamedFileMatch) {
|
|
351
|
+
facts.push({
|
|
352
|
+
scope: 'project',
|
|
353
|
+
key: 'recent_file_changes',
|
|
354
|
+
value: { items: [{ action: 'renamed', path: renamedFileMatch[1].trim(), toPath: renamedFileMatch[2].trim(), purpose: renamedFileMatch[3]?.trim() || undefined }] },
|
|
355
|
+
summary: trimSummary(`recent file changes include renamed ${renamedFileMatch[1].trim()} to ${renamedFileMatch[2].trim()}${renamedFileMatch[3]?.trim() ? ` (${renamedFileMatch[3].trim()})` : ''}`),
|
|
356
|
+
durableClass: 'file_change',
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
const deletedFileMatch = text.match(/^file deleted (\S+)(?: because (.+))?$/i);
|
|
360
|
+
if (deletedFileMatch) {
|
|
361
|
+
facts.push({
|
|
362
|
+
scope: 'project',
|
|
363
|
+
key: 'recent_file_changes',
|
|
364
|
+
value: { items: [{ action: 'deleted', path: deletedFileMatch[1].trim(), purpose: deletedFileMatch[2]?.trim() || undefined }] },
|
|
365
|
+
summary: trimSummary(`recent file changes include deleted ${deletedFileMatch[1].trim()}${deletedFileMatch[2]?.trim() ? ` (${deletedFileMatch[2].trim()})` : ''}`),
|
|
366
|
+
durableClass: 'file_change',
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
const commandRanMatch = text.match(/^command ran (.+)$/i);
|
|
370
|
+
if (commandRanMatch) {
|
|
371
|
+
const target = commandRanMatch[1].trim();
|
|
372
|
+
facts.push({
|
|
373
|
+
scope: 'project',
|
|
374
|
+
key: 'recent_actions',
|
|
375
|
+
value: { items: [{ kind: 'command', summary: `ran ${target}`, status: 'completed', target }] },
|
|
376
|
+
summary: trimSummary(`recent actions include [completed] command: ran ${target}`),
|
|
377
|
+
durableClass: 'action_log',
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
const commandFailedMatch = text.match(/^command failed (.+)$/i);
|
|
381
|
+
if (commandFailedMatch) {
|
|
382
|
+
const target = commandFailedMatch[1].trim();
|
|
383
|
+
facts.push({
|
|
384
|
+
scope: 'project',
|
|
385
|
+
key: 'recent_actions',
|
|
386
|
+
value: { items: [{ kind: 'command', summary: `failed ${target}`, status: 'failed', target }] },
|
|
387
|
+
summary: trimSummary(`recent actions include [failed] command: failed ${target}`),
|
|
388
|
+
durableClass: 'action_log',
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
const testPassedMatch = text.match(/^test passed (.+)$/i);
|
|
392
|
+
if (testPassedMatch) {
|
|
393
|
+
const target = testPassedMatch[1].trim();
|
|
394
|
+
facts.push({
|
|
395
|
+
scope: 'project',
|
|
396
|
+
key: 'recent_actions',
|
|
397
|
+
value: { items: [{ kind: 'test', summary: `passed ${target}`, status: 'passed', target }] },
|
|
398
|
+
summary: trimSummary(`recent actions include [passed] test: passed ${target}`),
|
|
399
|
+
durableClass: 'action_log',
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
const testFailedMatch = text.match(/^test failed (.+)$/i);
|
|
403
|
+
if (testFailedMatch) {
|
|
404
|
+
const target = testFailedMatch[1].trim();
|
|
405
|
+
facts.push({
|
|
406
|
+
scope: 'project',
|
|
407
|
+
key: 'recent_actions',
|
|
408
|
+
value: { items: [{ kind: 'test', summary: `failed ${target}`, status: 'failed', target }] },
|
|
409
|
+
summary: trimSummary(`recent actions include [failed] test: failed ${target}`),
|
|
410
|
+
durableClass: 'action_log',
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
const validationPassedMatch = text.match(/^validation passed (.+)$/i);
|
|
414
|
+
if (validationPassedMatch) {
|
|
415
|
+
const target = validationPassedMatch[1].trim();
|
|
416
|
+
facts.push({
|
|
417
|
+
scope: 'project',
|
|
418
|
+
key: 'recent_actions',
|
|
419
|
+
value: { items: [{ kind: 'validation', summary: `passed ${target}`, status: 'passed', target }] },
|
|
420
|
+
summary: trimSummary(`recent actions include [passed] validation: passed ${target}`),
|
|
421
|
+
durableClass: 'action_log',
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
const validationFailedMatch = text.match(/^validation failed (.+)$/i);
|
|
425
|
+
if (validationFailedMatch) {
|
|
426
|
+
const target = validationFailedMatch[1].trim();
|
|
427
|
+
facts.push({
|
|
428
|
+
scope: 'project',
|
|
429
|
+
key: 'recent_actions',
|
|
430
|
+
value: { items: [{ kind: 'validation', summary: `failed ${target}`, status: 'failed', target }] },
|
|
431
|
+
summary: trimSummary(`recent actions include [failed] validation: failed ${target}`),
|
|
432
|
+
durableClass: 'action_log',
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
const searchRanMatch = text.match(/^search ran (.+)$/i);
|
|
436
|
+
if (searchRanMatch) {
|
|
437
|
+
const target = searchRanMatch[1].trim();
|
|
438
|
+
facts.push({
|
|
439
|
+
scope: 'project',
|
|
440
|
+
key: 'recent_actions',
|
|
441
|
+
value: { items: [{ kind: 'search', summary: `ran ${target}`, status: 'completed', target }] },
|
|
442
|
+
summary: trimSummary(`recent actions include [completed] search: ran ${target}`),
|
|
443
|
+
durableClass: 'action_log',
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
return facts;
|
|
447
|
+
}
|
|
74
448
|
function isAutoRememberEnabled() {
|
|
75
449
|
const raw = process.env.IRANTI_AUTO_REMEMBER?.trim().toLowerCase();
|
|
76
450
|
return raw === '1' || raw === 'true' || raw === 'yes' || raw === 'on';
|
|
@@ -144,7 +518,7 @@ function classifyMemoryScope(message) {
|
|
|
144
518
|
const normalized = normalizePrompt(message).toLowerCase();
|
|
145
519
|
if (!normalized)
|
|
146
520
|
return null;
|
|
147
|
-
if (/\b(next step|blocker|decision|current owner)\b/.test(normalized)) {
|
|
521
|
+
if (/\b(next step|current step|blocker|decision|current owner|open risks?|artifacts?|failed path|alternative route|file (?:created|moved|renamed|deleted))\b/.test(normalized)) {
|
|
148
522
|
return 'project';
|
|
149
523
|
}
|
|
150
524
|
if (/\b(my|me|i)\b/.test(normalized)) {
|
|
@@ -222,6 +596,30 @@ function detectMandatoryRecall(message) {
|
|
|
222
596
|
reason: 'project_owner_recall',
|
|
223
597
|
};
|
|
224
598
|
}
|
|
599
|
+
if (/\b(?:what|remind me)(?:[^.?!]*)\bcurrent step\b/i.test(normalized)) {
|
|
600
|
+
return {
|
|
601
|
+
required: true,
|
|
602
|
+
scope: 'project',
|
|
603
|
+
key: 'current_step',
|
|
604
|
+
reason: 'project_current_step_recall',
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
if (/\b(?:what|which|remind me)(?:[^.?!]*)\bopen risks?\b/i.test(normalized)) {
|
|
608
|
+
return {
|
|
609
|
+
required: true,
|
|
610
|
+
scope: 'project',
|
|
611
|
+
key: 'open_risks',
|
|
612
|
+
reason: 'project_open_risks_recall',
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
if (/\b(?:what|which|remind me)(?:[^.?!]*)\b(?:important )?artifacts?\b/i.test(normalized)) {
|
|
616
|
+
return {
|
|
617
|
+
required: true,
|
|
618
|
+
scope: 'project',
|
|
619
|
+
key: 'important_artifacts',
|
|
620
|
+
reason: 'project_artifacts_recall',
|
|
621
|
+
};
|
|
622
|
+
}
|
|
225
623
|
return { required: false, scope: null };
|
|
226
624
|
}
|
|
227
625
|
function extractExplicitPromptMemory(prompt) {
|
|
@@ -256,33 +654,7 @@ function extractExplicitPromptMemory(prompt) {
|
|
|
256
654
|
if (preferenceMatch) {
|
|
257
655
|
facts.push(buildTextFact('likes', preferenceMatch[1]));
|
|
258
656
|
}
|
|
259
|
-
|
|
260
|
-
if (decisionMatch) {
|
|
261
|
-
facts.push({
|
|
262
|
-
scope: 'project',
|
|
263
|
-
key: 'decision',
|
|
264
|
-
value: { text: decisionMatch[1].trim() },
|
|
265
|
-
summary: `decision is ${decisionMatch[1].trim()}`,
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
const nextStepMatch = stripped.match(/^(?:the )?next step is (.+)$/i);
|
|
269
|
-
if (nextStepMatch) {
|
|
270
|
-
facts.push({
|
|
271
|
-
scope: 'project',
|
|
272
|
-
key: 'next_step',
|
|
273
|
-
value: { instruction: nextStepMatch[1].trim() },
|
|
274
|
-
summary: `next step is ${nextStepMatch[1].trim()}`,
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
const blockerMatch = stripped.match(/^(?:the )?blocker is (.+)$/i);
|
|
278
|
-
if (blockerMatch) {
|
|
279
|
-
facts.push({
|
|
280
|
-
scope: 'project',
|
|
281
|
-
key: 'blocker',
|
|
282
|
-
value: { text: blockerMatch[1].trim() },
|
|
283
|
-
summary: `blocker is ${blockerMatch[1].trim()}`,
|
|
284
|
-
});
|
|
285
|
-
}
|
|
657
|
+
facts.push(...extractProjectOperationalFacts(stripped));
|
|
286
658
|
const deduped = new Map();
|
|
287
659
|
for (const fact of facts) {
|
|
288
660
|
if (!fact.key || fact.summary.length > 220)
|
|
@@ -308,42 +680,7 @@ function extractExplicitAssistantMemory(response) {
|
|
|
308
680
|
})) {
|
|
309
681
|
facts.push(buildTextFact(yourFieldMatch[1], yourFieldMatch[2]));
|
|
310
682
|
}
|
|
311
|
-
|
|
312
|
-
if (decisionMatch) {
|
|
313
|
-
facts.push({
|
|
314
|
-
scope: 'project',
|
|
315
|
-
key: 'decision',
|
|
316
|
-
value: { text: decisionMatch[1].trim() },
|
|
317
|
-
summary: `decision is ${decisionMatch[1].trim()}`,
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
const nextStepMatch = lower.match(/^(?:the )?next step is (.+)$/i);
|
|
321
|
-
if (nextStepMatch) {
|
|
322
|
-
facts.push({
|
|
323
|
-
scope: 'project',
|
|
324
|
-
key: 'next_step',
|
|
325
|
-
value: { instruction: nextStepMatch[1].trim() },
|
|
326
|
-
summary: `next step is ${nextStepMatch[1].trim()}`,
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
const blockerMatch = lower.match(/^(?:the )?blocker is (.+)$/i);
|
|
330
|
-
if (blockerMatch) {
|
|
331
|
-
facts.push({
|
|
332
|
-
scope: 'project',
|
|
333
|
-
key: 'blocker',
|
|
334
|
-
value: { text: blockerMatch[1].trim() },
|
|
335
|
-
summary: `blocker is ${blockerMatch[1].trim()}`,
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
const ownerMatch = lower.match(/^(?:the )?current owner is (.+)$/i);
|
|
339
|
-
if (ownerMatch) {
|
|
340
|
-
facts.push({
|
|
341
|
-
scope: 'project',
|
|
342
|
-
key: 'current_owner',
|
|
343
|
-
value: { text: ownerMatch[1].trim() },
|
|
344
|
-
summary: `current owner is ${ownerMatch[1].trim()}`,
|
|
345
|
-
});
|
|
346
|
-
}
|
|
683
|
+
facts.push(...extractProjectOperationalFacts(lower));
|
|
347
684
|
const deduped = new Map();
|
|
348
685
|
for (const fact of facts) {
|
|
349
686
|
if (!fact.key || fact.summary.length > 220)
|
|
@@ -361,7 +698,7 @@ function comparableValue(value) {
|
|
|
361
698
|
}
|
|
362
699
|
}
|
|
363
700
|
async function persistExtractedFacts(params) {
|
|
364
|
-
const { iranti, facts, agent, source, phase, confidence, entity, projectEntity, personalEntity, } = params;
|
|
701
|
+
const { iranti, facts, agent, source, phase, confidence, entity, projectEntity, personalEntity, ledgerContext, } = params;
|
|
365
702
|
const skipped = [];
|
|
366
703
|
let written = 0;
|
|
367
704
|
const writtenEntities = new Set();
|
|
@@ -374,7 +711,9 @@ async function persistExtractedFacts(params) {
|
|
|
374
711
|
continue;
|
|
375
712
|
}
|
|
376
713
|
const existing = await iranti.query(targetEntity, fact.key).catch(() => ({ found: false }));
|
|
377
|
-
|
|
714
|
+
const mergedValue = existing.found ? mergeFactValue(fact.key, existing.value, fact.value) : fact.value;
|
|
715
|
+
const mergedSummary = summarizeMergedFact(fact.key, mergedValue, fact.summary);
|
|
716
|
+
if (existing.found && comparableValue(existing.value) === comparableValue(mergedValue)) {
|
|
378
717
|
skipped.push({ key: fact.key, reason: 'unchanged' });
|
|
379
718
|
continue;
|
|
380
719
|
}
|
|
@@ -384,12 +723,32 @@ async function persistExtractedFacts(params) {
|
|
|
384
723
|
await iranti.write({
|
|
385
724
|
entity: targetEntity,
|
|
386
725
|
key: fact.key,
|
|
387
|
-
value:
|
|
388
|
-
summary:
|
|
726
|
+
value: mergedValue,
|
|
727
|
+
summary: mergedSummary,
|
|
389
728
|
confidence,
|
|
390
729
|
source: writeSource,
|
|
391
730
|
agent,
|
|
731
|
+
properties: buildFactProperties(fact, phase),
|
|
392
732
|
});
|
|
733
|
+
if (phase === 'assistant_response') {
|
|
734
|
+
(0, staffEventRegistry_1.getStaffEventEmitter)().emit({
|
|
735
|
+
staffComponent: 'Attendant',
|
|
736
|
+
actionType: 'summary_written',
|
|
737
|
+
agentId: agent,
|
|
738
|
+
source: ledgerContext?.source?.trim() || source,
|
|
739
|
+
entityType: targetEntity.split('/', 2)[0],
|
|
740
|
+
entityId: targetEntity.split('/', 2)[1],
|
|
741
|
+
key: fact.key,
|
|
742
|
+
reason: 'strict_assistant_summary_persisted',
|
|
743
|
+
level: 'audit',
|
|
744
|
+
metadata: {
|
|
745
|
+
memoryScope: fact.scope,
|
|
746
|
+
durableClass: fact.durableClass,
|
|
747
|
+
capturePhase: phase,
|
|
748
|
+
...(ledgerContext?.host ? { host: ledgerContext.host } : {}),
|
|
749
|
+
},
|
|
750
|
+
});
|
|
751
|
+
}
|
|
393
752
|
written += 1;
|
|
394
753
|
writtenEntities.add(targetEntity);
|
|
395
754
|
}
|
|
@@ -402,7 +761,7 @@ async function persistExtractedFacts(params) {
|
|
|
402
761
|
};
|
|
403
762
|
}
|
|
404
763
|
async function autoRememberPromptFacts(params) {
|
|
405
|
-
const { iranti, prompt, agent, source, entity, projectEntity, personalEntity, confidence = 96, } = params;
|
|
764
|
+
const { iranti, prompt, agent, source, entity, projectEntity, personalEntity, confidence = 96, ledgerContext, } = params;
|
|
406
765
|
if (!isAutoRememberEnabled()) {
|
|
407
766
|
return {
|
|
408
767
|
enabled: false,
|
|
@@ -422,10 +781,11 @@ async function autoRememberPromptFacts(params) {
|
|
|
422
781
|
entity,
|
|
423
782
|
projectEntity,
|
|
424
783
|
personalEntity,
|
|
784
|
+
ledgerContext,
|
|
425
785
|
});
|
|
426
786
|
}
|
|
427
787
|
async function autoRememberAssistantFacts(params) {
|
|
428
|
-
const { iranti, response, agent, source, entity, projectEntity, personalEntity, confidence = 90, } = params;
|
|
788
|
+
const { iranti, response, agent, source, entity, projectEntity, personalEntity, confidence = 90, ledgerContext, } = params;
|
|
429
789
|
if (!isAutoRememberEnabled()) {
|
|
430
790
|
return {
|
|
431
791
|
enabled: false,
|
|
@@ -445,10 +805,11 @@ async function autoRememberAssistantFacts(params) {
|
|
|
445
805
|
entity,
|
|
446
806
|
projectEntity,
|
|
447
807
|
personalEntity,
|
|
808
|
+
ledgerContext,
|
|
448
809
|
});
|
|
449
810
|
}
|
|
450
811
|
async function rememberAssistantResponseFacts(params) {
|
|
451
|
-
const { iranti, response, agent, source, entity, projectEntity, personalEntity, confidence = 90, } = params;
|
|
812
|
+
const { iranti, response, agent, source, entity, projectEntity, personalEntity, confidence = 90, ledgerContext, } = params;
|
|
452
813
|
return persistExtractedFacts({
|
|
453
814
|
iranti,
|
|
454
815
|
facts: extractExplicitAssistantMemory(response),
|
|
@@ -459,6 +820,7 @@ async function rememberAssistantResponseFacts(params) {
|
|
|
459
820
|
entity,
|
|
460
821
|
projectEntity,
|
|
461
822
|
personalEntity,
|
|
823
|
+
ledgerContext,
|
|
462
824
|
});
|
|
463
825
|
}
|
|
464
826
|
function parseBackfillChatTranscript(content) {
|