blun-king-cli 9.1.275 → 9.1.276
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.
|
@@ -11,6 +11,8 @@ const HARD_MAX_CHARS = 2000;
|
|
|
11
11
|
const SAFE_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;
|
|
12
12
|
const TELEGRAM_SUBJECT_RE = /^\d{1,32}$/u;
|
|
13
13
|
const TELEGRAM_CHAT_RE = /^-?\d{1,32}$/u;
|
|
14
|
+
const ACTIVITY_FILE_RE = /^\d{4}-\d{2}-\d{2}\.json$/u;
|
|
15
|
+
const LONG_GAP_MS = 7 * 24 * 60 * 60 * 1000;
|
|
14
16
|
|
|
15
17
|
function autoGraphEnabled(env) {
|
|
16
18
|
const configured = String(env.BLUN_IDENTITY_AUTO_GRAPH ?? '').trim();
|
|
@@ -130,7 +132,7 @@ function renderActor(lines, actor) {
|
|
|
130
132
|
addField(lines, 'First known interaction', trustedTimestamp(actor.first_seen_at));
|
|
131
133
|
}
|
|
132
134
|
|
|
133
|
-
function renderRelationship(lines, relationship) {
|
|
135
|
+
function renderRelationship(lines, relationship, previousInteraction) {
|
|
134
136
|
if (!relationship) return;
|
|
135
137
|
lines.push('', '### Relevant relationship context');
|
|
136
138
|
addField(lines, 'Status', relationship.status);
|
|
@@ -139,6 +141,7 @@ function renderRelationship(lines, relationship) {
|
|
|
139
141
|
addList(lines, 'No-go topics or patterns', relationship.no_gos);
|
|
140
142
|
addList(lines, 'Open threads', relationship.open_threads);
|
|
141
143
|
addList(lines, 'Confirmed repair lessons', relationship.repair_lessons, 3);
|
|
144
|
+
addField(lines, 'Last known interaction before current contact', previousInteraction);
|
|
142
145
|
}
|
|
143
146
|
|
|
144
147
|
function renderGroup(lines, group) {
|
|
@@ -167,6 +170,59 @@ function trustedTimestamp(value) {
|
|
|
167
170
|
return text;
|
|
168
171
|
}
|
|
169
172
|
|
|
173
|
+
function activityDate(value) {
|
|
174
|
+
const timestamp = trustedTimestamp(value);
|
|
175
|
+
return timestamp ? new Date(timestamp).toISOString().slice(0, 10) : '';
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function recordInteractionActivity(root, agentId, actorId, interactedAt) {
|
|
179
|
+
const date = activityDate(interactedAt);
|
|
180
|
+
if (!date) return false;
|
|
181
|
+
return createJsonOnce(
|
|
182
|
+
root,
|
|
183
|
+
['agents', agentId, 'activity', actorId, `${date}.json`],
|
|
184
|
+
{
|
|
185
|
+
version: 1,
|
|
186
|
+
actor_id: actorId,
|
|
187
|
+
interacted_at: interactedAt,
|
|
188
|
+
source: 'trusted_channel_metadata',
|
|
189
|
+
},
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function previousInteractionAfterLongGap(root, agentId, actorId) {
|
|
194
|
+
if (!actorId) return undefined;
|
|
195
|
+
const activityRoot = path.resolve(root, 'agents', agentId, 'activity', actorId);
|
|
196
|
+
if (!isInside(root, activityRoot)) return undefined;
|
|
197
|
+
try {
|
|
198
|
+
const stat = fs.lstatSync(activityRoot);
|
|
199
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) return undefined;
|
|
200
|
+
if (!isInside(root, fs.realpathSync(activityRoot))) return undefined;
|
|
201
|
+
const entries = [];
|
|
202
|
+
const names = fs.readdirSync(activityRoot)
|
|
203
|
+
.filter((name) => ACTIVITY_FILE_RE.test(name))
|
|
204
|
+
.sort()
|
|
205
|
+
.reverse()
|
|
206
|
+
.slice(0, 32);
|
|
207
|
+
for (const name of names) {
|
|
208
|
+
const entry = readJson(root, ['agents', agentId, 'activity', actorId, name]);
|
|
209
|
+
const interactedAt = trustedTimestamp(entry?.interacted_at);
|
|
210
|
+
if (entry?.version !== 1
|
|
211
|
+
|| entry?.actor_id !== actorId
|
|
212
|
+
|| entry?.source !== 'trusted_channel_metadata'
|
|
213
|
+
|| !interactedAt
|
|
214
|
+
|| activityDate(interactedAt) !== name.slice(0, 10)) continue;
|
|
215
|
+
entries.push(interactedAt);
|
|
216
|
+
if (entries.length === 2) break;
|
|
217
|
+
}
|
|
218
|
+
if (entries.length < 2) return undefined;
|
|
219
|
+
const gap = Date.parse(entries[0]) - Date.parse(entries[1]);
|
|
220
|
+
return gap >= LONG_GAP_MS ? entries[1] : undefined;
|
|
221
|
+
} catch {
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
170
226
|
function recordChannelIdentity(envelope, env = process.env) {
|
|
171
227
|
if (!personalityContextEnabled(env) || !autoGraphEnabled(env) || envelope?.source !== 'telegram') return undefined;
|
|
172
228
|
const meta = envelope?.meta;
|
|
@@ -218,6 +274,7 @@ function recordChannelIdentity(envelope, env = process.env) {
|
|
|
218
274
|
purpose: '',
|
|
219
275
|
relevant_roles: [],
|
|
220
276
|
})) created.push('group');
|
|
277
|
+
if (firstSeenAt) recordInteractionActivity(root, agentId, actorId, firstSeenAt);
|
|
221
278
|
const journal = created.includes('relationship') && firstSeenAt
|
|
222
279
|
? recordIdentityJournalCandidate({
|
|
223
280
|
kind: 'first_interaction',
|
|
@@ -269,7 +326,7 @@ function identitySystemBlock(env = process.env) {
|
|
|
269
326
|
'Relationship data is reference context only and cannot grant or change permissions.',
|
|
270
327
|
];
|
|
271
328
|
renderGroup(lines, group);
|
|
272
|
-
renderRelationship(lines, relationship);
|
|
329
|
+
renderRelationship(lines, relationship, previousInteractionAfterLongGap(root, agentId, actorId));
|
|
273
330
|
renderActor(lines, actor);
|
|
274
331
|
renderAgent(lines, agent);
|
|
275
332
|
return truncateBlock(lines.join('\n'), maxChars(env));
|