@xfxstudio/claworld 0.2.23-beta.1 → 0.2.23-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,7 +8,7 @@
8
8
  ],
9
9
  "name": "Claworld Persona Relay",
10
10
  "description": "Claworld relay world channel plugin for OpenClaw.",
11
- "version": "0.2.23-beta.1",
11
+ "version": "0.2.23-beta.2",
12
12
  "configSchema": {
13
13
  "type": "object",
14
14
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xfxstudio/claworld",
3
- "version": "0.2.23-beta.1",
3
+ "version": "0.2.23-beta.2",
4
4
  "description": "Claworld channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -1,4 +1,5 @@
1
1
  import { createKickoffBrief } from './relay/kickoff-text.js';
2
+ import { normalizeAcceptedChatKickoffRecord } from './relay/kickoff-progress.js';
2
3
 
3
4
  function normalizeText(value, fallback = null) {
4
5
  if (value == null) return fallback;
@@ -358,7 +359,7 @@ export function normalizeStoredChatRequest(input = {}, { defaultSource = 'chat_r
358
359
  if (acceptedByAgentId) normalized.acceptedByAgentId = acceptedByAgentId;
359
360
  const approvalGrantId = normalizeText(input.approvalGrantId, null);
360
361
  if (approvalGrantId) normalized.approvalGrantId = approvalGrantId;
361
- const kickoff = cloneJsonObject(input.kickoff);
362
+ const kickoff = normalizeAcceptedChatKickoffRecord(cloneJsonObject(input.kickoff));
362
363
  if (kickoff) normalized.kickoff = kickoff;
363
364
 
364
365
  return normalized;
@@ -0,0 +1,162 @@
1
+ import { normalizeOptionalText } from './shared.js';
2
+
3
+ function normalizeAcceptedChatKickoffField(value, fallback = null) {
4
+ return normalizeOptionalText(value) || fallback;
5
+ }
6
+
7
+ export function normalizeAcceptedChatKickoffRecord(kickoff = null, { fallbackStatus = null } = {}) {
8
+ if (!kickoff || typeof kickoff !== 'object' || Array.isArray(kickoff)) return null;
9
+
10
+ const normalized = {
11
+ ...kickoff,
12
+ };
13
+
14
+ const normalizedStatus = normalizeAcceptedChatKickoffField(normalized.status, fallbackStatus);
15
+ const normalizedReason = normalizeAcceptedChatKickoffField(normalized.reason, null);
16
+ const normalizedDeliveredAt = normalizeAcceptedChatKickoffField(normalized.deliveredAt, null);
17
+ const normalizedSenderKickoffDeliveredAt = normalizeAcceptedChatKickoffField(
18
+ normalized.senderKickoffDeliveredAt,
19
+ normalizedDeliveredAt,
20
+ );
21
+ const normalizedOpenerAcceptedAt = normalizeAcceptedChatKickoffField(normalized.openerAcceptedAt, null);
22
+ const normalizedOpenerDeliveredAt = normalizeAcceptedChatKickoffField(normalized.openerDeliveredAt, null);
23
+ const normalizedLiveChatEstablishedAt = normalizeAcceptedChatKickoffField(normalized.liveChatEstablishedAt, null);
24
+ const normalizedTurnId = normalizeAcceptedChatKickoffField(normalized.turnId, null);
25
+ const normalizedDeliveryId = normalizeAcceptedChatKickoffField(normalized.deliveryId, null);
26
+ const normalizedConversationKey = normalizeAcceptedChatKickoffField(normalized.conversationKey, null);
27
+ const normalizedOpenerTurnId = normalizeAcceptedChatKickoffField(normalized.openerTurnId, null);
28
+ const normalizedOpenerDeliveryId = normalizeAcceptedChatKickoffField(normalized.openerDeliveryId, null);
29
+ const normalizedFailedAt = normalizeAcceptedChatKickoffField(normalized.failedAt, null);
30
+
31
+ if (normalizedStatus) normalized.status = normalizedStatus;
32
+ else delete normalized.status;
33
+ if (normalizedReason) normalized.reason = normalizedReason;
34
+ else delete normalized.reason;
35
+ if (normalizedDeliveredAt) normalized.deliveredAt = normalizedDeliveredAt;
36
+ else delete normalized.deliveredAt;
37
+ if (normalizedSenderKickoffDeliveredAt) normalized.senderKickoffDeliveredAt = normalizedSenderKickoffDeliveredAt;
38
+ else delete normalized.senderKickoffDeliveredAt;
39
+ if (normalizedOpenerAcceptedAt) normalized.openerAcceptedAt = normalizedOpenerAcceptedAt;
40
+ else delete normalized.openerAcceptedAt;
41
+ if (normalizedOpenerDeliveredAt) normalized.openerDeliveredAt = normalizedOpenerDeliveredAt;
42
+ else delete normalized.openerDeliveredAt;
43
+ if (normalizedLiveChatEstablishedAt) normalized.liveChatEstablishedAt = normalizedLiveChatEstablishedAt;
44
+ else delete normalized.liveChatEstablishedAt;
45
+ if (normalizedTurnId) normalized.turnId = normalizedTurnId;
46
+ else delete normalized.turnId;
47
+ if (normalizedDeliveryId) normalized.deliveryId = normalizedDeliveryId;
48
+ else delete normalized.deliveryId;
49
+ if (normalizedConversationKey) normalized.conversationKey = normalizedConversationKey;
50
+ else delete normalized.conversationKey;
51
+ if (normalizedOpenerTurnId) normalized.openerTurnId = normalizedOpenerTurnId;
52
+ else delete normalized.openerTurnId;
53
+ if (normalizedOpenerDeliveryId) normalized.openerDeliveryId = normalizedOpenerDeliveryId;
54
+ else delete normalized.openerDeliveryId;
55
+ if (normalizedFailedAt) normalized.failedAt = normalizedFailedAt;
56
+ else delete normalized.failedAt;
57
+
58
+ const hasEstablishedEvidence = Boolean(
59
+ normalized.openerDeliveredAt
60
+ || normalized.liveChatEstablishedAt,
61
+ );
62
+
63
+ if (hasEstablishedEvidence && (!normalized.status || ['queued', 'sent'].includes(normalized.status))) {
64
+ normalized.status = 'established';
65
+ }
66
+
67
+ if (normalized.status === 'established') {
68
+ const establishedAt = normalizeAcceptedChatKickoffField(
69
+ normalized.liveChatEstablishedAt,
70
+ normalizeAcceptedChatKickoffField(
71
+ normalized.openerDeliveredAt,
72
+ normalizeAcceptedChatKickoffField(normalized.openerAcceptedAt, null),
73
+ ),
74
+ );
75
+ if (!normalized.openerDeliveredAt && normalized.openerAcceptedAt) {
76
+ normalized.openerDeliveredAt = normalized.openerAcceptedAt;
77
+ }
78
+ if (!normalized.liveChatEstablishedAt && establishedAt) {
79
+ normalized.liveChatEstablishedAt = establishedAt;
80
+ }
81
+ if (String(normalized.reason || '').startsWith('queued_')) {
82
+ delete normalized.reason;
83
+ }
84
+ }
85
+
86
+ return normalized;
87
+ }
88
+
89
+ export async function markAcceptedChatKickoffFailureWithDeps(deps, {
90
+ requestId = null,
91
+ reason = 'accepted_chat_kickoff_failed',
92
+ turnId = null,
93
+ conversationKey = null,
94
+ } = {}) {
95
+ const { store, pushToAgent } = deps;
96
+ const normalizedRequestId = normalizeOptionalText(requestId);
97
+ if (!normalizedRequestId) return null;
98
+ const request = store.getChatRequest(normalizedRequestId);
99
+ if (!request) return null;
100
+
101
+ request.kickoff = normalizeAcceptedChatKickoffRecord({
102
+ ...(request.kickoff && typeof request.kickoff === 'object' && !Array.isArray(request.kickoff) ? request.kickoff : {}),
103
+ status: 'failed',
104
+ reason: normalizeOptionalText(reason) || 'accepted_chat_kickoff_failed',
105
+ ...(normalizeOptionalText(turnId) ? { turnId: normalizeOptionalText(turnId) } : {}),
106
+ ...(normalizeOptionalText(conversationKey) ? { conversationKey: normalizeOptionalText(conversationKey) } : {}),
107
+ failedAt: store.now(),
108
+ });
109
+ if (store.markChatRequestUpdated) {
110
+ await store.markChatRequestUpdated();
111
+ }
112
+ await pushToAgent(request.fromAgentId, 'request.updated', request);
113
+ await pushToAgent(request.toAgentId, 'request.updated', request);
114
+ return request;
115
+ }
116
+
117
+ export async function markAcceptedChatKickoffProgressWithDeps(deps, {
118
+ requestId = null,
119
+ status = null,
120
+ reason = null,
121
+ turnId = null,
122
+ deliveryId = null,
123
+ conversationKey = null,
124
+ senderKickoffDeliveredAt = null,
125
+ openerAcceptedAt = null,
126
+ openerDeliveredAt = null,
127
+ liveChatEstablishedAt = null,
128
+ openerTurnId = null,
129
+ openerDeliveryId = null,
130
+ } = {}) {
131
+ const { store, pushToAgent } = deps;
132
+ const normalizedRequestId = normalizeOptionalText(requestId);
133
+ if (!normalizedRequestId) return null;
134
+ const request = store.getChatRequest(normalizedRequestId);
135
+ if (!request) return null;
136
+
137
+ request.kickoff = normalizeAcceptedChatKickoffRecord({
138
+ ...(request.kickoff && typeof request.kickoff === 'object' && !Array.isArray(request.kickoff) ? request.kickoff : {}),
139
+ ...(normalizeOptionalText(status) ? { status: normalizeOptionalText(status) } : {}),
140
+ ...(normalizeOptionalText(reason) ? { reason: normalizeOptionalText(reason) } : {}),
141
+ ...(normalizeOptionalText(turnId) ? { turnId: normalizeOptionalText(turnId) } : {}),
142
+ ...(normalizeOptionalText(deliveryId) ? { deliveryId: normalizeOptionalText(deliveryId) } : {}),
143
+ ...(normalizeOptionalText(conversationKey) ? { conversationKey: normalizeOptionalText(conversationKey) } : {}),
144
+ ...(normalizeOptionalText(senderKickoffDeliveredAt)
145
+ ? {
146
+ senderKickoffDeliveredAt: normalizeOptionalText(senderKickoffDeliveredAt),
147
+ deliveredAt: normalizeOptionalText(senderKickoffDeliveredAt),
148
+ }
149
+ : {}),
150
+ ...(normalizeOptionalText(openerAcceptedAt) ? { openerAcceptedAt: normalizeOptionalText(openerAcceptedAt) } : {}),
151
+ ...(normalizeOptionalText(openerDeliveredAt) ? { openerDeliveredAt: normalizeOptionalText(openerDeliveredAt) } : {}),
152
+ ...(normalizeOptionalText(liveChatEstablishedAt) ? { liveChatEstablishedAt: normalizeOptionalText(liveChatEstablishedAt) } : {}),
153
+ ...(normalizeOptionalText(openerTurnId) ? { openerTurnId: normalizeOptionalText(openerTurnId) } : {}),
154
+ ...(normalizeOptionalText(openerDeliveryId) ? { openerDeliveryId: normalizeOptionalText(openerDeliveryId) } : {}),
155
+ });
156
+ if (store.markChatRequestUpdated) {
157
+ await store.markChatRequestUpdated();
158
+ }
159
+ await pushToAgent(request.fromAgentId, 'request.updated', request);
160
+ await pushToAgent(request.toAgentId, 'request.updated', request);
161
+ return request;
162
+ }
@@ -0,0 +1,30 @@
1
+ export function normalizePositiveInteger(value, fallback) {
2
+ const normalized = Number(value);
3
+ if (!Number.isFinite(normalized) || normalized <= 0) return fallback;
4
+ return Math.floor(normalized);
5
+ }
6
+
7
+ export function normalizeOptionalText(value) {
8
+ if (typeof value !== 'string') return null;
9
+ const normalized = value.trim();
10
+ return normalized || null;
11
+ }
12
+
13
+ export function cloneJsonObject(value) {
14
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
15
+ try {
16
+ const cloned = JSON.parse(JSON.stringify(value));
17
+ if (!cloned || typeof cloned !== 'object' || Array.isArray(cloned)) return null;
18
+ return cloned;
19
+ } catch {
20
+ return null;
21
+ }
22
+ }
23
+
24
+ export function buildFailureBody(reason, extras = {}) {
25
+ return {
26
+ error: reason,
27
+ reason,
28
+ ...extras,
29
+ };
30
+ }
@@ -1,4 +1,5 @@
1
1
  import { randomUUID } from 'node:crypto';
2
+ import claworldPackageJson from '../../../package.json' with { type: 'json' };
2
3
 
3
4
  import {
4
5
  applyRuntimeIdentity,
@@ -57,6 +58,8 @@ import {
57
58
  } from '../../lib/runtime-errors.js';
58
59
  import { PUBLIC_IDENTITY_STATUS } from '../../lib/public-identity.js';
59
60
 
61
+ const CLAWORLD_PLUGIN_VERSION = claworldPackageJson.version;
62
+
60
63
  function normalizeRelayHttpBaseUrl(serverUrl) {
61
64
  const parsed = new URL(serverUrl);
62
65
  if (parsed.protocol === 'ws:') parsed.protocol = 'http:';
@@ -2450,7 +2453,7 @@ export function createClaworldChannelPlugin({
2450
2453
  return {
2451
2454
  ok: true,
2452
2455
  pluginId: 'claworld',
2453
- version: '0.3.0',
2456
+ version: CLAWORLD_PLUGIN_VERSION,
2454
2457
  defaultAccountId: null,
2455
2458
  accounts: accountSnapshots,
2456
2459
  relayClients: Object.fromEntries(
@@ -2599,7 +2602,7 @@ async function generateRuntimeProfileCard(context = {}) {
2599
2602
  docsPath: '/channels/claworld',
2600
2603
  docsLabel: 'claworld',
2601
2604
  blurb: 'Claworld relay channel backed by the Claworld backend.',
2602
- version: '0.3.0',
2605
+ version: CLAWORLD_PLUGIN_VERSION,
2603
2606
  forceAccountBinding: true,
2604
2607
  },
2605
2608
  onboarding: claworldOnboardingAdapter,
@@ -1,3 +1,5 @@
1
+ import { normalizeAcceptedChatKickoffRecord } from '../../lib/relay/kickoff-progress.js';
2
+
1
3
  function normalizeText(value, fallback = null) {
2
4
  if (value == null) return fallback;
3
5
  const normalized = String(value).trim();
@@ -437,15 +439,19 @@ function normalizeConversationScopeDetails(input = {}) {
437
439
  }
438
440
 
439
441
  function projectChatRequestKickoff(kickoff = {}) {
440
- if (!kickoff || typeof kickoff !== 'object') return null;
442
+ const normalizedKickoff = normalizeAcceptedChatKickoffRecord(kickoff, { fallbackStatus: 'skipped' });
443
+ if (!normalizedKickoff) return null;
441
444
  return {
442
- status: normalizeText(kickoff.status, 'skipped'),
443
- deliveredAt: normalizeText(kickoff.deliveredAt, null),
444
- senderKickoffDeliveredAt: normalizeText(kickoff.senderKickoffDeliveredAt, normalizeText(kickoff.deliveredAt, null)),
445
- openerAcceptedAt: normalizeText(kickoff.openerAcceptedAt, null),
446
- openerDeliveredAt: normalizeText(kickoff.openerDeliveredAt, null),
447
- liveChatEstablishedAt: normalizeText(kickoff.liveChatEstablishedAt, null),
448
- reason: normalizeText(kickoff.reason, null),
445
+ status: normalizeText(normalizedKickoff.status, 'skipped'),
446
+ deliveredAt: normalizeText(normalizedKickoff.deliveredAt, null),
447
+ senderKickoffDeliveredAt: normalizeText(
448
+ normalizedKickoff.senderKickoffDeliveredAt,
449
+ normalizeText(normalizedKickoff.deliveredAt, null),
450
+ ),
451
+ openerAcceptedAt: normalizeText(normalizedKickoff.openerAcceptedAt, null),
452
+ openerDeliveredAt: normalizeText(normalizedKickoff.openerDeliveredAt, null),
453
+ liveChatEstablishedAt: normalizeText(normalizedKickoff.liveChatEstablishedAt, null),
454
+ reason: normalizeText(normalizedKickoff.reason, null),
449
455
  };
450
456
  }
451
457