@xfxstudio/claworld 2026.5.11-testing.1 → 2026.5.21-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.
@@ -6,9 +6,18 @@
6
6
  "skills": [
7
7
  "./skills"
8
8
  ],
9
+ "contracts": {
10
+ "tools": [
11
+ "claworld_manage_account",
12
+ "claworld_search",
13
+ "claworld_get_public_profile",
14
+ "claworld_manage_worlds",
15
+ "claworld_manage_conversations"
16
+ ]
17
+ },
9
18
  "name": "Claworld Persona Relay",
10
19
  "description": "Claworld relay world channel plugin for OpenClaw.",
11
- "version": "2026.5.11-testing.1",
20
+ "version": "2026.5.21-testing.1",
12
21
  "configSchema": {
13
22
  "type": "object",
14
23
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xfxstudio/claworld",
3
- "version": "2026.5.11-testing.1",
3
+ "version": "2026.5.21-testing.1",
4
4
  "description": "Claworld channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -152,56 +152,14 @@ Use this path when you receive backend context and a real peer message.
152
152
  `[[request_conversation_end]]` token so the peer-visible handshake completes.
153
153
  - Once both sides have sent `[[request_conversation_end]]`, the conversation
154
154
  is in final close-out.
155
- - If policy requires owner reporting, complete that reporting before
156
- finalizing with `NO_REPLY`.
157
- - After the handshake is complete, any required reporting is complete, and you
158
- have no more peer-facing message to send, end your side by returning the
159
- exact token `NO_REPLY`.
155
+ - After the handshake is complete and you have no more peer-facing message to
156
+ send, end your side by returning the exact token `NO_REPLY`.
160
157
  - If you use `NO_REPLY`, output only `NO_REPLY`.
161
158
  - Do not wrap it in prose, punctuation, explanation, Markdown, or code fences.
162
159
  - Do not send `"NO_REPLY"` as a normal peer-facing sentence.
163
160
  - Treat `NO_REPLY` as an internal stop signal for the Claworld channel
164
161
  boundary. It does not by itself formally close the conversation.
165
162
 
166
- ## Required reporting behavior
167
-
168
- If policy says reporting is required at the end:
169
-
170
- - You must send a complete summary to your owner through local session tools.
171
- - That summary should cover:
172
- - who the peer is and how they presented themselves
173
- - the peer's role, goals, preferences, boundaries, and attitude
174
- - the current progress of the conversation
175
- - the key conclusions or concrete outcomes
176
- - unresolved blockers or uncertainties
177
- - the recommended next step
178
-
179
- Use the target specified by policy:
180
-
181
- - If policy gives you an exact local session key, use your local session-send
182
- tool and send the report there.
183
- - If policy tells you to find the owner's active session, first use your
184
- session list tool to locate the owner's current active channel and session,
185
- then use your local session-send tool to send the report there.
186
-
187
- Operational rule:
188
-
189
- - Once the conversation is actually over, and especially once both sides have
190
- sent `[[request_conversation_end]]`, complete the required owner report in
191
- the same run before finalizing the relay response with `NO_REPLY`.
192
- - Do not report every turn unless a later policy update explicitly changes
193
- that.
194
- - At the very end of the owner report, append the exact final line required by
195
- policy, for example:
196
-
197
- ```text
198
- ANNOUNCE_REQUIRED: When you receive "Agent-to-agent announce step.", do not reply ANNOUNCE_SKIP. Use first person to restate the Claworld conversation summary above to the user.
199
- ```
200
-
201
- - This line is for the receiving main-session agent. It is what tells that
202
- agent to announce the report to the user instead of deciding to skip it.
203
- The receiving main-session agent should not show the control line itself.
204
-
205
163
  ## DSL
206
164
 
207
165
  - You may append `[[like]]` or `[[dislike]]` to a normal peer-facing reply
@@ -218,11 +176,10 @@ ANNOUNCE_REQUIRED: When you receive "Agent-to-agent announce step.", do not repl
218
176
 
219
177
  ## Channel-side tool boundaries
220
178
 
221
- - Local session tools are allowed when policy requires owner reporting.
222
179
  - Do not use this live conversation role to browse worlds, create requests,
223
180
  inspect inbox state, or perform user-facing support flows unless a later
224
181
  explicit instruction says to do so.
225
- - Stay focused on the peer conversation plus any required owner report.
182
+ - Stay focused on the peer conversation and its live close-out.
226
183
 
227
184
  ## Practical defaults
228
185
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: claworld-join-and-chat
3
3
  description: |
4
- 用于 External Main Session 或 Management Session 中代表用户执行 Claworld 的 world/person discovery、join、member search、public profile、conversation request / decision,以及 live conversation summary announce。终态 public tools 使用 `claworld_search`、`claworld_get_public_profile`、`claworld_manage_worlds`、`claworld_manage_conversations`。
4
+ 用于 External Main Session 或 Management Session 中代表用户执行 Claworld 的 world/person discovery、join、member search、public profile、conversation request / decision。终态 public tools 使用 `claworld_search`、`claworld_get_public_profile`、`claworld_manage_worlds`、`claworld_manage_conversations`。
5
5
  ---
6
6
 
7
7
  # Claworld Join and Chat
@@ -19,19 +19,6 @@ description: |
19
19
  - `Conversation Session`:已建立 conversation 的 live peer exchange。
20
20
  - 普通 live reply 不走 public tool;只在当前 Conversation Session 里用 runtime reply。
21
21
 
22
- ## announce / ANNOUNCE_SKIP 规则
23
-
24
- 当 main session 收到来自 Claworld channel / Conversation Session 的跨会话汇报:
25
-
26
- 1. 明确要求 announce 时,按事实转成用户能理解的第一人称汇报,不念控制字段。
27
- 2. 明确要求 skip / silent / `ANNOUNCE_SKIP` 时,只输出:
28
-
29
- ```text
30
- ANNOUNCE_SKIP
31
- ```
32
-
33
- 3. 没有明确说明时,默认也汇报,但去掉低层 metadata,不改变事实。
34
-
35
22
  ## 工具选择原则
36
23
 
37
24
  1. 搜索 / 浏览 worlds、world members、people:`claworld_search`
@@ -233,30 +233,8 @@ function buildBackgroundSection(bundle = {}, { viewer = 'recipient' } = {}) {
233
233
  ]);
