@volter-ai-dev/supercode-ui 0.1.66 → 0.1.68
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/activity.mjs +249 -4
- package/components.d.ts +3 -0
- package/components.mjs +382 -14
- package/composer.mjs +18 -3
- package/controller.mjs +5 -0
- package/conversation.mjs +42 -3
- package/core.d.ts +13 -0
- package/core.mjs +390 -1
- package/embed.mjs +377 -13
- package/index.d.ts +159 -0
- package/messenger.mjs +377 -13
- package/package.json +1 -1
- package/react/activity.mjs +249 -4
- package/react/components.mjs +382 -14
- package/react/composer.mjs +18 -3
- package/react/conversation.mjs +42 -3
- package/react/messenger.mjs +377 -13
- package/react/sessions.mjs +68 -4
- package/react/settings.mjs +10 -1
- package/react/subagents.mjs +42 -3
- package/sessions.mjs +68 -4
- package/settings.mjs +10 -1
- package/styles.css +11 -1
- package/subagents.mjs +42 -3
package/conversation.mjs
CHANGED
|
@@ -33,6 +33,10 @@ var EMPTY_UI_STATE = Object.freeze({
|
|
|
33
33
|
harness: "",
|
|
34
34
|
mode: "none",
|
|
35
35
|
strategy: null,
|
|
36
|
+
participant: Object.freeze({ kind: "local-user", label: null, origin: null }),
|
|
37
|
+
workspaceRef: Object.freeze({ kind: "none", value: null }),
|
|
38
|
+
mirror: null,
|
|
39
|
+
holder: null,
|
|
36
40
|
canSend: false,
|
|
37
41
|
canSteer: false,
|
|
38
42
|
canResume: false,
|
|
@@ -70,7 +74,12 @@ var EMPTY_UI_STATE = Object.freeze({
|
|
|
70
74
|
subagentInspector: null,
|
|
71
75
|
attached: null,
|
|
72
76
|
owned: null,
|
|
73
|
-
attachError: null
|
|
77
|
+
attachError: null,
|
|
78
|
+
agentPackage: null,
|
|
79
|
+
contributions: Object.freeze([]),
|
|
80
|
+
agentPackageError: null,
|
|
81
|
+
agentPackageCapabilityState: null,
|
|
82
|
+
agentPackageCapabilityError: null
|
|
74
83
|
});
|
|
75
84
|
var TOOL_CATEGORIES = /* @__PURE__ */ new Set(["read", "search", "edit", "command", "test", "web", "agent", "plan", "other"]);
|
|
76
85
|
var MAX_TOOL_FIELDS = 8;
|
|
@@ -530,7 +539,11 @@ function languageLabel(info) {
|
|
|
530
539
|
}
|
|
531
540
|
function frameCode(render, tokens, index, options, env, self) {
|
|
532
541
|
const label = markdown.utils.escapeHtml(languageLabel(tokens[index]?.info ?? ""));
|
|
533
|
-
|
|
542
|
+
const body = render(tokens, index, options, env, self).replace(
|
|
543
|
+
"<pre>",
|
|
544
|
+
`<pre tabindex="0" role="region" aria-label="${label} code">`
|
|
545
|
+
);
|
|
546
|
+
return `<div class="scui-code-block"><div class="scui-code-head"><span>${label}</span><button class="scui-code-copy" type="button" aria-label="Copy code" title="Copy code">${COPY_ICON}<span>Copy</span></button></div>${body}</div>`;
|
|
534
547
|
}
|
|
535
548
|
for (const kind of ["fence", "code_block"]) {
|
|
536
549
|
const render = markdown.renderer.rules[kind];
|
|
@@ -588,6 +601,12 @@ function Markdown({ value, copyText }) {
|
|
|
588
601
|
|
|
589
602
|
// src/memory.js
|
|
590
603
|
var MEMORY_LIMIT = 100;
|
|
604
|
+
var registry = /* @__PURE__ */ new Set();
|
|
605
|
+
function uiMemory() {
|
|
606
|
+
const map = /* @__PURE__ */ new Map();
|
|
607
|
+
registry.add(map);
|
|
608
|
+
return map;
|
|
609
|
+
}
|
|
591
610
|
function boundedSet(map, key, value) {
|
|
592
611
|
map.delete(key);
|
|
593
612
|
map.set(key, value);
|
|
@@ -1091,6 +1110,26 @@ function TechnicalDetails({ entry }) {
|
|
|
1091
1110
|
] });
|
|
1092
1111
|
}
|
|
1093
1112
|
function TranscriptEntry({ entry, state, adapter }) {
|
|
1113
|
+
if (entry.role === "opaque") {
|
|
1114
|
+
return /* @__PURE__ */ jsxs3("article", { className: "scui-message scui-opaque", "data-role": "opaque", "data-kind": entry.kind, "aria-label": "Unrecognized entry", children: [
|
|
1115
|
+
/* @__PURE__ */ jsxs3("div", { className: "scui-notice", "data-code": "opaque-entry", children: [
|
|
1116
|
+
"Unrecognized entry kind ",
|
|
1117
|
+
/* @__PURE__ */ jsx4("code", { children: entry.kind }),
|
|
1118
|
+
" \u2014 kept as-is, nothing dropped"
|
|
1119
|
+
] }),
|
|
1120
|
+
entry.text ? /* @__PURE__ */ jsx4(Markdown, { value: entry.text, copyText: adapter?.copyText }) : null,
|
|
1121
|
+
entry.raw ? /* @__PURE__ */ jsxs3("details", { className: "scui-tool-technical", children: [
|
|
1122
|
+
/* @__PURE__ */ jsx4("summary", { children: "Technical details" }),
|
|
1123
|
+
/* @__PURE__ */ jsx4("div", { children: /* @__PURE__ */ jsxs3("section", { children: [
|
|
1124
|
+
/* @__PURE__ */ jsx4("strong", { children: "Raw entry" }),
|
|
1125
|
+
/* @__PURE__ */ jsxs3("pre", { children: [
|
|
1126
|
+
entry.raw,
|
|
1127
|
+
entry.truncated ? "\n[truncated]" : ""
|
|
1128
|
+
] })
|
|
1129
|
+
] }) })
|
|
1130
|
+
] }) : null
|
|
1131
|
+
] });
|
|
1132
|
+
}
|
|
1094
1133
|
if (entry.role === "request") return /* @__PURE__ */ jsx4(RequestCard, { entry, adapter, canRespond: state.canRespond });
|
|
1095
1134
|
if (entry.role === "reasoning") {
|
|
1096
1135
|
return /* @__PURE__ */ jsxs3("details", { className: "scui-reasoning", open: entry.streaming, children: [
|
|
@@ -1220,7 +1259,7 @@ function SessionDetails({ semantics }) {
|
|
|
1220
1259
|
] })
|
|
1221
1260
|
] });
|
|
1222
1261
|
}
|
|
1223
|
-
var conversationMemory =
|
|
1262
|
+
var conversationMemory = uiMemory();
|
|
1224
1263
|
function ConversationAnnouncements({ state }) {
|
|
1225
1264
|
const previousBusy = useRef3(state.busy);
|
|
1226
1265
|
const [announcement, setAnnouncement] = useState2("");
|
package/core.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
export type {
|
|
2
2
|
AttachedSessionModel,
|
|
3
3
|
AgentActivityModel,
|
|
4
|
+
AgentContributionModel,
|
|
5
|
+
AgentPackageModel,
|
|
6
|
+
AgentPackageResourceModel,
|
|
7
|
+
AgentPackageResourceStateModel,
|
|
8
|
+
AgentPackageCapabilityStateModel,
|
|
4
9
|
ContinuationMode,
|
|
5
10
|
ControlStrategy,
|
|
6
11
|
HarnessId,
|
|
@@ -13,6 +18,10 @@ export type {
|
|
|
13
18
|
SessionMode,
|
|
14
19
|
SessionRowModel,
|
|
15
20
|
SessionSemanticsModel,
|
|
21
|
+
CrossSurfaceModel,
|
|
22
|
+
HarnessSessionDescriptorModel,
|
|
23
|
+
TriggerKind,
|
|
24
|
+
TriggerModel,
|
|
16
25
|
StartupPhase,
|
|
17
26
|
SupercodeUiIntent,
|
|
18
27
|
SupercodeUiState,
|
|
@@ -36,6 +45,7 @@ export {
|
|
|
36
45
|
groupConversation,
|
|
37
46
|
harnessDisplayName,
|
|
38
47
|
isSendKey,
|
|
48
|
+
normalizeContributions,
|
|
39
49
|
normalizeUiState,
|
|
40
50
|
operationLabel,
|
|
41
51
|
projectAgentActivity,
|
|
@@ -43,6 +53,9 @@ export {
|
|
|
43
53
|
relativeAge,
|
|
44
54
|
sessionActivity,
|
|
45
55
|
sessionDisplayName,
|
|
56
|
+
sessionRowsFromDescriptors,
|
|
57
|
+
surfaceLabel,
|
|
58
|
+
selectContributions,
|
|
46
59
|
terminalCommand,
|
|
47
60
|
toolCategory,
|
|
48
61
|
toolTarget,
|
package/core.mjs
CHANGED
|
@@ -30,6 +30,10 @@ export const EMPTY_UI_STATE = Object.freeze({
|
|
|
30
30
|
harness: '',
|
|
31
31
|
mode: 'none',
|
|
32
32
|
strategy: null,
|
|
33
|
+
participant: Object.freeze({ kind: 'local-user', label: null, origin: null }),
|
|
34
|
+
workspaceRef: Object.freeze({ kind: 'none', value: null }),
|
|
35
|
+
mirror: null,
|
|
36
|
+
holder: null,
|
|
33
37
|
canSend: false,
|
|
34
38
|
canSteer: false,
|
|
35
39
|
canResume: false,
|
|
@@ -68,6 +72,11 @@ export const EMPTY_UI_STATE = Object.freeze({
|
|
|
68
72
|
attached: null,
|
|
69
73
|
owned: null,
|
|
70
74
|
attachError: null,
|
|
75
|
+
agentPackage: null,
|
|
76
|
+
contributions: Object.freeze([]),
|
|
77
|
+
agentPackageError: null,
|
|
78
|
+
agentPackageCapabilityState: null,
|
|
79
|
+
agentPackageCapabilityError: null,
|
|
71
80
|
});
|
|
72
81
|
|
|
73
82
|
const ROLES = new Set(['system', 'user', 'assistant', 'tool', 'reasoning', 'request', 'notice']);
|
|
@@ -81,6 +90,10 @@ const AUTHENTICATION_PHASES = new Set(['idle', 'checking', 'required', 'configur
|
|
|
81
90
|
const MAX_TOOL_FIELDS = 8;
|
|
82
91
|
const MAX_TOOL_FIELD_CHARS = 800;
|
|
83
92
|
const MAX_TOOL_PREVIEW_CHARS = 4_000;
|
|
93
|
+
const MAX_CONTRIBUTIONS = 64;
|
|
94
|
+
const MAX_CONTRIBUTION_JSON_DEPTH = 12;
|
|
95
|
+
const MAX_CONTRIBUTION_COLLECTION = 100;
|
|
96
|
+
const MAX_CONTRIBUTION_STRING_CHARS = 16_000;
|
|
84
97
|
|
|
85
98
|
function record(value) {
|
|
86
99
|
return value !== null && typeof value === 'object' && !Array.isArray(value) ? value : null;
|
|
@@ -121,6 +134,165 @@ function nullableNumber(value) {
|
|
|
121
134
|
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
122
135
|
}
|
|
123
136
|
|
|
137
|
+
function relativeContributionPath(value) {
|
|
138
|
+
if (typeof value !== 'string' || value.startsWith('/') || /^[a-z]:[\\/]/i.test(value)) return null;
|
|
139
|
+
return value.replaceAll('\\', '/').split('/').some((segment) => segment === '..')
|
|
140
|
+
? null
|
|
141
|
+
: boundedString(value, 1_000);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function contributionJson(value, depth = 0, seen = new WeakSet()) {
|
|
145
|
+
if (value === null || typeof value === 'boolean' || typeof value === 'string') {
|
|
146
|
+
return typeof value === 'string' ? boundedString(value, MAX_CONTRIBUTION_STRING_CHARS) : value;
|
|
147
|
+
}
|
|
148
|
+
if (typeof value === 'number') return Number.isFinite(value) ? value : undefined;
|
|
149
|
+
if (depth >= MAX_CONTRIBUTION_JSON_DEPTH || !value || typeof value !== 'object' || seen.has(value)) {
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
seen.add(value);
|
|
153
|
+
if (Array.isArray(value)) {
|
|
154
|
+
const result = [];
|
|
155
|
+
for (const item of value.slice(0, MAX_CONTRIBUTION_COLLECTION)) {
|
|
156
|
+
const normalized = contributionJson(item, depth + 1, seen);
|
|
157
|
+
if (normalized !== undefined) result.push(normalized);
|
|
158
|
+
}
|
|
159
|
+
seen.delete(value);
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
const source = record(value);
|
|
163
|
+
if (!source) return undefined;
|
|
164
|
+
const result = {};
|
|
165
|
+
for (const [key, item] of Object.entries(source).slice(0, MAX_CONTRIBUTION_COLLECTION)) {
|
|
166
|
+
const normalized = contributionJson(item, depth + 1, seen);
|
|
167
|
+
if (normalized !== undefined) result[boundedString(key, 200)] = normalized;
|
|
168
|
+
}
|
|
169
|
+
seen.delete(value);
|
|
170
|
+
return result;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function normalizeContributions(value) {
|
|
174
|
+
if (!Array.isArray(value)) return [];
|
|
175
|
+
return value.slice(0, MAX_CONTRIBUTIONS).flatMap((candidate) => {
|
|
176
|
+
const item = record(candidate);
|
|
177
|
+
const placement = record(item?.placement);
|
|
178
|
+
const source = relativeContributionPath(item?.source);
|
|
179
|
+
if (
|
|
180
|
+
item?.schema !== 'supercode/contribution-v1' ||
|
|
181
|
+
typeof item.id !== 'string' || !item.id.trim() ||
|
|
182
|
+
typeof item.kind !== 'string' || !item.kind.trim() ||
|
|
183
|
+
source === null
|
|
184
|
+
) return [];
|
|
185
|
+
const data = contributionJson(item.data);
|
|
186
|
+
if (data === undefined) return [];
|
|
187
|
+
return [{
|
|
188
|
+
schema: 'supercode/contribution-v1',
|
|
189
|
+
id: boundedString(item.id, 200),
|
|
190
|
+
kind: boundedString(item.kind, 100),
|
|
191
|
+
...(typeof item.title === 'string' ? { title: boundedString(item.title, 500) } : {}),
|
|
192
|
+
...(placement ? { placement: {
|
|
193
|
+
...(typeof placement.surface === 'string' ? { surface: boundedString(placement.surface, 100) } : {}),
|
|
194
|
+
...(typeof placement.region === 'string' ? { region: boundedString(placement.region, 100) } : {}),
|
|
195
|
+
} } : {}),
|
|
196
|
+
data,
|
|
197
|
+
source,
|
|
198
|
+
}];
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function selectContributions(contributions, query = {}) {
|
|
203
|
+
return normalizeContributions(contributions).filter((contribution) => (
|
|
204
|
+
(query.kind === undefined || contribution.kind === query.kind) &&
|
|
205
|
+
(query.surface === undefined || contribution.placement?.surface === query.surface) &&
|
|
206
|
+
(query.region === undefined || contribution.placement?.region === query.region)
|
|
207
|
+
));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function readAgentPackage(value) {
|
|
211
|
+
const agentPackage = record(value);
|
|
212
|
+
const capabilities = record(agentPackage?.capabilities);
|
|
213
|
+
const storage = record(agentPackage?.storage);
|
|
214
|
+
const relativePath = relativeContributionPath(storage?.relativePath);
|
|
215
|
+
if (
|
|
216
|
+
agentPackage?.schemaVersion !== 1 ||
|
|
217
|
+
typeof agentPackage.id !== 'string' ||
|
|
218
|
+
typeof agentPackage.name !== 'string' ||
|
|
219
|
+
typeof agentPackage.version !== 'string' ||
|
|
220
|
+
!capabilities ||
|
|
221
|
+
!Array.isArray(capabilities.required) ||
|
|
222
|
+
!Array.isArray(capabilities.optional) ||
|
|
223
|
+
!storage ||
|
|
224
|
+
relativePath === null
|
|
225
|
+
) return null;
|
|
226
|
+
const resources = record(agentPackage.resources);
|
|
227
|
+
return {
|
|
228
|
+
id: boundedString(agentPackage.id, 200),
|
|
229
|
+
name: boundedString(agentPackage.name, 500),
|
|
230
|
+
version: boundedString(agentPackage.version, 100),
|
|
231
|
+
description: typeof agentPackage.description === 'string'
|
|
232
|
+
? boundedString(agentPackage.description, 2_000)
|
|
233
|
+
: null,
|
|
234
|
+
capabilities: {
|
|
235
|
+
required: capabilities.required.filter((item) => typeof item === 'string').slice(0, 100).map((item) => boundedString(item, 200)),
|
|
236
|
+
optional: capabilities.optional.filter((item) => typeof item === 'string').slice(0, 100).map((item) => boundedString(item, 200)),
|
|
237
|
+
},
|
|
238
|
+
storage: { relativePath },
|
|
239
|
+
resources: resources ? Object.fromEntries(Object.entries(resources).flatMap(([id, candidate]) => {
|
|
240
|
+
const state = record(candidate);
|
|
241
|
+
if (state?.schema !== 'supercode/package-resource-state-v1') return [];
|
|
242
|
+
if (state.status === 'absent' && typeof state.contributionId === 'string') {
|
|
243
|
+
return [[boundedString(id, 200), {
|
|
244
|
+
schema: 'supercode/package-resource-state-v1',
|
|
245
|
+
status: 'absent',
|
|
246
|
+
contributionId: boundedString(state.contributionId, 200),
|
|
247
|
+
}]];
|
|
248
|
+
}
|
|
249
|
+
if (state.status === 'error' && typeof state.contributionId === 'string' && typeof state.message === 'string') {
|
|
250
|
+
return [[boundedString(id, 200), {
|
|
251
|
+
schema: 'supercode/package-resource-state-v1',
|
|
252
|
+
status: 'error',
|
|
253
|
+
contributionId: boundedString(state.contributionId, 200),
|
|
254
|
+
message: boundedString(state.message),
|
|
255
|
+
}]];
|
|
256
|
+
}
|
|
257
|
+
const resource = record(state?.resource);
|
|
258
|
+
const data = contributionJson(resource?.data);
|
|
259
|
+
if (
|
|
260
|
+
state?.status !== 'ready' ||
|
|
261
|
+
resource?.schema !== 'supercode/package-resource-v1' ||
|
|
262
|
+
typeof resource.contributionId !== 'string' ||
|
|
263
|
+
!['json', 'text'].includes(resource.format) ||
|
|
264
|
+
typeof resource.mediaType !== 'string' ||
|
|
265
|
+
data === undefined
|
|
266
|
+
) return [];
|
|
267
|
+
return [[boundedString(id, 200), {
|
|
268
|
+
schema: 'supercode/package-resource-state-v1',
|
|
269
|
+
status: 'ready',
|
|
270
|
+
resource: {
|
|
271
|
+
schema: 'supercode/package-resource-v1',
|
|
272
|
+
contributionId: boundedString(resource.contributionId, 200),
|
|
273
|
+
format: resource.format,
|
|
274
|
+
mediaType: boundedString(resource.mediaType, 200),
|
|
275
|
+
data,
|
|
276
|
+
},
|
|
277
|
+
}]];
|
|
278
|
+
})) : {},
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function readAgentPackageCapabilityState(value) {
|
|
283
|
+
const state = record(value);
|
|
284
|
+
if (!state) return null;
|
|
285
|
+
const fields = ['availableRequired', 'missingRequired', 'enabledOptional', 'unavailableOptional'];
|
|
286
|
+
if (!fields.every((field) => Array.isArray(state[field]))) return null;
|
|
287
|
+
return {
|
|
288
|
+
ready: state.ready === true,
|
|
289
|
+
...Object.fromEntries(fields.map((field) => [
|
|
290
|
+
field,
|
|
291
|
+
state[field].filter((item) => typeof item === 'string').slice(0, 100).map((item) => boundedString(item, 200)),
|
|
292
|
+
])),
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
124
296
|
export function relativeAge(updatedAt, now = Date.now()) {
|
|
125
297
|
if (typeof updatedAt !== 'number' || !Number.isFinite(updatedAt) || updatedAt <= 0) return '';
|
|
126
298
|
const delta = Math.max(0, now - updatedAt);
|
|
@@ -504,12 +676,58 @@ function readToolPresentation(value, entry) {
|
|
|
504
676
|
};
|
|
505
677
|
}
|
|
506
678
|
|
|
679
|
+
// Fast-churning upstreams will emit entry kinds this messenger has never
|
|
680
|
+
// seen. Dropping them silently misrepresents the session, so an addressable
|
|
681
|
+
// entry (a record with a string id) whose role or shape is unrecognized is
|
|
682
|
+
// kept as a safe OPAQUE entry: the original kind is preserved as a label and
|
|
683
|
+
// the raw payload stays available under technical details (UNI-13, the
|
|
684
|
+
// messenger counterpart of the TUI's opaque-line rule). Only an entry with no
|
|
685
|
+
// usable identity is still skipped — it cannot be keyed stably across
|
|
686
|
+
// re-renders.
|
|
687
|
+
const OPAQUE_RAW_CHARS = 4_000;
|
|
688
|
+
|
|
689
|
+
function opaqueEntry(item) {
|
|
690
|
+
// An entry that is ALREADY opaque re-normalizes to itself (state flows
|
|
691
|
+
// through normalization more than once: host fixtures, the messenger's own
|
|
692
|
+
// read, mirrors), so the original kind and raw capture must survive
|
|
693
|
+
// instead of being re-wrapped under kind "opaque".
|
|
694
|
+
const previouslyOpaque = item.role === 'opaque';
|
|
695
|
+
let raw;
|
|
696
|
+
if (previouslyOpaque && typeof item.raw === 'string') {
|
|
697
|
+
raw = item.raw;
|
|
698
|
+
} else {
|
|
699
|
+
try {
|
|
700
|
+
raw = JSON.stringify(item, null, 2) ?? '';
|
|
701
|
+
} catch {
|
|
702
|
+
raw = '[unserializable entry payload]';
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
const kind = previouslyOpaque && typeof item.kind === 'string'
|
|
706
|
+
? item.kind
|
|
707
|
+
: typeof item.role === 'string'
|
|
708
|
+
? item.role
|
|
709
|
+
: '';
|
|
710
|
+
return {
|
|
711
|
+
id: item.id,
|
|
712
|
+
role: 'opaque',
|
|
713
|
+
kind: boundedString(kind, 120) || 'unknown',
|
|
714
|
+
text: typeof item.text === 'string' ? boundedString(item.text, OPAQUE_RAW_CHARS) : '',
|
|
715
|
+
ts: nullableNumber(item.ts),
|
|
716
|
+
truncated: item.truncated === true || raw.length > OPAQUE_RAW_CHARS,
|
|
717
|
+
raw: boundedString(raw, OPAQUE_RAW_CHARS),
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
|
|
507
721
|
function readTranscript(value) {
|
|
508
722
|
if (!Array.isArray(value)) return [];
|
|
509
723
|
const result = [];
|
|
510
724
|
for (const candidate of value) {
|
|
511
725
|
const item = record(candidate);
|
|
512
|
-
if (!item || typeof item.id !== 'string'
|
|
726
|
+
if (!item || typeof item.id !== 'string') continue;
|
|
727
|
+
if (typeof item.text !== 'string' || !ROLES.has(item.role)) {
|
|
728
|
+
result.push(opaqueEntry(item));
|
|
729
|
+
continue;
|
|
730
|
+
}
|
|
513
731
|
const entry = {
|
|
514
732
|
id: item.id,
|
|
515
733
|
role: item.role,
|
|
@@ -574,6 +792,105 @@ function readTranscript(value) {
|
|
|
574
792
|
return result;
|
|
575
793
|
}
|
|
576
794
|
|
|
795
|
+
// ---- UNI-8..UNI-12: the universal-layer UI nouns ---------------------------
|
|
796
|
+
|
|
797
|
+
/// UNI-8: WHOSE conversation a session is. The messenger's historical implicit
|
|
798
|
+
/// model is you-and-agent-in-a-workspace; Hermes/OpenClaw channel sessions are
|
|
799
|
+
/// someone-else-and-agent. `kind` is the honest tri-state; `label` is the
|
|
800
|
+
/// human ("@jane"), `origin` the surface ("slack", "telegram", "acp").
|
|
801
|
+
function readParticipant(value) {
|
|
802
|
+
const participant = record(value);
|
|
803
|
+
if (!participant) return { kind: 'local-user', label: null, origin: null };
|
|
804
|
+
return {
|
|
805
|
+
kind: ['local-user', 'foreign', 'unknown'].includes(participant.kind) ? participant.kind : 'unknown',
|
|
806
|
+
label: typeof participant.label === 'string' && participant.label ? boundedString(participant.label, 200) : null,
|
|
807
|
+
origin: typeof participant.origin === 'string' && participant.origin ? boundedString(participant.origin, 100) : null,
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
/// UNI-9: a TYPED workspace. `repo` carries a path, `channel` carries a
|
|
812
|
+
/// channel label, `none` is a first-class value (Hermes assistant chats) —
|
|
813
|
+
/// not an empty string pretending to be a path.
|
|
814
|
+
function readWorkspaceRef(value, fallbackPath = '') {
|
|
815
|
+
const ref = record(value);
|
|
816
|
+
if (ref && ['repo', 'none', 'channel'].includes(ref.kind)) {
|
|
817
|
+
return {
|
|
818
|
+
kind: ref.kind,
|
|
819
|
+
value: ref.kind === 'none' ? null : typeof ref.value === 'string' && ref.value ? boundedString(ref.value, 500) : null,
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
// Legacy hosts send only the string path.
|
|
823
|
+
return fallbackPath
|
|
824
|
+
? { kind: 'repo', value: boundedString(fallbackPath, 500) }
|
|
825
|
+
: { kind: 'none', value: null };
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
/// UNI-10: canonical-elsewhere. A bounded/truncated MIRROR of a session whose
|
|
829
|
+
/// canonical record lives in another store (OpenClaw supervising codex, ...).
|
|
830
|
+
function readMirror(value) {
|
|
831
|
+
const mirror = record(value);
|
|
832
|
+
if (!mirror || typeof mirror.canonicalHarness !== 'string' || !mirror.canonicalHarness) return null;
|
|
833
|
+
return {
|
|
834
|
+
canonicalHarness: boundedString(mirror.canonicalHarness, 100),
|
|
835
|
+
canonicalKey: typeof mirror.canonicalKey === 'string' && mirror.canonicalKey ? boundedString(mirror.canonicalKey, 300) : null,
|
|
836
|
+
bounded: mirror.bounded === true,
|
|
837
|
+
truncated: mirror.truncated === true,
|
|
838
|
+
origin: typeof mirror.origin === 'string' && mirror.origin ? boundedString(mirror.origin, 200) : null,
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/// UNI-11: which surface HOLDS this session now. `surface: 'supercode'` =
|
|
843
|
+
/// held here (writable path); any other string = held by that surface
|
|
844
|
+
/// (take-over is the transition, behind confirmIntent); null holder = unheld.
|
|
845
|
+
function readHolder(value) {
|
|
846
|
+
const holder = record(value);
|
|
847
|
+
if (!holder) return null;
|
|
848
|
+
const surface = typeof holder.surface === 'string' && holder.surface ? boundedString(holder.surface, 100) : null;
|
|
849
|
+
return {
|
|
850
|
+
surface,
|
|
851
|
+
canTakeOver: holder.canTakeOver === true && surface !== null && surface !== 'supercode',
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
/// UNI-12/ORCH-6: WHY a session exists. Trigger provenance for unattended
|
|
856
|
+
/// work, over the server's own `Trigger` enum. `manual` is the pre-ORCH-6
|
|
857
|
+
/// spelling of `human` and stays accepted so existing hosts keep working.
|
|
858
|
+
const TRIGGER_KINDS = ['human', 'channel', 'cron', 'heartbeat', 'webhook', 'parent', 'api', 'unknown'];
|
|
859
|
+
function readTrigger(value) {
|
|
860
|
+
const trigger = record(value);
|
|
861
|
+
if (!trigger) return null;
|
|
862
|
+
const kind = trigger.kind === 'manual' ? 'human' : trigger.kind;
|
|
863
|
+
return {
|
|
864
|
+
kind: TRIGGER_KINDS.includes(kind) ? kind : 'unknown',
|
|
865
|
+
label: typeof trigger.label === 'string' && trigger.label ? boundedString(trigger.label, 200) : null,
|
|
866
|
+
surface: typeof trigger.surface === 'string' && trigger.surface ? boundedString(trigger.surface, 300) : null,
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
/// ORCH-6: a conversation that moved to another surface (Hermes `handoff_*`).
|
|
871
|
+
function readCrossSurface(value) {
|
|
872
|
+
const moved = record(value);
|
|
873
|
+
if (!moved || typeof moved.state !== 'string' || !moved.state) return null;
|
|
874
|
+
return {
|
|
875
|
+
state: boundedString(moved.state, 100),
|
|
876
|
+
platform: typeof moved.platform === 'string' && moved.platform ? boundedString(moved.platform, 100) : null,
|
|
877
|
+
error: typeof moved.error === 'string' && moved.error ? boundedString(moved.error, 500) : null,
|
|
878
|
+
};
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
/// UNI-12: recurring-run grouping. A row that REPRESENTS a group of runs of
|
|
882
|
+
/// the same recurring trigger, so the inventory shows one line, not N.
|
|
883
|
+
function readRecurring(value) {
|
|
884
|
+
const recurring = record(value);
|
|
885
|
+
if (!recurring || typeof recurring.groupKey !== 'string' || !recurring.groupKey) return null;
|
|
886
|
+
const runs = Number.isSafeInteger(recurring.runs) && recurring.runs > 0 ? recurring.runs : 1;
|
|
887
|
+
return {
|
|
888
|
+
groupKey: boundedString(recurring.groupKey, 300),
|
|
889
|
+
runs,
|
|
890
|
+
lastStatus: ['ok', 'failed', 'mixed'].includes(recurring.lastStatus) ? recurring.lastStatus : null,
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
|
|
577
894
|
function readSessions(value) {
|
|
578
895
|
if (!Array.isArray(value)) return [];
|
|
579
896
|
return value.flatMap((raw) => {
|
|
@@ -596,6 +913,12 @@ function readSessions(value) {
|
|
|
596
913
|
runtimeStatus: row.runtimeStatus === 'running' || row.runtimeStatus === 'busy' || row.runtimeStatus === 'idle' ? row.runtimeStatus : null,
|
|
597
914
|
...(Number.isSafeInteger(row.subagentCount) && row.subagentCount > 0 ? { subagentCount: row.subagentCount } : {}),
|
|
598
915
|
activity: ACTIVITY_PRIORITY[row.activity] !== undefined ? row.activity : undefined,
|
|
916
|
+
...(row.participant !== undefined ? { participant: readParticipant(row.participant) } : {}),
|
|
917
|
+
workspaceRef: readWorkspaceRef(row.workspaceRef, string(row.cwd)),
|
|
918
|
+
...(readMirror(row.mirror) ? { mirror: readMirror(row.mirror) } : {}),
|
|
919
|
+
...(readTrigger(row.trigger) ? { trigger: readTrigger(row.trigger) } : {}),
|
|
920
|
+
...(readRecurring(row.recurring) ? { recurring: readRecurring(row.recurring) } : {}),
|
|
921
|
+
...(readCrossSurface(row.crossSurface) ? { crossSurface: readCrossSurface(row.crossSurface) } : {}),
|
|
599
922
|
}];
|
|
600
923
|
});
|
|
601
924
|
}
|
|
@@ -836,6 +1159,10 @@ export function normalizeUiState(value) {
|
|
|
836
1159
|
canConfigureSettings: raw.canConfigureSettings === true,
|
|
837
1160
|
messaging: raw.messaging === 'live_peer' ? 'live_peer' : null,
|
|
838
1161
|
workspace: string(raw.workspace),
|
|
1162
|
+
workspaceRef: readWorkspaceRef(raw.workspaceRef, string(raw.workspace)),
|
|
1163
|
+
participant: readParticipant(raw.participant),
|
|
1164
|
+
mirror: readMirror(raw.mirror),
|
|
1165
|
+
holder: readHolder(raw.holder),
|
|
839
1166
|
taskPlan: readTaskPlan(raw.taskPlan),
|
|
840
1167
|
semantics: readSemantics(raw.semantics),
|
|
841
1168
|
terminalHandoff: readTerminalHandoff(raw.terminalHandoff),
|
|
@@ -896,6 +1223,13 @@ export function normalizeUiState(value) {
|
|
|
896
1223
|
attached: readAttached(raw.attached),
|
|
897
1224
|
owned: readAttached(raw.owned),
|
|
898
1225
|
attachError: attachError && typeof attachError.key === 'string' && typeof attachError.message === 'string' ? { key: attachError.key, message: attachError.message } : null,
|
|
1226
|
+
agentPackage: readAgentPackage(raw.agentPackage),
|
|
1227
|
+
contributions: normalizeContributions(raw.contributions),
|
|
1228
|
+
agentPackageError: typeof raw.agentPackageError === 'string' ? boundedString(raw.agentPackageError) : null,
|
|
1229
|
+
agentPackageCapabilityState: readAgentPackageCapabilityState(raw.agentPackageCapabilityState),
|
|
1230
|
+
agentPackageCapabilityError: typeof raw.agentPackageCapabilityError === 'string'
|
|
1231
|
+
? boundedString(raw.agentPackageCapabilityError)
|
|
1232
|
+
: null,
|
|
899
1233
|
};
|
|
900
1234
|
}
|
|
901
1235
|
|
|
@@ -903,6 +1237,61 @@ export function harnessDisplayName(id) {
|
|
|
903
1237
|
return HARNESS_NAMES[id] ?? id;
|
|
904
1238
|
}
|
|
905
1239
|
|
|
1240
|
+
/// ORCH-6: the compact surface key a person reads —
|
|
1241
|
+
/// `telegram:dm:123456`, `slack:channel:C1`, `acp`. Empty for a surface with
|
|
1242
|
+
/// nothing to say (a terminal session).
|
|
1243
|
+
export function surfaceLabel(value) {
|
|
1244
|
+
const surface = record(value);
|
|
1245
|
+
if (!surface) return '';
|
|
1246
|
+
const parts = [surface.platform, surface.kind, surface.chat_id, surface.thread_id]
|
|
1247
|
+
.filter((part) => typeof part === 'string' && part);
|
|
1248
|
+
return parts.length ? boundedString(parts.join(':'), 300) : '';
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
/// ORCH-6: project `harness.v1.sessions.discover` / `sessions.load` rows into
|
|
1252
|
+
/// list rows. This is the ONE client-side mapping from the service's wire
|
|
1253
|
+
/// shape to {@link SessionRowModel}: the conversation nouns
|
|
1254
|
+
/// (`trigger`/`surface`/`profile`/`recurrence`/`cross_surface`/`workspace`)
|
|
1255
|
+
/// are read straight off the server's derivation, never re-derived here.
|
|
1256
|
+
export function sessionRowsFromDescriptors(value) {
|
|
1257
|
+
if (!Array.isArray(value)) return [];
|
|
1258
|
+
return readSessions(value.flatMap((raw) => {
|
|
1259
|
+
const descriptor = record(raw);
|
|
1260
|
+
const locator = record(descriptor?.locator);
|
|
1261
|
+
if (!locator || typeof locator.harness !== 'string' || typeof locator.session_id !== 'string') return [];
|
|
1262
|
+
const surface = record(descriptor.surface);
|
|
1263
|
+
const label = surfaceLabel(surface);
|
|
1264
|
+
const cwd = typeof descriptor.cwd === 'string' ? descriptor.cwd : '';
|
|
1265
|
+
const title = typeof descriptor.title === 'string' ? descriptor.title : '';
|
|
1266
|
+
const recurrence = record(descriptor.recurrence);
|
|
1267
|
+
return [{
|
|
1268
|
+
key: `${locator.harness}:${locator.session_id}`,
|
|
1269
|
+
harness: locator.harness,
|
|
1270
|
+
name: label || cwd.split(/[\\/]/).filter(Boolean).pop() || locator.session_id,
|
|
1271
|
+
cwd,
|
|
1272
|
+
title,
|
|
1273
|
+
updatedAt: descriptor.updated_at_ms ?? null,
|
|
1274
|
+
messages: descriptor.message_count ?? null,
|
|
1275
|
+
active: false,
|
|
1276
|
+
writable: false,
|
|
1277
|
+
live: false,
|
|
1278
|
+
runtimeStatus: null,
|
|
1279
|
+
subagentCount: descriptor.child_session_count,
|
|
1280
|
+
workspaceRef: descriptor.workspace,
|
|
1281
|
+
trigger: { kind: descriptor.trigger ?? 'unknown', label: recurrence?.job_id ?? descriptor.profile ?? null, surface: label || null },
|
|
1282
|
+
// One fire is one row: supercode renders the grouping a harness
|
|
1283
|
+
// publishes, it never invents a run count it has not counted.
|
|
1284
|
+
...(recurrence?.job_id ? { recurring: { groupKey: recurrence.job_id, runs: 1, lastStatus: null } } : {}),
|
|
1285
|
+
...(descriptor.cross_surface ? { crossSurface: descriptor.cross_surface } : {}),
|
|
1286
|
+
// A channel surface names the person who reached the agent; a terminal
|
|
1287
|
+
// session has no platform and stays the local user.
|
|
1288
|
+
...(surface?.platform && surface?.participant_id
|
|
1289
|
+
? { participant: { kind: 'foreign', label: surface.participant_id, origin: surface.platform } }
|
|
1290
|
+
: {}),
|
|
1291
|
+
}];
|
|
1292
|
+
}));
|
|
1293
|
+
}
|
|
1294
|
+
|
|
906
1295
|
export function sessionDisplayName(session) {
|
|
907
1296
|
const title = session.title?.trim();
|
|
908
1297
|
return title && title !== session.name ? title : session.name || 'Untitled chat';
|