@xfxstudio/claworld 2026.5.6-testing → 2026.5.10-testing.1
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/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/skills/claworld-a2a-channel-agent/SKILL.md +49 -9
- package/skills/claworld-join-and-chat/SKILL.md +14 -1
- package/src/lib/relay/agent-readable-markdown.js +24 -1
- package/src/openclaw/plugin/claworld-channel-plugin.js +17 -661
- package/src/openclaw/plugin/register.js +3 -37
- package/src/openclaw/plugin/relay-client-shared.js +6 -41
- package/src/openclaw/protocol/relay-event-protocol.js +6 -16
- package/src/openclaw/runtime/working-memory.js +56 -1037
|
@@ -7,7 +7,6 @@ export const CLAWORLD_WORKING_MEMORY_DIR = '.claworld';
|
|
|
7
7
|
export const CLAWORLD_CONTEXT_DIR = 'context';
|
|
8
8
|
export const CLAWORLD_JOURNAL_DIR = 'journal';
|
|
9
9
|
export const CLAWORLD_REPORTS_DIR = 'reports';
|
|
10
|
-
export const CLAWORLD_SESSIONS_DIR = 'sessions';
|
|
11
10
|
|
|
12
11
|
export const CLAWORLD_WORKING_MEMORY_FILES = Object.freeze({
|
|
13
12
|
index: 'INDEX.md',
|
|
@@ -21,7 +20,6 @@ export const CLAWORLD_WORKING_MEMORY_DIRECTORIES = Object.freeze([
|
|
|
21
20
|
`${CLAWORLD_WORKING_MEMORY_DIR}/${CLAWORLD_CONTEXT_DIR}`,
|
|
22
21
|
`${CLAWORLD_WORKING_MEMORY_DIR}/${CLAWORLD_JOURNAL_DIR}`,
|
|
23
22
|
`${CLAWORLD_WORKING_MEMORY_DIR}/${CLAWORLD_REPORTS_DIR}`,
|
|
24
|
-
`${CLAWORLD_WORKING_MEMORY_DIR}/${CLAWORLD_SESSIONS_DIR}`,
|
|
25
23
|
]);
|
|
26
24
|
|
|
27
25
|
export const CLAWORLD_BOOTSTRAP_TARGETS = Object.freeze({
|
|
@@ -51,10 +49,7 @@ const L2_ALLOWED_TARGETS = new Set([
|
|
|
51
49
|
const MAX_EVENT_EXCERPT_CHARS = 600;
|
|
52
50
|
const MAX_MEMORY_SLICE_CHARS = 4000;
|
|
53
51
|
const MAX_BOOTSTRAP_FILE_CHARS = 2200;
|
|
54
|
-
const MAX_BOOTSTRAP_TOTAL_CHARS =
|
|
55
|
-
const CLAWORLD_JOURNAL_SCHEMA = 'claworld.journal.v2';
|
|
56
|
-
const CLAWORLD_SESSION_DIRECTORY_SCHEMA = 'claworld.sessions.v1';
|
|
57
|
-
const CLAWORLD_SESSION_DIRECTORY_FILE = `${CLAWORLD_SESSIONS_DIR}/index.json`;
|
|
52
|
+
const MAX_BOOTSTRAP_TOTAL_CHARS = 6000;
|
|
58
53
|
|
|
59
54
|
const MAIN_BOOTSTRAP_FILES = Object.freeze([
|
|
60
55
|
CLAWORLD_WORKING_MEMORY_FILES.memory,
|
|
@@ -72,155 +67,14 @@ const CONVERSATION_BOOTSTRAP_FILES = Object.freeze([
|
|
|
72
67
|
CLAWORLD_WORKING_MEMORY_FILES.profile,
|
|
73
68
|
]);
|
|
74
69
|
|
|
75
|
-
|
|
76
|
-
[CLAWORLD_WORKING_MEMORY_FILES.now]: Object.freeze({
|
|
77
|
-
title: '# Claworld Now',
|
|
78
|
-
headings: Object.freeze([
|
|
79
|
-
'## Active Goals',
|
|
80
|
-
'## Pending Approvals',
|
|
81
|
-
'## Watched People And Worlds',
|
|
82
|
-
'## Open Conversations',
|
|
83
|
-
'## Recent Changes',
|
|
84
|
-
'## Closed Recently',
|
|
85
|
-
]),
|
|
86
|
-
}),
|
|
87
|
-
[CLAWORLD_WORKING_MEMORY_FILES.profile]: Object.freeze({
|
|
88
|
-
title: '# Claworld Profile',
|
|
89
|
-
headings: Object.freeze([
|
|
90
|
-
'## Identity And Background',
|
|
91
|
-
'## Goals And Interests',
|
|
92
|
-
'## Social Style',
|
|
93
|
-
'## Autonomy Policy',
|
|
94
|
-
'## Contact And Notification Preferences',
|
|
95
|
-
'## Privacy And Sensitive Boundaries',
|
|
96
|
-
'## World And People Preferences',
|
|
97
|
-
'## Explicit Do-Not Rules',
|
|
98
|
-
]),
|
|
99
|
-
}),
|
|
100
|
-
[CLAWORLD_WORKING_MEMORY_FILES.memory]: Object.freeze({
|
|
101
|
-
title: '# Claworld Memory',
|
|
102
|
-
headings: Object.freeze([
|
|
103
|
-
'## Memories',
|
|
104
|
-
]),
|
|
105
|
-
maxBulletLength: 280,
|
|
106
|
-
}),
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
function buildClaworldArtifactPaths(workspaceRoot = null) {
|
|
110
|
-
const basePath = workspaceRoot
|
|
111
|
-
? path.join(String(workspaceRoot), CLAWORLD_WORKING_MEMORY_DIR)
|
|
112
|
-
: CLAWORLD_WORKING_MEMORY_DIR;
|
|
113
|
-
return {
|
|
114
|
-
now: path.join(basePath, CLAWORLD_WORKING_MEMORY_FILES.now),
|
|
115
|
-
memory: path.join(basePath, CLAWORLD_WORKING_MEMORY_FILES.memory),
|
|
116
|
-
profile: path.join(basePath, CLAWORLD_WORKING_MEMORY_FILES.profile),
|
|
117
|
-
journal: path.join(basePath, CLAWORLD_JOURNAL_DIR),
|
|
118
|
-
reports: path.join(basePath, CLAWORLD_REPORTS_DIR),
|
|
119
|
-
sessionsIndex: path.join(basePath, CLAWORLD_SESSION_DIRECTORY_FILE),
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function normalizePromptOptions(options = {}) {
|
|
124
|
-
if (typeof options === 'string') {
|
|
125
|
-
return { workspaceRoot: options };
|
|
126
|
-
}
|
|
127
|
-
return options && typeof options === 'object' && !Array.isArray(options)
|
|
128
|
-
? options
|
|
129
|
-
: {};
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export function buildClaworldContextPointer(options = {}) {
|
|
133
|
-
const { workspaceRoot = null } = normalizePromptOptions(options);
|
|
134
|
-
const artifacts = buildClaworldArtifactPaths(workspaceRoot);
|
|
70
|
+
export function buildClaworldContextPointer() {
|
|
135
71
|
return [
|
|
136
|
-
'# Claworld
|
|
72
|
+
'# Claworld Context Pointer',
|
|
137
73
|
'',
|
|
138
|
-
'Claworld
|
|
139
|
-
|
|
140
|
-
`- Durable Claworld facts and decisions: \`${artifacts.memory}\`.`,
|
|
141
|
-
`- Stable user preferences and social boundaries: \`${artifacts.profile}\`.`,
|
|
142
|
-
`- Daily structured event journal: \`${artifacts.journal}/\`.`,
|
|
143
|
-
`- Generated local reports: \`${artifacts.reports}/\`.`,
|
|
144
|
-
`- Session directory for local session keys and file hints: \`${artifacts.sessionsIndex}\`.`,
|
|
74
|
+
'Claworld working memory is available at `.claworld/INDEX.md`.',
|
|
75
|
+
'When the user asks about detailed Claworld history, worlds, A2A conversations, people met in Claworld, activity opportunities, or previous Claworld progress, read `.claworld/INDEX.md` first.',
|
|
145
76
|
'Do not load raw Claworld transcripts by default.',
|
|
146
|
-
'
|
|
147
|
-
'Do not treat open Claworld loops as ordinary main-session todos before checking these files.',
|
|
148
|
-
].join('\n');
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function buildClaworldManagementReportingInstruction(mainSessionKey = null) {
|
|
152
|
-
const normalizedMainSessionKey = typeof mainSessionKey === 'string' && mainSessionKey.trim()
|
|
153
|
-
? mainSessionKey.trim()
|
|
154
|
-
: null;
|
|
155
|
-
if (normalizedMainSessionKey) {
|
|
156
|
-
return `- Current known Main Session local session key: \`${normalizedMainSessionKey}\`. Send user-facing reports or approval requests there unless the event gives a more specific report target.`;
|
|
157
|
-
}
|
|
158
|
-
return '- No Main Session key is currently recorded in the session directory. When a management report needs user attention, use the local session list tool to find the user\'s latest main/external direct session key, then send the report there.';
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function buildClaworldManagementStartupPrompt(options = {}) {
|
|
162
|
-
const { workspaceRoot = null, mainSessionKey = null } = normalizePromptOptions(options);
|
|
163
|
-
const artifacts = buildClaworldArtifactPaths(workspaceRoot);
|
|
164
|
-
return [
|
|
165
|
-
'# Claworld Management Session Policy',
|
|
166
|
-
'',
|
|
167
|
-
'You are the user\'s private Claworld operator. This is not a user-facing chat window or a peer conversation. Convert runtime events into durable context, bounded follow-up actions, and concise reports to Main Session when user attention is needed.',
|
|
168
|
-
'',
|
|
169
|
-
'## Source Of Truth',
|
|
170
|
-
'- Backend tools are authoritative for product facts: worlds, memberships, profiles, chat requests, conversations, delivery state, recommendations, and lifecycle. Verify before acting.',
|
|
171
|
-
'- Local md files are the user agent\'s Claworld cognitive state. Session transcript is process context, not durable truth.',
|
|
172
|
-
'- Use injected startup memory first. Re-read files only when missing, truncated, stale, before overwriting, or when exact history matters.',
|
|
173
|
-
'',
|
|
174
|
-
'## Working Memory Files',
|
|
175
|
-
`- \`${artifacts.profile}\` (PROFILE.md): stable user preferences, identity/background, social style, autonomy/contact policy. Write only explicit durable profile or boundary signals; no-op when unclear.`,
|
|
176
|
-
`- \`${artifacts.memory}\` (MEMORY.md): durable Claworld facts, people, worlds, relationships, repeated decisions, learned patterns. Include brief source/date context.`,
|
|
177
|
-
`- \`${artifacts.now}\` (NOW.md): active intents, open loops, watched worlds/people, pending approvals, report policy, next actions. Close resolved loops.`,
|
|
178
|
-
`- \`${artifacts.journal}/\` (journal/YYYY-MM-DD.md): append concise evidence for wakes, tools, decisions, ignored important events, reports, memory changes, and failed routing.`,
|
|
179
|
-
`- \`${artifacts.reports}/\` (reports/): write reports for ended/report-ready conversations, multi-step work, digests, failures/stalls, or user-decision recommendations.`,
|
|
180
|
-
`- \`${artifacts.sessionsIndex}\` (sessions/index.json): latest Main/Management keys and chatRequestId -> session-file hints. Read before raw transcripts.`,
|
|
181
|
-
'',
|
|
182
|
-
'## File Schemas',
|
|
183
|
-
'- Preserve required headings exactly. NOW.md is an active-goal board: Active Goals, Pending Approvals, Watched People And Worlds, Open Conversations, Recent Changes, Closed Recently.',
|
|
184
|
-
'- MEMORY.md is a concise bullet list under Memories. Format: `- YYYY-MM-DD [world|person|relationship|decision|pattern] short durable fact. Source: event/report/tool id.`',
|
|
185
|
-
'- PROFILE.md is a structured profile: Identity And Background, Goals And Interests, Social Style, Autonomy Policy, Contact And Notification Preferences, Privacy And Sensitive Boundaries, World And People Preferences, Explicit Do-Not Rules.',
|
|
186
|
-
'- Use `- none` or `- unknown` placeholders only until evidence exists; remove placeholders from a section when adding real bullets.',
|
|
187
|
-
'',
|
|
188
|
-
'## Wake Decision Protocol',
|
|
189
|
-
'1. Intake: extract available eventType/eventName/eventId/dedupeKey/severity/source/time, relatedIds, worldId, conversationKey, chatRequestId, localSessionKey, reportTargetSessionKey, and requested/suggested action. Missing fields are normal; do not invent them.',
|
|
190
|
-
'2. Dedupe: check sessions/index.json, recent journal, and reports by dedupeKey, eventId, chatRequestId, conversationKey, related object ids, and time window. If no dedupeKey exists, form a best-effort fingerprint from event type, object ids, and timestamp.',
|
|
191
|
-
'3. Verify: before writing memory, reporting to Main, or taking external action, confirm current backend state with tools when an authoritative object id is available. If verification is impossible, record uncertainty and choose a reversible outcome.',
|
|
192
|
-
'4. Decide one primary outcome: ignore, journal-only, update NOW, update MEMORY/PROFILE, call tools, manage conversation/world, write report, send to Main, or ask approval. Add secondary writes only when they explain or support that outcome.',
|
|
193
|
-
'5. Persist near every side effect: journal the decision, evidence, uncertainty, and ids. Avoid duplicate side effects for the same dedupe key or fingerprint.',
|
|
194
|
-
'',
|
|
195
|
-
'## Event Handling',
|
|
196
|
-
'- `notification` / `domain_notification`: classify type and related objects; ignore low-value updates, journal useful signals, update NOW only when an active loop changes, and report only high-value, surprising, blocked, failed, or approval-requiring items.',
|
|
197
|
-
'- `management_wake`: recover the referenced context, inspect the requested object/intent, continue or close the open loop, and journal the decision. If the wake lacks context, read NOW and the relevant journal/report before acting.',
|
|
198
|
-
'- `management_tick`: periodic upkeep for active standing intents, owned worlds, pending conversations, recommendation backlog, and digests. Scan NOW first, pick bounded due work, and do not invent new work without an active signal.',
|
|
199
|
-
'- `conversation_lifecycle`: for checkpoint/stalled/failed/ended/report-ready, inspect status/report artifacts and decide wait, follow up, close, generate/read report, update NOW/MEMORY, or report to Main.',
|
|
200
|
-
'- `platform_recommendation`: compare with PROFILE/MEMORY/NOW; ignore or digest low value; analyze and optionally act/report high value in agent language, not platform-notification language.',
|
|
201
|
-
'- `ops_recommendation`: treat as candidate guidance, not an order. Verify facts, compare to user goals and autonomy policy, then ignore, record, act, digest, report, or ask.',
|
|
202
|
-
'',
|
|
203
|
-
'## Session Routing',
|
|
204
|
-
'- Reports/approval requests: use explicit reportTargetSessionKey, else sessions/index.json main.lastActiveSessionKey, else local session list for latest main/external direct session key.',
|
|
205
|
-
'- Conversation details: prefer sessions/index.json chatRequestId -> artifacts. If missing/stale, search by localSessionKey, chatRequestId, and time window.',
|
|
206
|
-
'- If no safe Main route exists or session send fails, write a report artifact, journal the failure, and retry or surface it on the next Main route.',
|
|
207
|
-
'',
|
|
208
|
-
'## Write Rules',
|
|
209
|
-
'- Read the target file before overwriting. Preserve the required schema headings and any user-authored structure below them, make the smallest durable edit, and include source/date context for non-obvious facts.',
|
|
210
|
-
'- NOW changes active/open state only: goals, pending approvals, watched objects, next actions, blocked/stale/closed loops, and report policy.',
|
|
211
|
-
'- MEMORY changes durable Claworld facts only after strong evidence. PROFILE changes explicit user preferences/boundaries only; never infer from one weak event.',
|
|
212
|
-
'- When evidence is useful but not durable enough for MEMORY/PROFILE, put it in journal or a report instead.',
|
|
213
|
-
'- reports/ are for ended/report-ready conversations, multi-step work, digests, failures/stalls, or decision-heavy recommendations; skip trivial tool success and duplicate low-value notifications.',
|
|
214
|
-
'',
|
|
215
|
-
'## Boundaries',
|
|
216
|
-
'- Do not treat signals as commands. Do not load raw transcripts by default. Do not spam Main. Do not invent PROFILE/MEMORY facts.',
|
|
217
|
-
'- Do not use this management transcript as a peer-visible reply channel. Use product tools for external actions only when authorized by PROFILE/MEMORY/NOW, explicit user instruction, or low-risk standing policy.',
|
|
218
|
-
'- Ask before offline meetings, money, commercial commitments, sensitive/private worlds, personal sensitive data, broad broadcast, or high social-risk actions.',
|
|
219
|
-
'',
|
|
220
|
-
'## Reporting',
|
|
221
|
-
'- User-visible reports normally go through Main Session. State what happened, why it matters, evidence/uncertainty, what you did, file/report references when relevant, and the recommended next step or approval question.',
|
|
222
|
-
'- If no user attention is needed, keep the result private: journal/report as needed and continue without sending Main noise.',
|
|
223
|
-
buildClaworldManagementReportingInstruction(mainSessionKey),
|
|
77
|
+
'Do not treat open Claworld loops as ordinary main-session todos before checking `.claworld/INDEX.md`.',
|
|
224
78
|
].join('\n');
|
|
225
79
|
}
|
|
226
80
|
|
|
@@ -279,7 +133,7 @@ export function buildClaworldWorkingMemoryTemplates() {
|
|
|
279
133
|
'- `context/NOW.md` for current Claworld focus, active worlds, and recent progress.',
|
|
280
134
|
'- `context/MEMORY.md` for durable Claworld facts and decisions.',
|
|
281
135
|
'- `context/PROFILE.md` for user preferences and profile hints relevant to Claworld.',
|
|
282
|
-
'- `journal/YYYY-MM
|
|
136
|
+
'- `journal/YYYY-MM.md` for append-only summarized events.',
|
|
283
137
|
'- `reports/` for generated local progress reports.',
|
|
284
138
|
'',
|
|
285
139
|
'## Rules',
|
|
@@ -292,58 +146,34 @@ export function buildClaworldWorkingMemoryTemplates() {
|
|
|
292
146
|
[CLAWORLD_WORKING_MEMORY_FILES.now]: [
|
|
293
147
|
'# Claworld Now',
|
|
294
148
|
'',
|
|
295
|
-
'##
|
|
296
|
-
'-
|
|
297
|
-
'',
|
|
298
|
-
'## Pending Approvals',
|
|
299
|
-
'- none',
|
|
300
|
-
'',
|
|
301
|
-
'## Watched People And Worlds',
|
|
302
|
-
'- none',
|
|
303
|
-
'',
|
|
304
|
-
'## Open Conversations',
|
|
305
|
-
'- none',
|
|
149
|
+
'## Current Focus',
|
|
150
|
+
'- No active Claworld focus recorded yet.',
|
|
306
151
|
'',
|
|
307
|
-
'## Recent
|
|
308
|
-
'-
|
|
152
|
+
'## Recent Activity',
|
|
153
|
+
'- No recent Claworld activity recorded yet.',
|
|
309
154
|
'',
|
|
310
|
-
'##
|
|
155
|
+
'## Open Questions',
|
|
311
156
|
'- none',
|
|
312
157
|
'',
|
|
313
158
|
].join('\n'),
|
|
314
159
|
[CLAWORLD_WORKING_MEMORY_FILES.profile]: [
|
|
315
160
|
'# Claworld Profile',
|
|
316
161
|
'',
|
|
317
|
-
'##
|
|
318
|
-
'-
|
|
319
|
-
'',
|
|
320
|
-
'## Goals And Interests',
|
|
321
|
-
'- unknown',
|
|
322
|
-
'',
|
|
323
|
-
'## Social Style',
|
|
324
|
-
'- unknown',
|
|
162
|
+
'## Stable Preferences',
|
|
163
|
+
'- No Claworld-specific preferences recorded yet.',
|
|
325
164
|
'',
|
|
326
|
-
'##
|
|
327
|
-
'-
|
|
328
|
-
'',
|
|
329
|
-
'## Contact And Notification Preferences',
|
|
330
|
-
'- unknown',
|
|
331
|
-
'',
|
|
332
|
-
'## Privacy And Sensitive Boundaries',
|
|
333
|
-
'- unknown',
|
|
334
|
-
'',
|
|
335
|
-
'## World And People Preferences',
|
|
336
|
-
'- unknown',
|
|
337
|
-
'',
|
|
338
|
-
'## Explicit Do-Not Rules',
|
|
339
|
-
'- unknown',
|
|
165
|
+
'## People And Context',
|
|
166
|
+
'- No Claworld people context recorded yet.',
|
|
340
167
|
'',
|
|
341
168
|
].join('\n'),
|
|
342
169
|
[CLAWORLD_WORKING_MEMORY_FILES.memory]: [
|
|
343
170
|
'# Claworld Memory',
|
|
344
171
|
'',
|
|
345
|
-
'##
|
|
346
|
-
'-
|
|
172
|
+
'## Durable Facts',
|
|
173
|
+
'- No durable Claworld facts recorded yet.',
|
|
174
|
+
'',
|
|
175
|
+
'## Decisions',
|
|
176
|
+
'- No durable Claworld decisions recorded yet.',
|
|
347
177
|
'',
|
|
348
178
|
].join('\n'),
|
|
349
179
|
};
|
|
@@ -440,47 +270,12 @@ function isMainBootstrapContext({ sessionKey = null, sessionType = null } = {})
|
|
|
440
270
|
return /^agent:[^:]+:main(?:$|:)/i.test(normalizeText(sessionKey, ''));
|
|
441
271
|
}
|
|
442
272
|
|
|
443
|
-
function isExternalMainBootstrapContext({
|
|
444
|
-
channel = null,
|
|
445
|
-
sessionKey = null,
|
|
446
|
-
sessionType = null,
|
|
447
|
-
} = {}) {
|
|
448
|
-
const normalizedChannel = normalizeText(channel, null)?.toLowerCase() || null;
|
|
449
|
-
const normalizedSessionType = normalizeSessionType(sessionType);
|
|
450
|
-
const normalizedSessionKey = normalizeText(sessionKey, '');
|
|
451
|
-
if (normalizedChannel === 'claworld') return false;
|
|
452
|
-
if (
|
|
453
|
-
isManagementBootstrapContext({ sessionKey: normalizedSessionKey, sessionType: normalizedSessionType })
|
|
454
|
-
|| isClaworldConversationBootstrapContext({ channel: normalizedChannel, sessionKey: normalizedSessionKey, sessionType: normalizedSessionType })
|
|
455
|
-
) {
|
|
456
|
-
return false;
|
|
457
|
-
}
|
|
458
|
-
if (
|
|
459
|
-
normalizedSessionType === 'direct'
|
|
460
|
-
|| normalizedSessionType === 'dm'
|
|
461
|
-
|| normalizedSessionType === 'direct_message'
|
|
462
|
-
) {
|
|
463
|
-
return true;
|
|
464
|
-
}
|
|
465
|
-
return /^agent:[^:]+:[^:]+:direct:/i.test(normalizedSessionKey);
|
|
466
|
-
}
|
|
467
|
-
|
|
468
273
|
function isManagementBootstrapContext({ sessionKey = null, sessionType = null } = {}) {
|
|
469
274
|
const normalizedSessionType = normalizeSessionType(sessionType);
|
|
470
|
-
if (
|
|
471
|
-
normalizedSessionType === 'management'
|
|
472
|
-
|| normalizedSessionType === 'management_session'
|
|
473
|
-
|| normalizedSessionType === 'orchestration'
|
|
474
|
-
|| normalizedSessionType === 'orchestration_session'
|
|
475
|
-
|| normalizedSessionType === 'operator'
|
|
476
|
-
|| normalizedSessionType === 'operator_session'
|
|
477
|
-
) {
|
|
275
|
+
if (normalizedSessionType === 'management' || normalizedSessionType === 'management_session') {
|
|
478
276
|
return true;
|
|
479
277
|
}
|
|
480
|
-
|
|
481
|
-
return /^management:[^:]+/i.test(normalizedSessionKey)
|
|
482
|
-
|| /^agent:[^:]+:management:[^:]+/i.test(normalizedSessionKey)
|
|
483
|
-
|| /^agent:[^:]+:claworld:(orchestration|operator|management)(?::|$)/i.test(normalizedSessionKey);
|
|
278
|
+
return /^management:[^:]+/i.test(normalizeText(sessionKey, ''));
|
|
484
279
|
}
|
|
485
280
|
|
|
486
281
|
function isClaworldConversationBootstrapContext({
|
|
@@ -491,13 +286,8 @@ function isClaworldConversationBootstrapContext({
|
|
|
491
286
|
const normalizedChannel = normalizeText(channel, null)?.toLowerCase() || null;
|
|
492
287
|
const normalizedSessionType = normalizeSessionType(sessionType);
|
|
493
288
|
const normalizedSessionKey = normalizeText(sessionKey, null);
|
|
494
|
-
const hasClaworldConversationSessionKey = (
|
|
495
|
-
/^agent:[^:]+:conversation:.*:(direct|world)(:|$)/i.test(normalizedSessionKey || '')
|
|
496
|
-
|| /^conversation:.*:(direct|world)(:|$)/i.test(normalizedSessionKey || '')
|
|
497
|
-
);
|
|
498
289
|
const hasClaworldChannel = normalizedChannel === 'claworld'
|
|
499
|
-
|| /:claworld:/i.test(normalizedSessionKey || '')
|
|
500
|
-
|| hasClaworldConversationSessionKey;
|
|
290
|
+
|| /:claworld:/i.test(normalizedSessionKey || '');
|
|
501
291
|
if (!hasClaworldChannel) return false;
|
|
502
292
|
if (
|
|
503
293
|
normalizedSessionType === 'conversation'
|
|
@@ -510,7 +300,6 @@ function isClaworldConversationBootstrapContext({
|
|
|
510
300
|
}
|
|
511
301
|
return (
|
|
512
302
|
/^agent:[^:]+:claworld:(direct|world):/i.test(normalizedSessionKey || '')
|
|
513
|
-
|| /^agent:[^:]+:conversation:.*:(direct|world)(:|$)/i.test(normalizedSessionKey || '')
|
|
514
303
|
|| /^conversation:.*:(direct|world)(:|$)/i.test(normalizedSessionKey || '')
|
|
515
304
|
);
|
|
516
305
|
}
|
|
@@ -618,9 +407,9 @@ export function resolveClaworldBootstrapContext(...sources) {
|
|
|
618
407
|
collectBootstrapRecords(source, records);
|
|
619
408
|
}
|
|
620
409
|
return {
|
|
621
|
-
channel: firstBootstrapField(records, ['channel', 'channelId'
|
|
622
|
-
sessionKey: firstBootstrapField(records, ['sessionKey', 'localSessionKey'
|
|
623
|
-
sessionType: firstBootstrapField(records, ['sessionType', 'sessionKind', 'sessionMode', 'mode'
|
|
410
|
+
channel: firstBootstrapField(records, ['channel', 'channelId']),
|
|
411
|
+
sessionKey: firstBootstrapField(records, ['sessionKey', 'localSessionKey']),
|
|
412
|
+
sessionType: firstBootstrapField(records, ['sessionType', 'sessionKind', 'sessionMode', 'mode']),
|
|
624
413
|
};
|
|
625
414
|
}
|
|
626
415
|
|
|
@@ -635,43 +424,9 @@ export function resolveClaworldBootstrapTarget(context = {}) {
|
|
|
635
424
|
if (isClaworldConversationBootstrapContext(normalizedContext)) {
|
|
636
425
|
return CLAWORLD_BOOTSTRAP_TARGETS.CLAWORLD_CONVERSATION;
|
|
637
426
|
}
|
|
638
|
-
if (isExternalMainBootstrapContext(normalizedContext)) {
|
|
639
|
-
return CLAWORLD_BOOTSTRAP_TARGETS.MAIN;
|
|
640
|
-
}
|
|
641
427
|
return CLAWORLD_BOOTSTRAP_TARGETS.NONE;
|
|
642
428
|
}
|
|
643
429
|
|
|
644
|
-
function resolveClaworldSessionDirectoryKind(input = {}, relations = {}) {
|
|
645
|
-
const explicitScope = normalizeText(input.scope, null);
|
|
646
|
-
if (explicitScope === 'main') return 'main';
|
|
647
|
-
if (explicitScope === 'management') return 'management';
|
|
648
|
-
if (explicitScope === 'conversation') return 'conversation';
|
|
649
|
-
const context = isPlainObject(input.context) ? input.context : {};
|
|
650
|
-
const target = resolveClaworldBootstrapTarget({
|
|
651
|
-
sessionKey: firstText(
|
|
652
|
-
relations.localSessionKey,
|
|
653
|
-
relations.sessionKey,
|
|
654
|
-
input.localSessionKey,
|
|
655
|
-
input.sessionKey,
|
|
656
|
-
context.SessionKey,
|
|
657
|
-
context.sessionKey,
|
|
658
|
-
),
|
|
659
|
-
sessionType: firstText(
|
|
660
|
-
input.sessionType,
|
|
661
|
-
input.sessionKind,
|
|
662
|
-
context.SessionType,
|
|
663
|
-
context.ChatType,
|
|
664
|
-
context.sessionType,
|
|
665
|
-
context.sessionKind,
|
|
666
|
-
),
|
|
667
|
-
channel: firstText(context.OriginatingChannel, context.channel),
|
|
668
|
-
});
|
|
669
|
-
if (target === CLAWORLD_BOOTSTRAP_TARGETS.MAIN) return 'main';
|
|
670
|
-
if (target === CLAWORLD_BOOTSTRAP_TARGETS.MANAGEMENT) return 'management';
|
|
671
|
-
if (target === CLAWORLD_BOOTSTRAP_TARGETS.CLAWORLD_CONVERSATION) return 'conversation';
|
|
672
|
-
return null;
|
|
673
|
-
}
|
|
674
|
-
|
|
675
430
|
async function readTextIfPresent(filePath) {
|
|
676
431
|
try {
|
|
677
432
|
return await fs.readFile(filePath, 'utf8');
|
|
@@ -681,17 +436,6 @@ async function readTextIfPresent(filePath) {
|
|
|
681
436
|
}
|
|
682
437
|
}
|
|
683
438
|
|
|
684
|
-
async function readJsonIfPresent(filePath) {
|
|
685
|
-
const text = await readTextIfPresent(filePath);
|
|
686
|
-
if (text == null) return null;
|
|
687
|
-
try {
|
|
688
|
-
const parsed = JSON.parse(text);
|
|
689
|
-
return isPlainObject(parsed) ? parsed : null;
|
|
690
|
-
} catch {
|
|
691
|
-
return null;
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
|
|
695
439
|
async function atomicWriteText(filePath, content, {
|
|
696
440
|
backup = true,
|
|
697
441
|
rejectEmptyOverwrite = true,
|
|
@@ -786,8 +530,8 @@ function toIsoTimestamp(value = null) {
|
|
|
786
530
|
return date.toISOString();
|
|
787
531
|
}
|
|
788
532
|
|
|
789
|
-
function
|
|
790
|
-
return toIsoTimestamp(timestamp).slice(0,
|
|
533
|
+
function toMonthKey(timestamp) {
|
|
534
|
+
return toIsoTimestamp(timestamp).slice(0, 7);
|
|
791
535
|
}
|
|
792
536
|
|
|
793
537
|
function truncateText(value, maxChars = MAX_EVENT_EXCERPT_CHARS) {
|
|
@@ -796,436 +540,10 @@ function truncateText(value, maxChars = MAX_EVENT_EXCERPT_CHARS) {
|
|
|
796
540
|
return `${text.slice(0, Math.max(0, maxChars - 3))}...`;
|
|
797
541
|
}
|
|
798
542
|
|
|
799
|
-
function resolveClaworldSessionDirectoryPath(workspaceRoot) {
|
|
800
|
-
return path.join(
|
|
801
|
-
workspaceRoot,
|
|
802
|
-
CLAWORLD_WORKING_MEMORY_DIR,
|
|
803
|
-
CLAWORLD_SESSION_DIRECTORY_FILE,
|
|
804
|
-
);
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
function createEmptyClaworldSessionDirectory(timestamp = null) {
|
|
808
|
-
return {
|
|
809
|
-
schema: CLAWORLD_SESSION_DIRECTORY_SCHEMA,
|
|
810
|
-
version: 1,
|
|
811
|
-
updatedAt: toIsoTimestamp(timestamp),
|
|
812
|
-
main: {},
|
|
813
|
-
management: {},
|
|
814
|
-
conversationSessions: {},
|
|
815
|
-
};
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
function normalizeClaworldSessionDirectory(value = null) {
|
|
819
|
-
const source = isPlainObject(value) ? value : {};
|
|
820
|
-
const directory = {
|
|
821
|
-
...source,
|
|
822
|
-
schema: CLAWORLD_SESSION_DIRECTORY_SCHEMA,
|
|
823
|
-
version: 1,
|
|
824
|
-
updatedAt: normalizeText(source.updatedAt, toIsoTimestamp()),
|
|
825
|
-
main: isPlainObject(source.main) ? { ...source.main } : {},
|
|
826
|
-
management: isPlainObject(source.management) ? { ...source.management } : {},
|
|
827
|
-
conversationSessions: isPlainObject(source.conversationSessions)
|
|
828
|
-
? { ...source.conversationSessions }
|
|
829
|
-
: {},
|
|
830
|
-
};
|
|
831
|
-
return directory;
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
function normalizeChatRequestId(input = {}, relations = {}) {
|
|
835
|
-
return firstText(
|
|
836
|
-
relations.chatRequestId,
|
|
837
|
-
relations.requestId,
|
|
838
|
-
input.chatRequestId,
|
|
839
|
-
input.requestId,
|
|
840
|
-
input.refs?.chatRequestId,
|
|
841
|
-
input.refs?.requestId,
|
|
842
|
-
);
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
function compactDirectoryObject(value = {}) {
|
|
846
|
-
return cleanJournalObject(value);
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
function compactSessionArtifact(artifact = {}) {
|
|
850
|
-
const sessionFile = firstText(artifact.sessionFile, artifact.transcriptPath);
|
|
851
|
-
const transcriptPath = firstText(
|
|
852
|
-
artifact.transcriptPath && artifact.transcriptPath !== sessionFile ? artifact.transcriptPath : null,
|
|
853
|
-
);
|
|
854
|
-
const deliveryId = firstText(artifact.deliveryId);
|
|
855
|
-
return compactDirectoryObject({
|
|
856
|
-
sessionId: firstText(artifact.sessionId),
|
|
857
|
-
sessionFile,
|
|
858
|
-
transcriptPath,
|
|
859
|
-
deliveryId,
|
|
860
|
-
sourceEventId: deliveryId ? null : firstText(artifact.sourceEventId, artifact.eventId),
|
|
861
|
-
seenAt: firstText(artifact.seenAt, artifact.lastSeenAt, artifact.firstSeenAt),
|
|
862
|
-
});
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
function normalizeSessionArtifacts(artifacts = []) {
|
|
866
|
-
if (!Array.isArray(artifacts)) return [];
|
|
867
|
-
return artifacts.reduce((nextArtifacts, artifact) => (
|
|
868
|
-
upsertSessionArtifact(nextArtifacts, artifact)
|
|
869
|
-
), []);
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
function upsertSessionArtifact(artifacts = [], artifact = {}) {
|
|
873
|
-
const normalizedArtifact = compactSessionArtifact(artifact);
|
|
874
|
-
if (
|
|
875
|
-
!normalizedArtifact.sessionId
|
|
876
|
-
&& !normalizedArtifact.sessionFile
|
|
877
|
-
&& !normalizedArtifact.transcriptPath
|
|
878
|
-
) {
|
|
879
|
-
return artifacts;
|
|
880
|
-
}
|
|
881
|
-
const key = [
|
|
882
|
-
normalizedArtifact.sessionId || '',
|
|
883
|
-
normalizedArtifact.sessionFile || '',
|
|
884
|
-
normalizedArtifact.transcriptPath || '',
|
|
885
|
-
].join('\u0000');
|
|
886
|
-
const nextArtifacts = Array.isArray(artifacts) ? [...artifacts] : [];
|
|
887
|
-
const index = nextArtifacts.findIndex((entry) => [
|
|
888
|
-
entry?.sessionId || '',
|
|
889
|
-
entry?.sessionFile || '',
|
|
890
|
-
entry?.transcriptPath || '',
|
|
891
|
-
].join('\u0000') === key);
|
|
892
|
-
if (index >= 0) {
|
|
893
|
-
nextArtifacts[index] = compactDirectoryObject({
|
|
894
|
-
...nextArtifacts[index],
|
|
895
|
-
...normalizedArtifact,
|
|
896
|
-
seenAt: normalizedArtifact.seenAt || nextArtifacts[index].seenAt,
|
|
897
|
-
});
|
|
898
|
-
return nextArtifacts;
|
|
899
|
-
}
|
|
900
|
-
nextArtifacts.push(normalizedArtifact);
|
|
901
|
-
return nextArtifacts;
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
function buildLatestSessionHint(source = {}, timestamp = null) {
|
|
905
|
-
const artifact = compactSessionArtifact({
|
|
906
|
-
sessionId: source.sessionId || source.latestSessionId,
|
|
907
|
-
sessionFile: source.sessionFile || source.latestSessionFile,
|
|
908
|
-
transcriptPath: source.transcriptPath || source.latestTranscriptPath,
|
|
909
|
-
seenAt: timestamp || source.seenAt || source.lastSeenAt || source.firstSeenAt,
|
|
910
|
-
});
|
|
911
|
-
if (!artifact.sessionId && !artifact.sessionFile && !artifact.transcriptPath) return null;
|
|
912
|
-
const latest = { ...artifact };
|
|
913
|
-
delete latest.deliveryId;
|
|
914
|
-
delete latest.sourceEventId;
|
|
915
|
-
return compactDirectoryObject(latest);
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
function normalizeChatRequestDirectoryEntry(entry = {}) {
|
|
919
|
-
const current = isPlainObject(entry) ? entry : {};
|
|
920
|
-
const artifacts = normalizeSessionArtifacts(
|
|
921
|
-
Array.isArray(current.artifacts) ? current.artifacts : current.sessionArtifacts,
|
|
922
|
-
);
|
|
923
|
-
return compactDirectoryObject({
|
|
924
|
-
firstSeenAt: current.firstSeenAt,
|
|
925
|
-
lastSeenAt: current.lastSeenAt,
|
|
926
|
-
artifacts,
|
|
927
|
-
});
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
function normalizeChatRequestsDirectory(chatRequests = {}) {
|
|
931
|
-
if (!isPlainObject(chatRequests)) return {};
|
|
932
|
-
const normalized = {};
|
|
933
|
-
for (const [chatRequestId, entry] of Object.entries(chatRequests)) {
|
|
934
|
-
const normalizedEntry = normalizeChatRequestDirectoryEntry(entry);
|
|
935
|
-
if (Object.keys(normalizedEntry).length > 0) {
|
|
936
|
-
normalized[chatRequestId] = normalizedEntry;
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
return normalized;
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
function buildSessionArtifact({ relations = {}, timestamp = null } = {}) {
|
|
943
|
-
return compactSessionArtifact({
|
|
944
|
-
sessionId: relations.sessionId,
|
|
945
|
-
sessionFile: relations.sessionFile,
|
|
946
|
-
transcriptPath: relations.transcriptPath,
|
|
947
|
-
deliveryId: relations.deliveryId,
|
|
948
|
-
eventId: relations.eventId,
|
|
949
|
-
seenAt: timestamp,
|
|
950
|
-
});
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
function applyClaworldSessionDirectoryUpdate(directory = {}, input = {}) {
|
|
954
|
-
const refs = isPlainObject(input.refs) ? input.refs : {};
|
|
955
|
-
const relations = resolveJournalRelations(input, refs);
|
|
956
|
-
const kind = resolveClaworldSessionDirectoryKind(input, relations);
|
|
957
|
-
const timestamp = toIsoTimestamp(input.timestamp || Date.now());
|
|
958
|
-
const localSessionKey = firstText(relations.localSessionKey, relations.sessionKey, input.localSessionKey, input.sessionKey);
|
|
959
|
-
const relaySessionKey = firstText(relations.relaySessionKey, input.relaySessionKey);
|
|
960
|
-
if (!kind || !localSessionKey) {
|
|
961
|
-
return { updated: false, reason: 'missing_session_reference', directory };
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
const nextDirectory = normalizeClaworldSessionDirectory(directory);
|
|
965
|
-
nextDirectory.updatedAt = timestamp;
|
|
966
|
-
|
|
967
|
-
if (kind === 'main') {
|
|
968
|
-
nextDirectory.main = compactDirectoryObject({
|
|
969
|
-
...nextDirectory.main,
|
|
970
|
-
lastActiveSessionKey: localSessionKey,
|
|
971
|
-
lastUpdatedAt: timestamp,
|
|
972
|
-
localAgentId: relations.localAgentId,
|
|
973
|
-
source: input.source,
|
|
974
|
-
});
|
|
975
|
-
return { updated: true, kind, directory: nextDirectory };
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
if (kind === 'management') {
|
|
979
|
-
nextDirectory.management = compactDirectoryObject({
|
|
980
|
-
...nextDirectory.management,
|
|
981
|
-
lastActiveLocalSessionKey: localSessionKey,
|
|
982
|
-
relaySessionKey,
|
|
983
|
-
localAgentId: relations.localAgentId,
|
|
984
|
-
targetAgentId: relations.targetAgentId,
|
|
985
|
-
lastUpdatedAt: timestamp,
|
|
986
|
-
});
|
|
987
|
-
return { updated: true, kind, directory: nextDirectory };
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
const conversationSessions = isPlainObject(nextDirectory.conversationSessions)
|
|
991
|
-
? { ...nextDirectory.conversationSessions }
|
|
992
|
-
: {};
|
|
993
|
-
const currentSession = isPlainObject(conversationSessions[localSessionKey])
|
|
994
|
-
? conversationSessions[localSessionKey]
|
|
995
|
-
: {};
|
|
996
|
-
const currentLatest = isPlainObject(currentSession.latest)
|
|
997
|
-
? buildLatestSessionHint(currentSession.latest)
|
|
998
|
-
: buildLatestSessionHint(currentSession);
|
|
999
|
-
const nextLatest = buildLatestSessionHint(relations, timestamp) || currentLatest;
|
|
1000
|
-
let nextSession = compactDirectoryObject({
|
|
1001
|
-
relaySessionKey: firstText(relaySessionKey, currentSession.relaySessionKey),
|
|
1002
|
-
conversationKey: firstText(currentSession.conversationKey, relations.conversationKey),
|
|
1003
|
-
worldId: firstText(currentSession.worldId, relations.worldId),
|
|
1004
|
-
localAgentId: firstText(currentSession.localAgentId, relations.localAgentId),
|
|
1005
|
-
firstSeenAt: currentSession.firstSeenAt || timestamp,
|
|
1006
|
-
lastSeenAt: timestamp,
|
|
1007
|
-
latest: nextLatest,
|
|
1008
|
-
chatRequests: normalizeChatRequestsDirectory(currentSession.chatRequests),
|
|
1009
|
-
});
|
|
1010
|
-
|
|
1011
|
-
const chatRequestId = normalizeChatRequestId(input, relations);
|
|
1012
|
-
if (chatRequestId) {
|
|
1013
|
-
const chatRequests = isPlainObject(nextSession.chatRequests)
|
|
1014
|
-
? { ...nextSession.chatRequests }
|
|
1015
|
-
: {};
|
|
1016
|
-
const currentRequest = isPlainObject(chatRequests[chatRequestId])
|
|
1017
|
-
? chatRequests[chatRequestId]
|
|
1018
|
-
: {};
|
|
1019
|
-
const artifact = buildSessionArtifact({ relations, timestamp });
|
|
1020
|
-
const currentArtifacts = Array.isArray(currentRequest.artifacts)
|
|
1021
|
-
? currentRequest.artifacts
|
|
1022
|
-
: currentRequest.sessionArtifacts;
|
|
1023
|
-
const nextRequest = compactDirectoryObject({
|
|
1024
|
-
firstSeenAt: currentRequest.firstSeenAt || timestamp,
|
|
1025
|
-
lastSeenAt: timestamp,
|
|
1026
|
-
artifacts: upsertSessionArtifact(currentArtifacts, artifact),
|
|
1027
|
-
});
|
|
1028
|
-
chatRequests[chatRequestId] = nextRequest;
|
|
1029
|
-
nextSession = compactDirectoryObject({
|
|
1030
|
-
...nextSession,
|
|
1031
|
-
chatRequests,
|
|
1032
|
-
});
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
conversationSessions[localSessionKey] = nextSession;
|
|
1036
|
-
nextDirectory.conversationSessions = conversationSessions;
|
|
1037
|
-
return { updated: true, kind, directory: nextDirectory };
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
export async function readClaworldSessionDirectory(options = {}, readOptions = {}) {
|
|
1041
|
-
const workspaceRoot = resolveClaworldWorkspaceRoot(options, readOptions.homeDir || os.homedir());
|
|
1042
|
-
const sessionDirectoryPath = resolveClaworldSessionDirectoryPath(workspaceRoot);
|
|
1043
|
-
const current = await readJsonIfPresent(sessionDirectoryPath);
|
|
1044
|
-
return {
|
|
1045
|
-
workspaceRoot,
|
|
1046
|
-
sessionDirectoryPath,
|
|
1047
|
-
directory: normalizeClaworldSessionDirectory(current || createEmptyClaworldSessionDirectory()),
|
|
1048
|
-
exists: current != null,
|
|
1049
|
-
};
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
export async function updateClaworldSessionDirectory(options = {}, input = {}, updateOptions = {}) {
|
|
1053
|
-
const workspaceRoot = resolveClaworldWorkspaceRoot(options, updateOptions.homeDir || os.homedir());
|
|
1054
|
-
await ensureClaworldWorkingMemory(workspaceRoot, updateOptions);
|
|
1055
|
-
const sessionDirectoryPath = resolveClaworldSessionDirectoryPath(workspaceRoot);
|
|
1056
|
-
const current = await readJsonIfPresent(sessionDirectoryPath);
|
|
1057
|
-
const baseDirectory = current || createEmptyClaworldSessionDirectory(input.timestamp || updateOptions.timestamp);
|
|
1058
|
-
const result = applyClaworldSessionDirectoryUpdate(baseDirectory, input);
|
|
1059
|
-
if (!result.updated) {
|
|
1060
|
-
return {
|
|
1061
|
-
ok: true,
|
|
1062
|
-
updated: false,
|
|
1063
|
-
reason: result.reason,
|
|
1064
|
-
workspaceRoot,
|
|
1065
|
-
sessionDirectoryPath,
|
|
1066
|
-
directory: normalizeClaworldSessionDirectory(baseDirectory),
|
|
1067
|
-
};
|
|
1068
|
-
}
|
|
1069
|
-
await atomicWriteText(
|
|
1070
|
-
sessionDirectoryPath,
|
|
1071
|
-
`${JSON.stringify(result.directory, null, 2)}\n`,
|
|
1072
|
-
{
|
|
1073
|
-
backup: false,
|
|
1074
|
-
rejectEmptyOverwrite: false,
|
|
1075
|
-
},
|
|
1076
|
-
);
|
|
1077
|
-
return {
|
|
1078
|
-
ok: true,
|
|
1079
|
-
updated: true,
|
|
1080
|
-
kind: result.kind,
|
|
1081
|
-
workspaceRoot,
|
|
1082
|
-
sessionDirectoryPath,
|
|
1083
|
-
directory: result.directory,
|
|
1084
|
-
};
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
543
|
function flattenInline(value) {
|
|
1088
544
|
return truncateText(String(value ?? '').replace(/\s+/g, ' ').trim());
|
|
1089
545
|
}
|
|
1090
546
|
|
|
1091
|
-
function firstText(...values) {
|
|
1092
|
-
for (const value of values) {
|
|
1093
|
-
const normalized = normalizeText(value, null);
|
|
1094
|
-
if (normalized) return normalized;
|
|
1095
|
-
}
|
|
1096
|
-
return null;
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
function hasStructuredValue(value) {
|
|
1100
|
-
if (value == null) return false;
|
|
1101
|
-
if (typeof value === 'string') return normalizeText(value, null) != null;
|
|
1102
|
-
if (Array.isArray(value)) return value.length > 0;
|
|
1103
|
-
if (isPlainObject(value)) return Object.keys(value).length > 0;
|
|
1104
|
-
return true;
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
function cleanJournalValue(value) {
|
|
1108
|
-
if (value == null) return null;
|
|
1109
|
-
if (value instanceof Date) return toIsoTimestamp(value);
|
|
1110
|
-
if (typeof value === 'string') return normalizeText(value, null);
|
|
1111
|
-
if (typeof value === 'number' || typeof value === 'boolean') return value;
|
|
1112
|
-
if (Array.isArray(value)) {
|
|
1113
|
-
const cleanedArray = value
|
|
1114
|
-
.map((entry) => cleanJournalValue(entry))
|
|
1115
|
-
.filter(hasStructuredValue);
|
|
1116
|
-
return cleanedArray.length > 0 ? cleanedArray : null;
|
|
1117
|
-
}
|
|
1118
|
-
if (isPlainObject(value)) {
|
|
1119
|
-
const cleanedObject = {};
|
|
1120
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
1121
|
-
const cleaned = cleanJournalValue(entry);
|
|
1122
|
-
if (hasStructuredValue(cleaned)) {
|
|
1123
|
-
cleanedObject[key] = cleaned;
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
return Object.keys(cleanedObject).length > 0 ? cleanedObject : null;
|
|
1127
|
-
}
|
|
1128
|
-
return normalizeText(value, null);
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
function cleanJournalObject(value = {}) {
|
|
1132
|
-
return cleanJournalValue(value) || {};
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
function resolveJournalScope(input = {}, relations = {}) {
|
|
1136
|
-
const directScope = normalizeText(input.scope, null);
|
|
1137
|
-
if (directScope) return directScope;
|
|
1138
|
-
const context = isPlainObject(input.context) ? input.context : {};
|
|
1139
|
-
const normalizedTarget = resolveClaworldBootstrapTarget({
|
|
1140
|
-
sessionKey: firstText(
|
|
1141
|
-
input.localSessionKey,
|
|
1142
|
-
input.sessionKey,
|
|
1143
|
-
relations.localSessionKey,
|
|
1144
|
-
relations.sessionKey,
|
|
1145
|
-
context.SessionKey,
|
|
1146
|
-
context.sessionKey,
|
|
1147
|
-
),
|
|
1148
|
-
sessionType: firstText(
|
|
1149
|
-
input.sessionType,
|
|
1150
|
-
input.sessionKind,
|
|
1151
|
-
context.SessionType,
|
|
1152
|
-
context.ChatType,
|
|
1153
|
-
context.sessionType,
|
|
1154
|
-
context.sessionKind,
|
|
1155
|
-
),
|
|
1156
|
-
channel: firstText(context.OriginatingChannel, context.channel),
|
|
1157
|
-
});
|
|
1158
|
-
if (normalizedTarget === CLAWORLD_BOOTSTRAP_TARGETS.CLAWORLD_CONVERSATION) return 'conversation';
|
|
1159
|
-
if (normalizedTarget === CLAWORLD_BOOTSTRAP_TARGETS.MANAGEMENT) return 'management';
|
|
1160
|
-
if (normalizedTarget === CLAWORLD_BOOTSTRAP_TARGETS.MAIN) return 'main';
|
|
1161
|
-
return firstText(input.sessionKind, input.sessionType, context.sessionKind, context.SessionType, 'runtime');
|
|
1162
|
-
}
|
|
1163
|
-
|
|
1164
|
-
function resolveJournalRelations(input = {}, refs = {}) {
|
|
1165
|
-
const relations = isPlainObject(input.relations)
|
|
1166
|
-
? input.relations
|
|
1167
|
-
: isPlainObject(input.correlation)
|
|
1168
|
-
? input.correlation
|
|
1169
|
-
: {};
|
|
1170
|
-
const context = isPlainObject(input.context) ? input.context : {};
|
|
1171
|
-
const artifacts = isPlainObject(input.artifacts) ? input.artifacts : {};
|
|
1172
|
-
const requestId = firstText(
|
|
1173
|
-
relations.requestId,
|
|
1174
|
-
relations.chatRequestId,
|
|
1175
|
-
input.requestId,
|
|
1176
|
-
input.chatRequestId,
|
|
1177
|
-
refs.requestId,
|
|
1178
|
-
refs.chatRequestId,
|
|
1179
|
-
refs.friendRequestId,
|
|
1180
|
-
);
|
|
1181
|
-
return cleanJournalObject({
|
|
1182
|
-
requestId,
|
|
1183
|
-
chatRequestId: firstText(relations.chatRequestId, input.chatRequestId, refs.chatRequestId, requestId),
|
|
1184
|
-
deliveryId: firstText(relations.deliveryId, input.deliveryId, refs.deliveryId, context.RelayDeliveryId),
|
|
1185
|
-
eventId: firstText(relations.eventId, input.eventId, input.id, refs.eventId, context.RelayEventId),
|
|
1186
|
-
notificationId: firstText(relations.notificationId, refs.notificationId),
|
|
1187
|
-
inboxItemId: firstText(relations.inboxItemId, refs.inboxItemId),
|
|
1188
|
-
conversationKey: firstText(relations.conversationKey, input.conversationKey, refs.conversationKey),
|
|
1189
|
-
worldId: firstText(relations.worldId, input.worldId, refs.worldId),
|
|
1190
|
-
accountId: firstText(relations.accountId, input.accountId, refs.accountId, context.AccountId),
|
|
1191
|
-
agentCode: firstText(relations.agentCode, refs.agentCode),
|
|
1192
|
-
localAgentId: firstText(relations.localAgentId, input.localAgentId, context.AgentId, context.agentId),
|
|
1193
|
-
targetAgentId: firstText(relations.targetAgentId, input.targetAgentId, refs.targetAgentId, context.RelayTargetAgentId),
|
|
1194
|
-
fromAgentId: firstText(relations.fromAgentId, input.fromAgentId, refs.fromAgentId, context.RelayFromAgentId),
|
|
1195
|
-
sessionKey: firstText(relations.sessionKey, input.sessionKey, context.SessionKey, context.sessionKey),
|
|
1196
|
-
localSessionKey: firstText(relations.localSessionKey, input.localSessionKey, context.LocalSessionKey, context.SessionKey),
|
|
1197
|
-
relaySessionKey: firstText(relations.relaySessionKey, input.relaySessionKey, context.RelaySessionKey),
|
|
1198
|
-
sessionId: firstText(relations.sessionId, input.sessionId, artifacts.sessionId, context.SessionId),
|
|
1199
|
-
sessionFile: firstText(relations.sessionFile, input.sessionFile, artifacts.sessionFile, artifacts.sessionPath, context.SessionFile),
|
|
1200
|
-
sessionStorePath: firstText(relations.sessionStorePath, input.sessionStorePath, artifacts.sessionStorePath),
|
|
1201
|
-
transcriptPath: firstText(relations.transcriptPath, input.transcriptPath, artifacts.transcriptPath),
|
|
1202
|
-
reportPath: firstText(relations.reportPath, input.reportPath, artifacts.reportPath),
|
|
1203
|
-
});
|
|
1204
|
-
}
|
|
1205
|
-
|
|
1206
|
-
function buildJournalKey(relations = {}, fallbackEventId = null) {
|
|
1207
|
-
const event = firstText(relations.eventId, fallbackEventId);
|
|
1208
|
-
const request = firstText(relations.requestId, relations.chatRequestId);
|
|
1209
|
-
const session = firstText(relations.localSessionKey, relations.relaySessionKey, relations.sessionKey);
|
|
1210
|
-
const continuity = request
|
|
1211
|
-
? `request:${request}`
|
|
1212
|
-
: firstText(
|
|
1213
|
-
relations.conversationKey ? `conversation:${relations.conversationKey}` : null,
|
|
1214
|
-
session ? `session:${session}` : null,
|
|
1215
|
-
relations.worldId ? `world:${relations.worldId}` : null,
|
|
1216
|
-
event ? `event:${event}` : null,
|
|
1217
|
-
);
|
|
1218
|
-
return cleanJournalObject({
|
|
1219
|
-
event,
|
|
1220
|
-
continuity,
|
|
1221
|
-
request,
|
|
1222
|
-
session,
|
|
1223
|
-
sessionId: relations.sessionId,
|
|
1224
|
-
world: relations.worldId,
|
|
1225
|
-
conversation: relations.conversationKey,
|
|
1226
|
-
});
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
547
|
export function buildClaworldMaintenanceEvent(input = {}) {
|
|
1230
548
|
const toolName = normalizeText(input.toolName, null);
|
|
1231
549
|
const source = normalizeText(input.source, toolName ? 'claworld_tool' : 'claworld_runtime');
|
|
@@ -1239,26 +557,14 @@ export function buildClaworldMaintenanceEvent(input = {}) {
|
|
|
1239
557
|
.filter(([, value]) => value != null),
|
|
1240
558
|
)
|
|
1241
559
|
: {};
|
|
1242
|
-
const id = normalizeText(input.id, `${source}:${kind}:${timestamp}`);
|
|
1243
|
-
const relations = resolveJournalRelations({ ...input, eventId: input.eventId || id }, refs);
|
|
1244
|
-
const scope = resolveJournalScope(input, relations);
|
|
1245
560
|
return {
|
|
1246
|
-
|
|
1247
|
-
id,
|
|
561
|
+
id: normalizeText(input.id, `${source}:${kind}:${timestamp}`),
|
|
1248
562
|
timestamp,
|
|
1249
563
|
source,
|
|
1250
564
|
kind,
|
|
1251
|
-
eventType: normalizeText(input.eventType, kind),
|
|
1252
|
-
scope,
|
|
1253
565
|
summary,
|
|
1254
566
|
excerpt: truncateText(input.excerpt || ''),
|
|
1255
567
|
refs,
|
|
1256
|
-
key: buildJournalKey(relations, id),
|
|
1257
|
-
relations,
|
|
1258
|
-
actor: cleanJournalObject(input.actor || {}),
|
|
1259
|
-
tool: cleanJournalObject(input.tool || {}),
|
|
1260
|
-
artifacts: cleanJournalObject(input.artifacts || {}),
|
|
1261
|
-
maintenance: cleanJournalObject(input.maintenance || {}),
|
|
1262
568
|
};
|
|
1263
569
|
}
|
|
1264
570
|
|
|
@@ -1291,13 +597,9 @@ function compactResultPayload(payload = {}) {
|
|
|
1291
597
|
const keys = [
|
|
1292
598
|
'status',
|
|
1293
599
|
'tool',
|
|
1294
|
-
'action',
|
|
1295
|
-
'scope',
|
|
1296
|
-
'query',
|
|
1297
600
|
'accountId',
|
|
1298
601
|
'worldId',
|
|
1299
602
|
'displayName',
|
|
1300
|
-
'requestId',
|
|
1301
603
|
'chatRequestId',
|
|
1302
604
|
'conversationKey',
|
|
1303
605
|
'feedbackId',
|
|
@@ -1314,288 +616,75 @@ function compactResultPayload(payload = {}) {
|
|
|
1314
616
|
return compact;
|
|
1315
617
|
}
|
|
1316
618
|
|
|
1317
|
-
function readNestedObject(value, key) {
|
|
1318
|
-
const nested = value?.[key];
|
|
1319
|
-
return nested && typeof nested === 'object' && !Array.isArray(nested) ? nested : {};
|
|
1320
|
-
}
|
|
1321
|
-
|
|
1322
|
-
function firstObjectFromArray(value) {
|
|
1323
|
-
if (!Array.isArray(value)) return {};
|
|
1324
|
-
return value.find((entry) => isPlainObject(entry)) || {};
|
|
1325
|
-
}
|
|
1326
|
-
|
|
1327
|
-
function nestedConversationWorldId(value = {}) {
|
|
1328
|
-
return firstText(value.worldId, value.conversation?.worldId);
|
|
1329
|
-
}
|
|
1330
|
-
|
|
1331
|
-
function resolvePayloadItemCounts(payload = {}) {
|
|
1332
|
-
const counts = [];
|
|
1333
|
-
for (const [key, label] of [
|
|
1334
|
-
['worlds', 'worlds'],
|
|
1335
|
-
['members', 'members'],
|
|
1336
|
-
['people', 'people'],
|
|
1337
|
-
['results', 'results'],
|
|
1338
|
-
['items', 'items'],
|
|
1339
|
-
['pendingRequests', 'pendingRequests'],
|
|
1340
|
-
['recentRequests', 'recentRequests'],
|
|
1341
|
-
['chats', 'chats'],
|
|
1342
|
-
]) {
|
|
1343
|
-
if (Array.isArray(payload[key])) {
|
|
1344
|
-
counts.push(`${label}=${payload[key].length}`);
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
return counts;
|
|
1348
|
-
}
|
|
1349
|
-
|
|
1350
|
-
function formatToolSummaryPart(key, value) {
|
|
1351
|
-
const normalized = flattenInline(value);
|
|
1352
|
-
return normalized ? `${key}=${normalized}` : null;
|
|
1353
|
-
}
|
|
1354
|
-
|
|
1355
|
-
function buildToolCallSummary({ toolName, params = {}, payload = {}, compactPayload = {} } = {}) {
|
|
1356
|
-
const parts = [
|
|
1357
|
-
formatToolSummaryPart('action', firstText(params.action, payload.action)),
|
|
1358
|
-
formatToolSummaryPart('scope', firstText(params.scope, payload.scope)),
|
|
1359
|
-
formatToolSummaryPart('query', firstText(params.query, payload.query)),
|
|
1360
|
-
formatToolSummaryPart('status', firstText(payload.status, compactPayload.status)),
|
|
1361
|
-
formatToolSummaryPart('worldId', firstText(params.worldId, payload.worldId)),
|
|
1362
|
-
formatToolSummaryPart('chatRequestId', firstText(params.chatRequestId, payload.chatRequestId)),
|
|
1363
|
-
formatToolSummaryPart('conversationKey', firstText(params.conversationKey, payload.conversationKey)),
|
|
1364
|
-
...resolvePayloadItemCounts(payload),
|
|
1365
|
-
].filter(Boolean);
|
|
1366
|
-
return `${toolName} completed${parts.length > 0 ? ` (${parts.join(', ')})` : ''}.`;
|
|
1367
|
-
}
|
|
1368
|
-
|
|
1369
619
|
export function buildClaworldToolMaintenanceEvent({
|
|
1370
620
|
toolName,
|
|
1371
621
|
params = {},
|
|
1372
622
|
result = null,
|
|
1373
623
|
timestamp = null,
|
|
1374
|
-
context = {},
|
|
1375
624
|
} = {}) {
|
|
1376
625
|
const normalizedToolName = normalizeText(toolName, null);
|
|
1377
626
|
if (!normalizedToolName || !normalizedToolName.startsWith('claworld_')) return null;
|
|
1378
627
|
const payload = parseToolResultPayload(result) || {};
|
|
1379
|
-
const chatRequest = readNestedObject(payload, 'chatRequest');
|
|
1380
|
-
const request = readNestedObject(payload, 'request');
|
|
1381
|
-
const kickoff = readNestedObject(payload, 'kickoff');
|
|
1382
|
-
const chat = readNestedObject(payload, 'chat');
|
|
1383
|
-
const firstChat = firstObjectFromArray(payload.chats);
|
|
1384
|
-
const conversation = readNestedObject(payload, 'conversation');
|
|
1385
628
|
const compactPayload = compactResultPayload(payload);
|
|
1386
|
-
const requestId = firstText(
|
|
1387
|
-
params.requestId,
|
|
1388
|
-
params.chatRequestId,
|
|
1389
|
-
payload.requestId,
|
|
1390
|
-
payload.chatRequestId,
|
|
1391
|
-
chatRequest.chatRequestId,
|
|
1392
|
-
chatRequest.requestId,
|
|
1393
|
-
request.requestId,
|
|
1394
|
-
chat.chatRequestId,
|
|
1395
|
-
firstChat.chatRequestId,
|
|
1396
|
-
);
|
|
1397
|
-
const requesterSessionKey = firstText(
|
|
1398
|
-
context.sessionKey,
|
|
1399
|
-
context.SessionKey,
|
|
1400
|
-
context.localSessionKey,
|
|
1401
|
-
context.LocalSessionKey,
|
|
1402
|
-
);
|
|
1403
|
-
const resultConversationSessionKey = firstText(
|
|
1404
|
-
chat.localSessionKey,
|
|
1405
|
-
firstChat.localSessionKey,
|
|
1406
|
-
payload.localSessionKey,
|
|
1407
|
-
kickoff.localSessionKey,
|
|
1408
|
-
conversation.localSessionKey,
|
|
1409
|
-
);
|
|
1410
|
-
const conversationToolNames = new Set([
|
|
1411
|
-
'claworld_manage_conversations',
|
|
1412
|
-
'claworld_chat_inbox',
|
|
1413
|
-
'claworld_request_chat',
|
|
1414
|
-
]);
|
|
1415
|
-
const localSessionKey = firstText(
|
|
1416
|
-
conversationToolNames.has(normalizedToolName) ? resultConversationSessionKey : null,
|
|
1417
|
-
requesterSessionKey,
|
|
1418
|
-
resultConversationSessionKey,
|
|
1419
|
-
);
|
|
1420
|
-
const relaySessionKey = firstText(
|
|
1421
|
-
context.RelaySessionKey,
|
|
1422
|
-
payload.sessionKey,
|
|
1423
|
-
chat.sessionKey,
|
|
1424
|
-
firstChat.sessionKey,
|
|
1425
|
-
kickoff.sessionKey,
|
|
1426
|
-
conversation.sessionKey,
|
|
1427
|
-
);
|
|
1428
|
-
const targetDiffersFromRequester = requesterSessionKey && localSessionKey && requesterSessionKey !== localSessionKey;
|
|
1429
|
-
const sessionKind = resolveJournalScope({
|
|
1430
|
-
sessionKey: localSessionKey || relaySessionKey,
|
|
1431
|
-
sessionType: targetDiffersFromRequester
|
|
1432
|
-
? null
|
|
1433
|
-
: firstText(context.sessionType, context.SessionType, context.ChatType, context.sessionKind),
|
|
1434
|
-
context: targetDiffersFromRequester ? {} : context,
|
|
1435
|
-
});
|
|
1436
|
-
const actorSessionKind = resolveJournalScope({
|
|
1437
|
-
sessionKey: requesterSessionKey || localSessionKey || relaySessionKey,
|
|
1438
|
-
sessionType: firstText(context.sessionType, context.SessionType, context.ChatType, context.sessionKind),
|
|
1439
|
-
context,
|
|
1440
|
-
});
|
|
1441
629
|
const refs = {
|
|
1442
630
|
accountId: params.accountId || payload.accountId,
|
|
1443
|
-
worldId:
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
nestedConversationWorldId(chat),
|
|
1447
|
-
nestedConversationWorldId(firstChat),
|
|
1448
|
-
nestedConversationWorldId(chatRequest),
|
|
1449
|
-
nestedConversationWorldId(request),
|
|
1450
|
-
conversation.worldId,
|
|
1451
|
-
),
|
|
1452
|
-
requestId,
|
|
1453
|
-
chatRequestId: firstText(
|
|
1454
|
-
params.chatRequestId,
|
|
1455
|
-
payload.chatRequestId,
|
|
1456
|
-
chatRequest.chatRequestId,
|
|
1457
|
-
request.chatRequestId,
|
|
1458
|
-
chat.chatRequestId,
|
|
1459
|
-
firstChat.chatRequestId,
|
|
1460
|
-
requestId,
|
|
1461
|
-
),
|
|
1462
|
-
conversationKey: firstText(
|
|
1463
|
-
params.conversationKey,
|
|
1464
|
-
payload.conversationKey,
|
|
1465
|
-
conversation.conversationKey,
|
|
1466
|
-
kickoff.conversationKey,
|
|
1467
|
-
chat.conversationKey,
|
|
1468
|
-
firstChat.conversationKey,
|
|
1469
|
-
),
|
|
631
|
+
worldId: params.worldId || payload.worldId,
|
|
632
|
+
chatRequestId: params.chatRequestId || payload.chatRequestId,
|
|
633
|
+
conversationKey: params.conversationKey || payload.conversationKey,
|
|
1470
634
|
agentCode: params.agentCode || payload.agentCode,
|
|
1471
|
-
sessionKey: localSessionKey || relaySessionKey,
|
|
1472
|
-
relaySessionKey,
|
|
1473
635
|
};
|
|
1474
636
|
return buildClaworldMaintenanceEvent({
|
|
1475
637
|
source: 'claworld_tool',
|
|
1476
638
|
kind: normalizedToolName,
|
|
1477
|
-
eventType: 'tool_call',
|
|
1478
|
-
scope: sessionKind,
|
|
1479
639
|
toolName: normalizedToolName,
|
|
1480
640
|
timestamp,
|
|
1481
641
|
refs,
|
|
1482
|
-
|
|
1483
|
-
requestId,
|
|
1484
|
-
chatRequestId: refs.chatRequestId,
|
|
1485
|
-
conversationKey: refs.conversationKey,
|
|
1486
|
-
worldId: refs.worldId,
|
|
1487
|
-
accountId: refs.accountId,
|
|
1488
|
-
agentCode: refs.agentCode,
|
|
1489
|
-
localSessionKey,
|
|
1490
|
-
relaySessionKey,
|
|
1491
|
-
sessionKey: localSessionKey || relaySessionKey,
|
|
1492
|
-
localAgentId: firstText(context.agentId, context.AgentId),
|
|
1493
|
-
sessionId: targetDiffersFromRequester ? null : firstText(context.sessionId, context.SessionId),
|
|
1494
|
-
sessionFile: targetDiffersFromRequester ? null : firstText(context.sessionFile, context.SessionFile, context.sessionPath),
|
|
1495
|
-
transcriptPath: targetDiffersFromRequester ? null : firstText(context.transcriptPath),
|
|
1496
|
-
},
|
|
1497
|
-
actor: {
|
|
1498
|
-
sessionKind: actorSessionKind,
|
|
1499
|
-
agentId: firstText(context.agentId, context.AgentId),
|
|
1500
|
-
sessionKey: requesterSessionKey || localSessionKey || relaySessionKey,
|
|
1501
|
-
},
|
|
1502
|
-
tool: {
|
|
1503
|
-
name: normalizedToolName,
|
|
1504
|
-
action: firstText(params.action, payload.action),
|
|
1505
|
-
status: firstText(payload.status, 'succeeded'),
|
|
1506
|
-
requesterSessionKey,
|
|
1507
|
-
targetSessionKey: targetDiffersFromRequester ? localSessionKey : null,
|
|
1508
|
-
},
|
|
1509
|
-
summary: buildToolCallSummary({
|
|
1510
|
-
toolName: normalizedToolName,
|
|
1511
|
-
params,
|
|
1512
|
-
payload,
|
|
1513
|
-
compactPayload,
|
|
1514
|
-
}),
|
|
642
|
+
summary: `${normalizedToolName} succeeded.`,
|
|
1515
643
|
excerpt: Object.keys(compactPayload).length > 0
|
|
1516
644
|
? JSON.stringify(compactPayload)
|
|
1517
645
|
: null,
|
|
1518
646
|
});
|
|
1519
647
|
}
|
|
1520
648
|
|
|
1521
|
-
function
|
|
1522
|
-
return
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
timestamp: event.timestamp,
|
|
1527
|
-
source: event.source,
|
|
1528
|
-
scope: event.scope,
|
|
1529
|
-
eventType: event.eventType,
|
|
1530
|
-
kind: event.kind,
|
|
1531
|
-
summary: event.summary,
|
|
1532
|
-
refs: event.refs,
|
|
1533
|
-
relations: event.relations,
|
|
1534
|
-
actor: event.actor,
|
|
1535
|
-
tool: event.tool,
|
|
1536
|
-
artifacts: event.artifacts,
|
|
1537
|
-
maintenance: event.maintenance,
|
|
1538
|
-
excerpt: event.excerpt || null,
|
|
1539
|
-
});
|
|
1540
|
-
}
|
|
1541
|
-
|
|
1542
|
-
function buildJournalEntry(event = {}) {
|
|
1543
|
-
const title = flattenInline(event.summary || event.kind || 'Claworld event recorded.');
|
|
1544
|
-
const heading = [
|
|
1545
|
-
event.timestamp,
|
|
1546
|
-
event.scope || event.source,
|
|
1547
|
-
event.kind,
|
|
1548
|
-
title,
|
|
1549
|
-
].filter(Boolean).join(' · ');
|
|
1550
|
-
return [
|
|
1551
|
-
`## ${heading}`,
|
|
1552
|
-
'',
|
|
1553
|
-
'```json',
|
|
1554
|
-
JSON.stringify(buildJournalEntryPayload(event), null, 2),
|
|
1555
|
-
'```',
|
|
1556
|
-
'',
|
|
1557
|
-
].join('\n');
|
|
649
|
+
function formatRefs(refs = {}) {
|
|
650
|
+
return Object.entries(refs)
|
|
651
|
+
.filter(([, value]) => value != null)
|
|
652
|
+
.map(([key, value]) => `${key}=${flattenInline(value)}`)
|
|
653
|
+
.join(', ');
|
|
1558
654
|
}
|
|
1559
655
|
|
|
1560
656
|
export async function appendClaworldJournalEvent(options = {}, event = {}, appendOptions = {}) {
|
|
1561
657
|
const workspaceRoot = resolveClaworldWorkspaceRoot(options, appendOptions.homeDir || os.homedir());
|
|
1562
658
|
await ensureClaworldWorkingMemory(workspaceRoot, appendOptions);
|
|
1563
659
|
const normalizedEvent = buildClaworldMaintenanceEvent(event);
|
|
1564
|
-
const
|
|
660
|
+
const monthKey = toMonthKey(normalizedEvent.timestamp);
|
|
1565
661
|
const journalPath = path.join(
|
|
1566
662
|
workspaceRoot,
|
|
1567
663
|
CLAWORLD_WORKING_MEMORY_DIR,
|
|
1568
664
|
CLAWORLD_JOURNAL_DIR,
|
|
1569
|
-
`${
|
|
665
|
+
`${monthKey}.md`,
|
|
1570
666
|
);
|
|
1571
667
|
const currentContent = await readTextIfPresent(journalPath);
|
|
1572
|
-
const
|
|
668
|
+
const refsLine = formatRefs(normalizedEvent.refs);
|
|
669
|
+
const entry = [
|
|
670
|
+
`## ${normalizedEvent.timestamp} - ${normalizedEvent.kind}`,
|
|
671
|
+
`- Source: ${normalizedEvent.source}`,
|
|
672
|
+
`- Summary: ${flattenInline(normalizedEvent.summary)}`,
|
|
673
|
+
normalizedEvent.excerpt ? `- Excerpt: ${flattenInline(normalizedEvent.excerpt)}` : null,
|
|
674
|
+
refsLine ? `- Refs: ${refsLine}` : null,
|
|
675
|
+
'',
|
|
676
|
+
].filter((line) => line != null).join('\n');
|
|
1573
677
|
if (currentContent == null) {
|
|
1574
|
-
await atomicWriteText(journalPath, `# Claworld Journal ${
|
|
678
|
+
await atomicWriteText(journalPath, `# Claworld Journal ${monthKey}\n\n${entry}`, {
|
|
1575
679
|
backup: false,
|
|
1576
680
|
rejectEmptyOverwrite: false,
|
|
1577
681
|
});
|
|
1578
682
|
} else {
|
|
1579
683
|
await fs.appendFile(journalPath, `${currentContent.endsWith('\n') ? '' : '\n'}${entry}`, 'utf8');
|
|
1580
684
|
}
|
|
1581
|
-
let sessionDirectory = null;
|
|
1582
|
-
if (appendOptions.updateSessionDirectory !== false) {
|
|
1583
|
-
try {
|
|
1584
|
-
sessionDirectory = await updateClaworldSessionDirectory(workspaceRoot, normalizedEvent, {
|
|
1585
|
-
...appendOptions,
|
|
1586
|
-
timestamp: normalizedEvent.timestamp,
|
|
1587
|
-
});
|
|
1588
|
-
} catch (error) {
|
|
1589
|
-
sessionDirectory = {
|
|
1590
|
-
ok: false,
|
|
1591
|
-
error: error?.message || String(error),
|
|
1592
|
-
};
|
|
1593
|
-
}
|
|
1594
|
-
}
|
|
1595
685
|
return {
|
|
1596
686
|
ok: true,
|
|
1597
687
|
journalPath,
|
|
1598
|
-
sessionDirectory,
|
|
1599
688
|
event: normalizedEvent,
|
|
1600
689
|
};
|
|
1601
690
|
}
|
|
@@ -1642,27 +731,11 @@ export async function buildClaworldBootstrapPromptContext(context = {}, options
|
|
|
1642
731
|
})
|
|
1643
732
|
: { slices: {} };
|
|
1644
733
|
const pointerInjected = target === CLAWORLD_BOOTSTRAP_TARGETS.MAIN;
|
|
1645
|
-
const managementPolicyInjected = target === CLAWORLD_BOOTSTRAP_TARGETS.MANAGEMENT;
|
|
1646
|
-
let managementMainSessionKey = null;
|
|
1647
|
-
if (managementPolicyInjected && resolvedWorkspaceRoot) {
|
|
1648
|
-
try {
|
|
1649
|
-
const sessionDirectory = await readClaworldSessionDirectory(resolvedWorkspaceRoot);
|
|
1650
|
-
managementMainSessionKey = normalizeText(
|
|
1651
|
-
sessionDirectory.directory?.main?.lastActiveSessionKey,
|
|
1652
|
-
null,
|
|
1653
|
-
);
|
|
1654
|
-
} catch {
|
|
1655
|
-
managementMainSessionKey = null;
|
|
1656
|
-
}
|
|
1657
|
-
}
|
|
1658
734
|
const parts = [];
|
|
1659
735
|
let truncated = false;
|
|
1660
736
|
if (pointerInjected) {
|
|
1661
737
|
const pointerBudget = maxTotalChars - measureBootstrapParts(parts) - (parts.length > 0 ? 2 : 0);
|
|
1662
|
-
const fittedPointer = truncateBootstrapText(
|
|
1663
|
-
buildClaworldContextPointer({ workspaceRoot: resolvedWorkspaceRoot }),
|
|
1664
|
-
pointerBudget,
|
|
1665
|
-
);
|
|
738
|
+
const fittedPointer = truncateBootstrapText(buildClaworldContextPointer(), pointerBudget);
|
|
1666
739
|
if (fittedPointer.text) {
|
|
1667
740
|
parts.push(fittedPointer.text);
|
|
1668
741
|
}
|
|
@@ -1670,22 +743,6 @@ export async function buildClaworldBootstrapPromptContext(context = {}, options
|
|
|
1670
743
|
truncated = true;
|
|
1671
744
|
}
|
|
1672
745
|
}
|
|
1673
|
-
if (managementPolicyInjected) {
|
|
1674
|
-
const policyBudget = maxTotalChars - measureBootstrapParts(parts) - (parts.length > 0 ? 2 : 0);
|
|
1675
|
-
const fittedPolicy = truncateBootstrapText(
|
|
1676
|
-
buildClaworldManagementStartupPrompt({
|
|
1677
|
-
workspaceRoot: resolvedWorkspaceRoot,
|
|
1678
|
-
mainSessionKey: managementMainSessionKey,
|
|
1679
|
-
}),
|
|
1680
|
-
policyBudget,
|
|
1681
|
-
);
|
|
1682
|
-
if (fittedPolicy.text) {
|
|
1683
|
-
parts.push(fittedPolicy.text);
|
|
1684
|
-
}
|
|
1685
|
-
if (fittedPolicy.truncated) {
|
|
1686
|
-
truncated = true;
|
|
1687
|
-
}
|
|
1688
|
-
}
|
|
1689
746
|
const sectionTitle = target === CLAWORLD_BOOTSTRAP_TARGETS.MAIN
|
|
1690
747
|
? '# Claworld Startup Memory'
|
|
1691
748
|
: target === CLAWORLD_BOOTSTRAP_TARGETS.MANAGEMENT
|
|
@@ -1716,7 +773,6 @@ export async function buildClaworldBootstrapPromptContext(context = {}, options
|
|
|
1716
773
|
workspaceRoot: resolvedWorkspaceRoot,
|
|
1717
774
|
files: selectedFiles.map((relativePath) => buildBootstrapFileLabel(relativePath)),
|
|
1718
775
|
pointerInjected,
|
|
1719
|
-
managementPolicyInjected,
|
|
1720
776
|
fallbackFiles: fileSections.fallbackFiles,
|
|
1721
777
|
omittedFiles: fileSections.omittedFiles,
|
|
1722
778
|
truncated,
|
|
@@ -1778,40 +834,6 @@ function assertAllowedTarget(runType, target) {
|
|
|
1778
834
|
}
|
|
1779
835
|
}
|
|
1780
836
|
|
|
1781
|
-
function normalizeMarkdownNewlines(content) {
|
|
1782
|
-
return String(content ?? '').replace(/\r\n/g, '\n');
|
|
1783
|
-
}
|
|
1784
|
-
|
|
1785
|
-
function assertContextMarkdownUsesBullets(target, normalizedContent, schema) {
|
|
1786
|
-
const maxBulletLength = schema.maxBulletLength || null;
|
|
1787
|
-
const lines = normalizedContent.split('\n');
|
|
1788
|
-
for (const line of lines) {
|
|
1789
|
-
const trimmed = line.trim();
|
|
1790
|
-
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
1791
|
-
if (!trimmed.startsWith('- ')) {
|
|
1792
|
-
throw new Error(`Claworld maintenance patch for ${target} must use bullet lines under schema sections.`);
|
|
1793
|
-
}
|
|
1794
|
-
if (maxBulletLength && trimmed.length > maxBulletLength) {
|
|
1795
|
-
throw new Error(`Claworld maintenance patch for ${target} has a bullet longer than ${maxBulletLength} characters.`);
|
|
1796
|
-
}
|
|
1797
|
-
}
|
|
1798
|
-
}
|
|
1799
|
-
|
|
1800
|
-
function assertContextFileSchema(target, content) {
|
|
1801
|
-
const schema = CLAWORLD_CONTEXT_FILE_SCHEMAS[target];
|
|
1802
|
-
if (!schema) return;
|
|
1803
|
-
const normalizedContent = normalizeMarkdownNewlines(content);
|
|
1804
|
-
if (!normalizedContent.startsWith(`${schema.title}\n`)) {
|
|
1805
|
-
throw new Error(`Claworld maintenance patch for ${target} must start with ${schema.title}.`);
|
|
1806
|
-
}
|
|
1807
|
-
for (const heading of schema.headings) {
|
|
1808
|
-
if (!normalizedContent.includes(`\n${heading}\n`)) {
|
|
1809
|
-
throw new Error(`Claworld maintenance patch for ${target} is missing required section ${heading}.`);
|
|
1810
|
-
}
|
|
1811
|
-
}
|
|
1812
|
-
assertContextMarkdownUsesBullets(target, normalizedContent, schema);
|
|
1813
|
-
}
|
|
1814
|
-
|
|
1815
837
|
function normalizeReportPatch(report, index) {
|
|
1816
838
|
const filename = normalizeText(report?.filename ?? report?.name, `report-${index + 1}.md`);
|
|
1817
839
|
const normalizedFilename = path.posix.basename(filename.endsWith('.md') ? filename : `${filename}.md`);
|
|
@@ -1868,10 +890,10 @@ export function normalizeClaworldMaintenanceOutput(runType, output = {}, options
|
|
|
1868
890
|
patches.push(normalizeFilePatchValue(output.memoryMd, CLAWORLD_WORKING_MEMORY_FILES.memory, 'memoryMd'));
|
|
1869
891
|
}
|
|
1870
892
|
if (Object.prototype.hasOwnProperty.call(output, 'journalAppendMd')) {
|
|
1871
|
-
const
|
|
893
|
+
const monthKey = toMonthKey(options.timestamp || output.timestamp || Date.now());
|
|
1872
894
|
patches.push({
|
|
1873
895
|
operation: 'append_section',
|
|
1874
|
-
target: `${CLAWORLD_JOURNAL_DIR}/${
|
|
896
|
+
target: `${CLAWORLD_JOURNAL_DIR}/${monthKey}.md`,
|
|
1875
897
|
content: String(output.journalAppendMd ?? ''),
|
|
1876
898
|
});
|
|
1877
899
|
}
|
|
@@ -1904,9 +926,6 @@ export function normalizeClaworldMaintenanceOutput(runType, output = {}, options
|
|
|
1904
926
|
target,
|
|
1905
927
|
content: String(patch.content ?? ''),
|
|
1906
928
|
};
|
|
1907
|
-
if (operation === 'replace') {
|
|
1908
|
-
assertContextFileSchema(target, normalizedPatch.content);
|
|
1909
|
-
}
|
|
1910
929
|
const rationale = normalizeText(patch.rationale, null);
|
|
1911
930
|
if (rationale) normalizedPatch.rationale = rationale;
|
|
1912
931
|
return normalizedPatch;
|
|
@@ -1936,8 +955,8 @@ async function applyMaintenancePatch(workspaceRoot, patch) {
|
|
|
1936
955
|
await fs.mkdir(path.dirname(absolutePath), { recursive: true });
|
|
1937
956
|
const currentContent = await readTextIfPresent(absolutePath);
|
|
1938
957
|
if (currentContent == null) {
|
|
1939
|
-
const
|
|
1940
|
-
await atomicWriteText(absolutePath, `# Claworld Journal ${
|
|
958
|
+
const monthKey = path.basename(patch.target, '.md');
|
|
959
|
+
await atomicWriteText(absolutePath, `# Claworld Journal ${monthKey}\n\n${patch.content}`, {
|
|
1941
960
|
backup: false,
|
|
1942
961
|
rejectEmptyOverwrite: false,
|
|
1943
962
|
});
|