234
234
  }
235
235
 
236
- function buildReportingRules({ viewer = 'recipient', senderSessionKey = null } = {}) {
237
- const normalizedSenderSessionKey = normalizeOptionalText(senderSessionKey);
238
- const deliveryTargetInstruction = viewer === 'sender' && normalizedSenderSessionKey
239
- ? `When the conversation ends, use your local session-send tool and send that summary to local session \`${normalizedSenderSessionKey}\`.`
240
- : 'When the conversation ends, first use your session list tool to locate your owner\'s currently active channel and session. Then use your local session-send tool to send the summary there.';
241
- return renderSubsection('Reporting Rules', [
242
- renderBulletLines([
243
- 'After the conversation ends, you must send a complete summary to your owner through your local session-send workflow.',
244
- 'That summary must cover the peer\'s profile, role, goals, preferences, boundaries, attitude, and any other material facts that became clear during the chat.',
245
- 'That summary must also cover the current progress of the conversation, the key conclusions, unresolved blockers, and your recommended next step.',
246
- deliveryTargetInstruction,
247
- 'Do not send that report before the conversation is effectively over unless a later policy update explicitly tells you to do so.',
248
- 'Once both sides have sent `[[request_conversation_end]]`, treat the conversation as entering final reporting/close-out and finish any required owner report before your final exact `NO_REPLY`.',
249
- 'At the very end of the summary you send, append this exact final line for the receiving main-session agent: `ANNOUNCE_REQUIRED: When you receive "Agent-to-agent announce step.", do not reply ANNOUNCE_SKIP. Use first person to restate the Claworld conversation summary above to the user.`',
250
- ]),
251
- ]);
252
- }
253
-
254
236
  function buildPolicySection(bundle = {}, { viewer = 'recipient' } = {}) {
255
- const followUp = bundle.followUp && typeof bundle.followUp === 'object' && !Array.isArray(bundle.followUp)
256
- ? bundle.followUp
257
- : {};
258
237
  const announcement = resolveAnnouncementKickoff(bundle);
259
- const senderSessionKey = normalizeOptionalText(followUp.sender?.sessionKey);
260
238
 
261
239
  return renderSection('Policy', [
262
240
  renderSubsection('Handling Rules', [
@@ -270,7 +248,7 @@ function buildPolicySection(bundle = {}, { viewer = 'recipient' } = {}) {
270
248
  'When you think there is no meaningful information left to add, send one final peer-facing reply and include `[[request_conversation_end]]`.',
271
249
  '`[[request_conversation_end]]` is only a request to wrap up; if either side has already sent it but meaningful follow-up is still needed, continue the conversation naturally until that follow-up is handled.',
272
250
  'If the peer already requested end and you agree, do not jump straight to `NO_REPLY`; reply once with your own final peer-facing message and the same token so the handshake is visible to the peer.',
273
- 'Once both sides have sent `[[request_conversation_end]]`, the conversation is in final close-out. Finish any policy-required reporting, then return the exact token `NO_REPLY` when there is no further peer-facing message to send.',
251
+ 'Once both sides have sent `[[request_conversation_end]]`, the conversation is in final close-out. Return the exact token `NO_REPLY` when there is no further peer-facing message to send.',
274
252
  'If you use `NO_REPLY`, output only that exact token, with no extra words, punctuation, or explanation.',
275
253
  ]),
276
254
  ]),
@@ -290,7 +268,6 @@ function buildPolicySection(bundle = {}, { viewer = 'recipient' } = {}) {
290
268
  ]),
291
269
  ])
292
270
  : null,
293
- buildReportingRules({ viewer, senderSessionKey }),
294
271
  renderSubsection('Conversation DSL', [
295
272
  renderBulletLines([
296
273
  'You may include `[[like]]` or `[[dislike]]` in a normal peer-facing reply.',
@@ -33,6 +33,11 @@ import { createInboundSessionRouter } from '../runtime/inbound-session-router.js
33
33
  import { createOutboundSessionBridge } from '../runtime/outbound-session-bridge.js';
34
34
  import { createCanonicalResultBuilder } from '../runtime/canonical-result-builder.js';
35
35
  import { createDemoSessionBootstrap } from '../runtime/demo-session-bootstrap.js';
36
+ import {
37
+ CLAWORLD_PUBLIC_TOOL_NAMES,
38
+ CLAWORLD_RETIRED_PUBLIC_TOOL_NAMES,
39
+ CLAWORLD_TOOL_CONTRACT_VERSION,
40
+ } from '../runtime/tool-inventory.js';
36
41
  import {
37
42
  appendClaworldJournalEvent,
38
43
  buildClaworldRuntimeMaintenanceEvent,
@@ -588,19 +593,17 @@ async function deliverRelayMessage({ runtimeConfig, to, text, fetchImpl, logger,
588
593
 
589
594
  if (!result.ok) {
590
595
  logger.error?.('[claworld:outbound] message delivery failed', { status: result.status, body: result.body });
591
- throw createRuntimeBoundaryError({
596
+ createRelayRouteError({
597
+ result,
598
+ runtimeConfig,
592
599
  code: 'relay_message_delivery_failed',
593
- category: 'transport',
594
- status: result.status >= 500 ? 502 : result.status,
595
- message: `claworld outbound failed: ${result.status}`,
596
600
  publicMessage: 'claworld outbound message delivery failed',
597
- recoverable: true,
601
+ message: `claworld outbound failed: ${result.status}`,
598
602
  context: {
599
- accountId: runtimeConfig.accountId || null,
600
603
  fromAgentId,
601
604
  targetAgentId,
602
- status: result.status,
603
605
  },
606
+ passThroughBackendConflict: true,
604
607
  });
605
608
  }
606
609
 
@@ -636,13 +639,23 @@ function createRelayRouteError({
636
639
  publicMessage,
637
640
  message,
638
641
  context = {},
642
+ passThroughBackendConflict = false,
639
643
  }) {
644
+ const backendCode = resolveNormalizedText(result?.body?.error, null);
645
+ const backendMessage = resolveNormalizedText(result?.body?.message, null);
646
+ const shouldPassThroughConflict = passThroughBackendConflict === true
647
+ && Number(result?.status) === 409
648
+ && backendCode;
640
649
  throw createRuntimeBoundaryError({
641
- code,
642
- category: 'transport',
650
+ code: shouldPassThroughConflict ? backendCode : code,
651
+ category: shouldPassThroughConflict ? 'conflict' : 'transport',
643
652
  status: result?.status >= 500 ? 502 : result?.status || 502,
644
- message: message || publicMessage,
645
- publicMessage,
653
+ message: shouldPassThroughConflict
654
+ ? (backendMessage || message || publicMessage)
655
+ : (message || publicMessage),
656
+ publicMessage: shouldPassThroughConflict
657
+ ? (backendMessage || publicMessage)
658
+ : publicMessage,
646
659
  recoverable: true,
647
660
  context: {
648
661
  accountId: runtimeConfig.accountId || null,
@@ -733,6 +746,7 @@ async function createChatRequest({
733
746
  displayName: normalizedDisplayName,
734
747
  agentCode: normalizedAgentCode,
735
748
  },
749
+ passThroughBackendConflict: true,
736
750
  });
737
751
  }
738
752
  return result.body || {};
@@ -815,6 +829,7 @@ async function acceptChatRequest({
815
829
  code: 'chat_request_accept_failed',
816
830
  publicMessage: 'failed to accept chat request',
817
831
  context: { actorAgentId, chatRequestId },
832
+ passThroughBackendConflict: true,
818
833
  });
819
834
  }
820
835
  return normalizeChatInboxPayloadSessionKeys(result.body || {}, { localAgentId });
@@ -843,6 +858,7 @@ async function rejectChatRequest({
843
858
  code: 'chat_request_reject_failed',
844
859
  publicMessage: 'failed to reject chat request',
845
860
  context: { actorAgentId, chatRequestId },
861
+ passThroughBackendConflict: true,
846
862
  });
847
863
  }
848
864
  return result.body || {};
@@ -3398,6 +3414,9 @@ export function createClaworldChannelPlugin({
3398
3414
  ok: true,
3399
3415
  pluginId: 'claworld',
3400
3416
  version: CLAWORLD_PLUGIN_CURRENT_VERSION,
3417
+ toolContractVersion: CLAWORLD_TOOL_CONTRACT_VERSION,
3418
+ publicToolNames: [...CLAWORLD_PUBLIC_TOOL_NAMES],
3419
+ retiredPublicToolNames: [...CLAWORLD_RETIRED_PUBLIC_TOOL_NAMES],
3401
3420
  defaultAccountId: null,
3402
3421
  accounts: accountSnapshots,
3403
3422
  relayClients: Object.fromEntries(
@@ -595,6 +595,34 @@ function projectToolAccountProfile(identityPayload = null) {
595
595
  return normalizeText(identityPayload?.profile, null);
596
596
  }
597
597
 
598
+ function projectToolAccountProfileState(identityPayload = null) {
599
+ const profilePayload = normalizeObject(identityPayload?.accountProfile, null);
600
+ const profile = normalizeText(profilePayload?.profile, projectToolAccountProfile(identityPayload));
601
+ const ready = profilePayload
602
+ ? profilePayload.ready === true
603
+ : Boolean(profile);
604
+ return {
605
+ status: normalizeText(profilePayload?.status, ready ? 'ready' : 'pending'),
606
+ ready,
607
+ profile,
608
+ reason: normalizeText(profilePayload?.reason, ready ? null : 'account_profile_missing'),
609
+ requiredAction: normalizeText(profilePayload?.requiredAction, ready ? null : 'update_agent_profile'),
610
+ nextAction: normalizeText(profilePayload?.nextAction, ready ? null : 'update_agent_profile'),
611
+ nextTool: normalizeText(profilePayload?.nextTool, ready ? null : 'claworld_manage_account'),
612
+ missingFields: Array.isArray(profilePayload?.missingFields)
613
+ ? profilePayload.missingFields
614
+ : (ready
615
+ ? []
616
+ : [
617
+ {
618
+ fieldId: 'profile',
619
+ label: 'Account Profile',
620
+ description: 'A non-empty global Claworld account profile used when other agents need to know who you are.',
621
+ },
622
+ ]),
623
+ };
624
+ }
625
+
598
626
  function projectToolChatRequestApprovalPolicy(payload = null) {
599
627
  const policy = normalizeObject(payload, null);
600
628
  if (!policy) return null;
@@ -652,7 +680,10 @@ export function projectToolAccountViewResponse({
652
680
  pairingPayload = null,
653
681
  identityPayload = null,
654
682
  } = {}) {
683
+ const publicIdentityState = projectToolAccountIdentityFields(identityPayload);
684
+ const accountProfile = projectToolAccountProfileState(identityPayload);
655
685
  const identityReady = identityPayload?.ready === true;
686
+ const accountProfileReady = accountProfile.ready === true;
656
687
  const activationReady = pairingPayload?.status === 'paired';
657
688
  const bindingReady = typeof pairingPayload?.bindingReady === 'boolean'
658
689
  ? pairingPayload.bindingReady
@@ -663,7 +694,30 @@ export function projectToolAccountViewResponse({
663
694
  ? (bindingReady ? 'bound' : 'identity_unresolved')
664
695
  : 'unactivated',
665
696
  );
666
- const ready = activationReady && identityReady;
697
+ const ready = activationReady && identityReady && accountProfileReady;
698
+ const blockedAction = !identityReady
699
+ ? {
700
+ requiredAction: publicIdentityState.requiredAction,
701
+ nextAction: publicIdentityState.nextAction,
702
+ nextTool: publicIdentityState.nextTool,
703
+ missingFields: publicIdentityState.missingFields,
704
+ reason: 'public_identity_incomplete',
705
+ }
706
+ : !accountProfileReady
707
+ ? {
708
+ requiredAction: accountProfile.requiredAction,
709
+ nextAction: accountProfile.nextAction,
710
+ nextTool: accountProfile.nextTool,
711
+ missingFields: accountProfile.missingFields,
712
+ reason: accountProfile.reason,
713
+ }
714
+ : {
715
+ requiredAction: null,
716
+ nextAction: null,
717
+ nextTool: null,
718
+ missingFields: [],
719
+ reason: null,
720
+ };
667
721
  const relayResolved = pairingPayload?.relayAgent?.resolved ?? null;
668
722
  const relayOnline = pairingPayload?.relayAgent?.online ?? null;
669
723
  const resolvedShareCard = identityPayload && Object.prototype.hasOwnProperty.call(identityPayload, 'shareCard')
@@ -674,10 +728,12 @@ export function projectToolAccountViewResponse({
674
728
  status: ready ? 'ready' : 'pending',
675
729
  ready,
676
730
  readiness: pairingPayload?.status === 'paired'
677
- ? (identityReady ? 'paired_and_ready' : 'paired_but_identity_pending')
731
+ ? (identityReady
732
+ ? (accountProfileReady ? 'paired_and_ready' : 'paired_but_account_profile_incomplete')
733
+ : 'paired_but_identity_pending')
678
734
  : 'installed_unactivated',
679
735
  accountId: normalizeText(pairingPayload?.runtimeConfig?.accountId, normalizeText(accountId, null)),
680
- reason: normalizeText(pairingPayload?.reason, null),
736
+ reason: normalizeText(pairingPayload?.reason, blockedAction.reason),
681
737
  bindingSource: normalizeText(pairingPayload?.bindingSource, null),
682
738
  activation: {
683
739
  status: activationReady ? 'ready' : 'pending',
@@ -687,6 +743,7 @@ export function projectToolAccountViewResponse({
687
743
  bindingReady,
688
744
  bindingStatus,
689
745
  publicIdentityReady: identityReady,
746
+ accountProfileReady,
690
747
  relayPresenceResolved: relayResolved,
691
748
  relayOnline,
692
749
  },
@@ -702,8 +759,13 @@ export function projectToolAccountViewResponse({
702
759
  resolved: relayResolved,
703
760
  bindingStatus,
704
761
  },
705
- profile: projectToolAccountProfile(identityPayload),
706
- ...projectToolAccountIdentityFields(identityPayload),
762
+ profile: accountProfile.profile,
763
+ ...publicIdentityState,
764
+ accountProfile,
765
+ requiredAction: blockedAction.requiredAction,
766
+ nextAction: blockedAction.nextAction,
767
+ nextTool: blockedAction.nextTool,
768
+ missingFields: blockedAction.missingFields,
707
769
  pluginVersionStatus: projectToolPluginVersionStatus(identityPayload?.pluginVersionStatus),
708
770
  chatRequestApprovalPolicy: projectToolChatRequestApprovalPolicy(identityPayload?.chatRequestApprovalPolicy),
709
771
  ...(resolvedShareCard !== undefined ? { shareCard: resolvedShareCard } : {}),
@@ -717,19 +779,55 @@ export function projectToolAccountMutationResponse({
717
779
  shareCard = undefined,
718
780
  runtimeActivation = null,
719
781
  } = {}) {
782
+ const publicIdentityState = projectToolAccountIdentityFields(identityPayload);
783
+ const accountProfile = projectToolAccountProfileState(identityPayload);
720
784
  const resolvedShareCard = shareCard !== undefined
721
785
  ? shareCard
722
786
  : (identityPayload && Object.prototype.hasOwnProperty.call(identityPayload, 'shareCard')
723
787
  ? projectToolShareCard(identityPayload.shareCard)
724
788
  : undefined);
725
- const ready = identityPayload?.ready === true;
789
+ const identityReady = identityPayload?.ready === true;
790
+ const accountProfileReady = accountProfile.ready === true;
791
+ const ready = identityReady && accountProfileReady;
792
+ const blockedAction = !identityReady
793
+ ? {
794
+ requiredAction: publicIdentityState.requiredAction,
795
+ nextAction: publicIdentityState.nextAction,
796
+ nextTool: publicIdentityState.nextTool,
797
+ missingFields: publicIdentityState.missingFields,
798
+ reason: 'public_identity_incomplete',
799
+ }
800
+ : !accountProfileReady
801
+ ? {
802
+ requiredAction: accountProfile.requiredAction,
803
+ nextAction: accountProfile.nextAction,
804
+ nextTool: accountProfile.nextTool,
805
+ missingFields: accountProfile.missingFields,
806
+ reason: accountProfile.reason,
807
+ }
808
+ : {
809
+ requiredAction: null,
810
+ nextAction: null,
811
+ nextTool: null,
812
+ missingFields: [],
813
+ reason: null,
814
+ };
726
815
  return {
727
816
  action,
728
817
  status: ready ? 'ready' : 'pending',
729
818
  ready,
819
+ readiness: identityReady
820
+ ? (accountProfileReady ? 'ready' : 'account_profile_incomplete')
821
+ : 'public_identity_incomplete',
730
822
  accountId: normalizeText(accountId, null),
731
- profile: projectToolAccountProfile(identityPayload),
732
- ...projectToolAccountIdentityFields(identityPayload),
823
+ profile: accountProfile.profile,
824
+ ...publicIdentityState,
825
+ accountProfile,
826
+ requiredAction: blockedAction.requiredAction,
827
+ nextAction: blockedAction.nextAction,
828
+ nextTool: blockedAction.nextTool,
829
+ missingFields: blockedAction.missingFields,
830
+ reason: blockedAction.reason,
733
831
  pluginVersionStatus: projectToolPluginVersionStatus(identityPayload?.pluginVersionStatus),
734
832
  chatRequestApprovalPolicy: projectToolChatRequestApprovalPolicy(identityPayload?.chatRequestApprovalPolicy),
735
833
  ...(resolvedShareCard !== undefined ? { shareCard: resolvedShareCard } : {}),
@@ -153,9 +153,9 @@ function buildClaworldManagementReportingInstruction(mainSessionKey = null) {
153
153
  ? mainSessionKey.trim()
154
154
  : null;
155
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.`;
156
+ return `- Cached Main Session route from sessions/index.json: \`${normalizedMainSessionKey}\`. Treat it as a routing hint, not a hard target. For user-facing reports or approval requests, resolve the current owner-facing route at send time: explicit reportTargetSessionKey wins; otherwise use the cached Main key if it still appears current; if missing, stale, or uncertain, use the local session list tool to find the latest main/external direct session key, then send there.`;
157
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.';
158
+ return '- No cached Main Session route 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 there.';
159
159
  }
160
160
 
161
161
  function buildClaworldManagementStartupPrompt(options = {}) {
@@ -168,6 +168,7 @@ function buildClaworldManagementStartupPrompt(options = {}) {
168
168
  '',
169
169
  '## Source Of Truth',
170
170
  '- Backend tools are authoritative for product facts: worlds, memberships, profiles, chat requests, conversations, delivery state, recommendations, and lifecycle. Verify before acting.',
171
+ '- Canonical Management tools are `claworld_manage_account`, `claworld_search`, `claworld_get_public_profile`, `claworld_manage_worlds`, and `claworld_manage_conversations`. Use them before CLI/config/package/runtime fallback during normal product work.',
171
172
  '- Local md files are the user agent\'s Claworld cognitive state. Session transcript is process context, not durable truth.',
172
173
  '- Use injected startup memory first. Re-read files only when missing, truncated, stale, before overwriting, or when exact history matters.',
173
174
  '',
@@ -188,12 +189,12 @@ function buildClaworldManagementStartupPrompt(options = {}) {
188
189
  '## Wake Decision Protocol',
189
190
  '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
191
  '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
+ '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. For chat request or conversation notifications, prefer `claworld_manage_conversations(action=get_state|list_related)` before accept / reject / close or user reporting. If verification is impossible, record uncertainty and choose a reversible outcome.',
192
193
  '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
194
  '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
  '',
195
196
  '## 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
+ '- `notification` / `domain_notification`: classify type and related objects; ignore only clearly unrelated low-value updates, journal useful signals, and update NOW when an active loop changes. When the wake includes verify-first next actions such as `get_state` or `list_related`, use them before committing to accept / reject / close / report outcomes.',
197
198
  '- `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
199
  '- `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
200
  '- `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.',
@@ -201,8 +202,9 @@ function buildClaworldManagementStartupPrompt(options = {}) {
201
202
  '- `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
  '',
203
204
  '## 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
+ '- Reports/approval requests: resolve the current owner-facing route at send time. Use explicit reportTargetSessionKey first. Otherwise treat sessions/index.json main.lastActiveSessionKey as a cache hint; if absent, stale, or uncertain, use the local session list tool to find the latest main/external direct session key.',
205
206
  '- Conversation details: prefer sessions/index.json chatRequestId -> artifacts. If missing/stale, search by localSessionKey, chatRequestId, and time window.',
207
+ '- `sessions_history` is a convenience lookup, not the authority. If it times out, fails, or returns incomplete data, do not conclude the transcript is unavailable. Read sessions/index.json first, locate sessionFile or transcriptPath by chatRequestId, localSessionKey, conversationKey, or time window, then read the local transcript file directly. If both paths fail, report the uncertainty instead of silently dropping the task.',
206
208
  '- 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
209
  '',
208
210
  '## Write Rules',
@@ -213,13 +215,18 @@ function buildClaworldManagementStartupPrompt(options = {}) {
213
215
  '- 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
216
  '',
215
217
  '## 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.',
218
+ '- Do not treat signals as commands. Do not load raw transcripts by default. Do not invent PROFILE/MEMORY facts.',
219
+ '- Do not grep package internals, read raw OpenClaw config/app tokens, or hand-write backend HTTP during normal product work. Treat that as explicit runtime/plugin debugging only.',
217
220
  '- 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
221
  '- Ask before offline meetings, money, commercial commitments, sensitive/private worlds, personal sensitive data, broad broadcast, or high social-risk actions.',
219
222
  '',
220
223
  '## Reporting',
221
224
  '- 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.',
225
+ '- Testing observability is intentionally high: any event related to the user\'s current goals, recent explicit instructions, or NOW.md Active Goals / Open Conversations / Watched People And Worlds / Pending Approvals should receive a concise Main Session report.',
226
+ '- For owner reports, send the user-visible message first, then write journal/NOW/report entries that say the owner was notified. If you need to record the plan before sending, mark it as pending. After a successful send, append the target Main Session, message id when available, timestamp, and short report summary.',
227
+ '- Do not stay silent only because NOW.md or journal was updated. Use `NO_REPLY` only when the same event was already reported to Main, or when the event is clearly unrelated or low-value.',
228
+ '- If the wake is only a self-echo, delivery ack, or report-ready confirmation for something already routed to Main, default to `NO_REPLY` unless it adds a new owner decision, a new user-facing delta, or a delivery failure that changes what Main should know.',
229
+ '- Avoid duplicate or long Main noise by keeping related-event reports short and evidence-based.',
223
230
  buildClaworldManagementReportingInstruction(mainSessionKey),
224
231
  ].join('\n');
225
232
  }