@volter-ai-dev/supercode-ui 0.1.65 → 0.1.67
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/README.md +5 -0
- package/activity.mjs +136 -2
- package/components.mjs +136 -2
- package/composer.mjs +6 -1
- package/controller.mjs +5 -0
- package/conversation.mjs +6 -1
- package/core.d.ts +7 -0
- package/core.mjs +175 -0
- package/embed.mjs +136 -2
- package/host.d.ts +29 -0
- package/host.mjs +128 -5
- package/index.d.ts +56 -0
- package/messenger.mjs +136 -2
- package/package.json +1 -1
- package/react/activity.mjs +136 -2
- package/react/components.mjs +136 -2
- package/react/composer.mjs +6 -1
- package/react/conversation.mjs +6 -1
- package/react/messenger.mjs +136 -2
- package/react/sessions.mjs +6 -1
- package/react/settings.mjs +6 -1
- package/react/subagents.mjs +6 -1
- package/sessions.mjs +6 -1
- package/settings.mjs +6 -1
- package/subagents.mjs +6 -1
package/embed.mjs
CHANGED
|
@@ -72,7 +72,12 @@ var EMPTY_UI_STATE = Object.freeze({
|
|
|
72
72
|
subagentInspector: null,
|
|
73
73
|
attached: null,
|
|
74
74
|
owned: null,
|
|
75
|
-
attachError: null
|
|
75
|
+
attachError: null,
|
|
76
|
+
agentPackage: null,
|
|
77
|
+
contributions: Object.freeze([]),
|
|
78
|
+
agentPackageError: null,
|
|
79
|
+
agentPackageCapabilityState: null,
|
|
80
|
+
agentPackageCapabilityError: null
|
|
76
81
|
});
|
|
77
82
|
var ROLES = /* @__PURE__ */ new Set(["system", "user", "assistant", "tool", "reasoning", "request", "notice"]);
|
|
78
83
|
var MODES = /* @__PURE__ */ new Set(["none", "control", "mirror"]);
|
|
@@ -85,6 +90,10 @@ var AUTHENTICATION_PHASES = /* @__PURE__ */ new Set(["idle", "checking", "requir
|
|
|
85
90
|
var MAX_TOOL_FIELDS = 8;
|
|
86
91
|
var MAX_TOOL_FIELD_CHARS = 800;
|
|
87
92
|
var MAX_TOOL_PREVIEW_CHARS = 4e3;
|
|
93
|
+
var MAX_CONTRIBUTIONS = 64;
|
|
94
|
+
var MAX_CONTRIBUTION_JSON_DEPTH = 12;
|
|
95
|
+
var MAX_CONTRIBUTION_COLLECTION = 100;
|
|
96
|
+
var MAX_CONTRIBUTION_STRING_CHARS = 16e3;
|
|
88
97
|
function record(value) {
|
|
89
98
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
90
99
|
}
|
|
@@ -117,6 +126,126 @@ function number(value, fallback = 0) {
|
|
|
117
126
|
function nullableNumber(value) {
|
|
118
127
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
119
128
|
}
|
|
129
|
+
function relativeContributionPath(value) {
|
|
130
|
+
if (typeof value !== "string" || value.startsWith("/") || /^[a-z]:[\\/]/i.test(value)) return null;
|
|
131
|
+
return value.replaceAll("\\", "/").split("/").some((segment) => segment === "..") ? null : boundedString(value, 1e3);
|
|
132
|
+
}
|
|
133
|
+
function contributionJson(value, depth = 0, seen = /* @__PURE__ */ new WeakSet()) {
|
|
134
|
+
if (value === null || typeof value === "boolean" || typeof value === "string") {
|
|
135
|
+
return typeof value === "string" ? boundedString(value, MAX_CONTRIBUTION_STRING_CHARS) : value;
|
|
136
|
+
}
|
|
137
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : void 0;
|
|
138
|
+
if (depth >= MAX_CONTRIBUTION_JSON_DEPTH || !value || typeof value !== "object" || seen.has(value)) {
|
|
139
|
+
return void 0;
|
|
140
|
+
}
|
|
141
|
+
seen.add(value);
|
|
142
|
+
if (Array.isArray(value)) {
|
|
143
|
+
const result2 = [];
|
|
144
|
+
for (const item of value.slice(0, MAX_CONTRIBUTION_COLLECTION)) {
|
|
145
|
+
const normalized = contributionJson(item, depth + 1, seen);
|
|
146
|
+
if (normalized !== void 0) result2.push(normalized);
|
|
147
|
+
}
|
|
148
|
+
seen.delete(value);
|
|
149
|
+
return result2;
|
|
150
|
+
}
|
|
151
|
+
const source = record(value);
|
|
152
|
+
if (!source) return void 0;
|
|
153
|
+
const result = {};
|
|
154
|
+
for (const [key, item] of Object.entries(source).slice(0, MAX_CONTRIBUTION_COLLECTION)) {
|
|
155
|
+
const normalized = contributionJson(item, depth + 1, seen);
|
|
156
|
+
if (normalized !== void 0) result[boundedString(key, 200)] = normalized;
|
|
157
|
+
}
|
|
158
|
+
seen.delete(value);
|
|
159
|
+
return result;
|
|
160
|
+
}
|
|
161
|
+
function normalizeContributions(value) {
|
|
162
|
+
if (!Array.isArray(value)) return [];
|
|
163
|
+
return value.slice(0, MAX_CONTRIBUTIONS).flatMap((candidate) => {
|
|
164
|
+
const item = record(candidate);
|
|
165
|
+
const placement = record(item?.placement);
|
|
166
|
+
const source = relativeContributionPath(item?.source);
|
|
167
|
+
if (item?.schema !== "supercode/contribution-v1" || typeof item.id !== "string" || !item.id.trim() || typeof item.kind !== "string" || !item.kind.trim() || source === null) return [];
|
|
168
|
+
const data = contributionJson(item.data);
|
|
169
|
+
if (data === void 0) return [];
|
|
170
|
+
return [{
|
|
171
|
+
schema: "supercode/contribution-v1",
|
|
172
|
+
id: boundedString(item.id, 200),
|
|
173
|
+
kind: boundedString(item.kind, 100),
|
|
174
|
+
...typeof item.title === "string" ? { title: boundedString(item.title, 500) } : {},
|
|
175
|
+
...placement ? { placement: {
|
|
176
|
+
...typeof placement.surface === "string" ? { surface: boundedString(placement.surface, 100) } : {},
|
|
177
|
+
...typeof placement.region === "string" ? { region: boundedString(placement.region, 100) } : {}
|
|
178
|
+
} } : {},
|
|
179
|
+
data,
|
|
180
|
+
source
|
|
181
|
+
}];
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
function readAgentPackage(value) {
|
|
185
|
+
const agentPackage = record(value);
|
|
186
|
+
const capabilities = record(agentPackage?.capabilities);
|
|
187
|
+
const storage = record(agentPackage?.storage);
|
|
188
|
+
const relativePath = relativeContributionPath(storage?.relativePath);
|
|
189
|
+
if (agentPackage?.schemaVersion !== 1 || typeof agentPackage.id !== "string" || typeof agentPackage.name !== "string" || typeof agentPackage.version !== "string" || !capabilities || !Array.isArray(capabilities.required) || !Array.isArray(capabilities.optional) || !storage || relativePath === null) return null;
|
|
190
|
+
const resources = record(agentPackage.resources);
|
|
191
|
+
return {
|
|
192
|
+
id: boundedString(agentPackage.id, 200),
|
|
193
|
+
name: boundedString(agentPackage.name, 500),
|
|
194
|
+
version: boundedString(agentPackage.version, 100),
|
|
195
|
+
description: typeof agentPackage.description === "string" ? boundedString(agentPackage.description, 2e3) : null,
|
|
196
|
+
capabilities: {
|
|
197
|
+
required: capabilities.required.filter((item) => typeof item === "string").slice(0, 100).map((item) => boundedString(item, 200)),
|
|
198
|
+
optional: capabilities.optional.filter((item) => typeof item === "string").slice(0, 100).map((item) => boundedString(item, 200))
|
|
199
|
+
},
|
|
200
|
+
storage: { relativePath },
|
|
201
|
+
resources: resources ? Object.fromEntries(Object.entries(resources).flatMap(([id, candidate]) => {
|
|
202
|
+
const state = record(candidate);
|
|
203
|
+
if (state?.schema !== "supercode/package-resource-state-v1") return [];
|
|
204
|
+
if (state.status === "absent" && typeof state.contributionId === "string") {
|
|
205
|
+
return [[boundedString(id, 200), {
|
|
206
|
+
schema: "supercode/package-resource-state-v1",
|
|
207
|
+
status: "absent",
|
|
208
|
+
contributionId: boundedString(state.contributionId, 200)
|
|
209
|
+
}]];
|
|
210
|
+
}
|
|
211
|
+
if (state.status === "error" && typeof state.contributionId === "string" && typeof state.message === "string") {
|
|
212
|
+
return [[boundedString(id, 200), {
|
|
213
|
+
schema: "supercode/package-resource-state-v1",
|
|
214
|
+
status: "error",
|
|
215
|
+
contributionId: boundedString(state.contributionId, 200),
|
|
216
|
+
message: boundedString(state.message)
|
|
217
|
+
}]];
|
|
218
|
+
}
|
|
219
|
+
const resource = record(state?.resource);
|
|
220
|
+
const data = contributionJson(resource?.data);
|
|
221
|
+
if (state?.status !== "ready" || resource?.schema !== "supercode/package-resource-v1" || typeof resource.contributionId !== "string" || !["json", "text"].includes(resource.format) || typeof resource.mediaType !== "string" || data === void 0) return [];
|
|
222
|
+
return [[boundedString(id, 200), {
|
|
223
|
+
schema: "supercode/package-resource-state-v1",
|
|
224
|
+
status: "ready",
|
|
225
|
+
resource: {
|
|
226
|
+
schema: "supercode/package-resource-v1",
|
|
227
|
+
contributionId: boundedString(resource.contributionId, 200),
|
|
228
|
+
format: resource.format,
|
|
229
|
+
mediaType: boundedString(resource.mediaType, 200),
|
|
230
|
+
data
|
|
231
|
+
}
|
|
232
|
+
}]];
|
|
233
|
+
})) : {}
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
function readAgentPackageCapabilityState(value) {
|
|
237
|
+
const state = record(value);
|
|
238
|
+
if (!state) return null;
|
|
239
|
+
const fields = ["availableRequired", "missingRequired", "enabledOptional", "unavailableOptional"];
|
|
240
|
+
if (!fields.every((field) => Array.isArray(state[field]))) return null;
|
|
241
|
+
return {
|
|
242
|
+
ready: state.ready === true,
|
|
243
|
+
...Object.fromEntries(fields.map((field) => [
|
|
244
|
+
field,
|
|
245
|
+
state[field].filter((item) => typeof item === "string").slice(0, 100).map((item) => boundedString(item, 200))
|
|
246
|
+
]))
|
|
247
|
+
};
|
|
248
|
+
}
|
|
120
249
|
function relativeAge(updatedAt, now = Date.now()) {
|
|
121
250
|
if (typeof updatedAt !== "number" || !Number.isFinite(updatedAt) || updatedAt <= 0) return "";
|
|
122
251
|
const delta = Math.max(0, now - updatedAt);
|
|
@@ -834,7 +963,12 @@ function normalizeUiState(value) {
|
|
|
834
963
|
subagentInspector: readSubagentInspector(raw.subagentInspector),
|
|
835
964
|
attached: readAttached(raw.attached),
|
|
836
965
|
owned: readAttached(raw.owned),
|
|
837
|
-
attachError: attachError && typeof attachError.key === "string" && typeof attachError.message === "string" ? { key: attachError.key, message: attachError.message } : null
|
|
966
|
+
attachError: attachError && typeof attachError.key === "string" && typeof attachError.message === "string" ? { key: attachError.key, message: attachError.message } : null,
|
|
967
|
+
agentPackage: readAgentPackage(raw.agentPackage),
|
|
968
|
+
contributions: normalizeContributions(raw.contributions),
|
|
969
|
+
agentPackageError: typeof raw.agentPackageError === "string" ? boundedString(raw.agentPackageError) : null,
|
|
970
|
+
agentPackageCapabilityState: readAgentPackageCapabilityState(raw.agentPackageCapabilityState),
|
|
971
|
+
agentPackageCapabilityError: typeof raw.agentPackageCapabilityError === "string" ? boundedString(raw.agentPackageCapabilityError) : null
|
|
838
972
|
};
|
|
839
973
|
}
|
|
840
974
|
function harnessDisplayName(id) {
|
package/host.d.ts
CHANGED
|
@@ -109,6 +109,35 @@ export function createNativeSessionAttentionTracker(options?: {
|
|
|
109
109
|
onChange?(state: NativeSessionAttentionTrackerState): void;
|
|
110
110
|
}): NativeSessionAttentionTracker;
|
|
111
111
|
|
|
112
|
+
export interface NativeMessengerStateSnapshot extends NativeSessionAttentionTrackerState {
|
|
113
|
+
drafts: Record<string, string>;
|
|
114
|
+
preferredLaunchModes: Record<string, 'headless' | 'terminal'>;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function normalizeNativeMessengerState(value: unknown): NativeMessengerStateSnapshot;
|
|
118
|
+
|
|
119
|
+
export class NativeMessengerState {
|
|
120
|
+
constructor(options?: {
|
|
121
|
+
state?: unknown;
|
|
122
|
+
onChange?(state: NativeMessengerStateSnapshot): void;
|
|
123
|
+
});
|
|
124
|
+
acknowledge(key: string): boolean;
|
|
125
|
+
observeAttention(options: NativeSessionAttentionObservation): {
|
|
126
|
+
attention: import('./index.js').SessionAttention[];
|
|
127
|
+
settleAfterMs: number | null;
|
|
128
|
+
};
|
|
129
|
+
draft(key: string): string;
|
|
130
|
+
setDraft(key: string, draft: string): boolean;
|
|
131
|
+
preferredLaunchMode(harness: string): 'headless' | 'terminal' | null;
|
|
132
|
+
setPreferredLaunchMode(harness: string, mode: 'headless' | 'terminal'): boolean;
|
|
133
|
+
snapshot(): NativeMessengerStateSnapshot;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function createNativeMessengerState(options?: {
|
|
137
|
+
state?: unknown;
|
|
138
|
+
onChange?(state: NativeMessengerStateSnapshot): void;
|
|
139
|
+
}): NativeMessengerState;
|
|
140
|
+
|
|
112
141
|
export interface RemoteUiBindingOptions
|
|
113
142
|
extends Pick<
|
|
114
143
|
UiAdapter,
|
package/host.mjs
CHANGED
|
@@ -3,11 +3,18 @@ import { dispatchControllerIntent, projectClientSnapshot } from './controller.mj
|
|
|
3
3
|
import { conversationPreviewText } from '@volter-ai-dev/supercode-client';
|
|
4
4
|
|
|
5
5
|
const FRAME_SCHEMA = 'supercode.ui-host-state.v1';
|
|
6
|
+
const NATIVE_STATE_LIMIT = 500;
|
|
6
7
|
|
|
7
8
|
function objectRecord(value) {
|
|
8
9
|
return value && typeof value === 'object' && !Array.isArray(value) ? value : null;
|
|
9
10
|
}
|
|
10
11
|
|
|
12
|
+
function setRecentBounded(map, key, value) {
|
|
13
|
+
map.delete(key);
|
|
14
|
+
map.set(key, value);
|
|
15
|
+
while (map.size > NATIVE_STATE_LIMIT) map.delete(map.keys().next().value);
|
|
16
|
+
}
|
|
17
|
+
|
|
11
18
|
function defaultInstanceId() {
|
|
12
19
|
if (typeof globalThis.crypto?.randomUUID === 'function') return globalThis.crypto.randomUUID();
|
|
13
20
|
return `host-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
@@ -325,10 +332,12 @@ export function createSessionAttentionTracker(options) {
|
|
|
325
332
|
function nativeAttentionState(value) {
|
|
326
333
|
const parsed = objectRecord(value);
|
|
327
334
|
const attention = Array.isArray(parsed?.attention)
|
|
328
|
-
? parsed.attention.slice(0,
|
|
335
|
+
? parsed.attention.slice(0, NATIVE_STATE_LIMIT).flatMap((candidate) => {
|
|
329
336
|
const item = objectRecord(candidate);
|
|
330
337
|
if (
|
|
331
338
|
typeof item?.key !== 'string' ||
|
|
339
|
+
!item.key ||
|
|
340
|
+
item.key.length > 200 ||
|
|
332
341
|
!['unseen', 'finished', 'failed'].includes(item.kind)
|
|
333
342
|
) return [];
|
|
334
343
|
return [{
|
|
@@ -349,7 +358,7 @@ function nativeAttentionState(value) {
|
|
|
349
358
|
const cursors = objectRecord(parsed?.observedCursors);
|
|
350
359
|
const observedCursors = cursors
|
|
351
360
|
? Object.fromEntries(
|
|
352
|
-
Object.entries(cursors).slice(0,
|
|
361
|
+
Object.entries(cursors).slice(0, NATIVE_STATE_LIMIT).filter(
|
|
353
362
|
([key, cursor]) => key.length <= 200 && typeof cursor === 'string' && cursor.length <= 200,
|
|
354
363
|
),
|
|
355
364
|
)
|
|
@@ -434,7 +443,7 @@ export class NativeSessionAttentionTracker {
|
|
|
434
443
|
runtimeStatus: row.runtimeStatus,
|
|
435
444
|
});
|
|
436
445
|
if (current.cursor !== null) {
|
|
437
|
-
this.#observedCursors
|
|
446
|
+
setRecentBounded(this.#observedCursors, row.key, current.cursor);
|
|
438
447
|
changed = true;
|
|
439
448
|
}
|
|
440
449
|
continue;
|
|
@@ -468,7 +477,7 @@ export class NativeSessionAttentionTracker {
|
|
|
468
477
|
}
|
|
469
478
|
|
|
470
479
|
if (prior.cursor !== null && this.#observedCursors.get(row.key) !== prior.cursor) {
|
|
471
|
-
this.#observedCursors
|
|
480
|
+
setRecentBounded(this.#observedCursors, row.key, prior.cursor);
|
|
472
481
|
changed = true;
|
|
473
482
|
}
|
|
474
483
|
this.#observedUpdates.set(row.key, {
|
|
@@ -549,7 +558,7 @@ export class NativeSessionAttentionTracker {
|
|
|
549
558
|
...(boundedPreview ? { preview: boundedPreview } : {}),
|
|
550
559
|
};
|
|
551
560
|
if (JSON.stringify(prior) === JSON.stringify(next)) return false;
|
|
552
|
-
this.#attention
|
|
561
|
+
setRecentBounded(this.#attention, key, next);
|
|
553
562
|
return true;
|
|
554
563
|
}
|
|
555
564
|
|
|
@@ -562,6 +571,120 @@ export function createNativeSessionAttentionTracker(options) {
|
|
|
562
571
|
return new NativeSessionAttentionTracker(options);
|
|
563
572
|
}
|
|
564
573
|
|
|
574
|
+
export function normalizeNativeMessengerState(value) {
|
|
575
|
+
const parsed = objectRecord(value);
|
|
576
|
+
const attention = nativeAttentionState(parsed);
|
|
577
|
+
const draftRecord = objectRecord(parsed?.drafts);
|
|
578
|
+
const drafts = draftRecord
|
|
579
|
+
? Object.fromEntries(
|
|
580
|
+
Object.entries(draftRecord).slice(0, NATIVE_STATE_LIMIT).flatMap(([key, draft]) =>
|
|
581
|
+
key && key.length <= 200 && typeof draft === 'string' && draft
|
|
582
|
+
? [[key, draft.slice(0, 50_000)]]
|
|
583
|
+
: [],
|
|
584
|
+
),
|
|
585
|
+
)
|
|
586
|
+
: {};
|
|
587
|
+
const modeRecord = objectRecord(parsed?.preferredLaunchModes);
|
|
588
|
+
const preferredLaunchModes = modeRecord
|
|
589
|
+
? Object.fromEntries(
|
|
590
|
+
Object.entries(modeRecord).slice(0, NATIVE_STATE_LIMIT).filter(
|
|
591
|
+
([key, mode]) =>
|
|
592
|
+
key && key.length <= 200 && (mode === 'headless' || mode === 'terminal'),
|
|
593
|
+
),
|
|
594
|
+
)
|
|
595
|
+
: {};
|
|
596
|
+
return {
|
|
597
|
+
version: 1,
|
|
598
|
+
attention: attention.attention,
|
|
599
|
+
observedCursors: attention.observedCursors,
|
|
600
|
+
drafts,
|
|
601
|
+
preferredLaunchModes,
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/** One serializable, bounded state owner for native-session messenger chrome. Storage location,
|
|
606
|
+
* encryption, debounce, and multi-device synchronization remain host policy. */
|
|
607
|
+
export class NativeMessengerState {
|
|
608
|
+
#attention;
|
|
609
|
+
#drafts;
|
|
610
|
+
#onChange;
|
|
611
|
+
#preferredLaunchModes;
|
|
612
|
+
|
|
613
|
+
constructor(options = {}) {
|
|
614
|
+
const state = normalizeNativeMessengerState(options.state);
|
|
615
|
+
this.#onChange = options.onChange;
|
|
616
|
+
this.#drafts = new Map(Object.entries(state.drafts));
|
|
617
|
+
this.#preferredLaunchModes = new Map(Object.entries(state.preferredLaunchModes));
|
|
618
|
+
this.#attention = new NativeSessionAttentionTracker({
|
|
619
|
+
state,
|
|
620
|
+
onChange: () => this.#emit(),
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
observeAttention(options) {
|
|
625
|
+
return this.#attention.observe(options);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
acknowledge(key) {
|
|
629
|
+
return this.#attention.acknowledge(key);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
draft(key) {
|
|
633
|
+
return this.#drafts.get(key) ?? '';
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
setDraft(key, draft) {
|
|
637
|
+
if (typeof key !== 'string' || !key || key.length > 200 || typeof draft !== 'string') {
|
|
638
|
+
throw new TypeError('NativeMessengerState draft needs a bounded key and string value.');
|
|
639
|
+
}
|
|
640
|
+
const next = draft.slice(0, 50_000);
|
|
641
|
+
const previous = this.#drafts.get(key) ?? '';
|
|
642
|
+
if (previous === next) return false;
|
|
643
|
+
if (next) setRecentBounded(this.#drafts, key, next);
|
|
644
|
+
else this.#drafts.delete(key);
|
|
645
|
+
this.#emit();
|
|
646
|
+
return true;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
preferredLaunchMode(harness) {
|
|
650
|
+
return this.#preferredLaunchModes.get(harness) ?? null;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
setPreferredLaunchMode(harness, mode) {
|
|
654
|
+
if (
|
|
655
|
+
typeof harness !== 'string' ||
|
|
656
|
+
!harness ||
|
|
657
|
+
harness.length > 200 ||
|
|
658
|
+
(mode !== 'headless' && mode !== 'terminal')
|
|
659
|
+
) {
|
|
660
|
+
throw new TypeError('NativeMessengerState launch preference must be headless or terminal.');
|
|
661
|
+
}
|
|
662
|
+
if (this.#preferredLaunchModes.get(harness) === mode) return false;
|
|
663
|
+
setRecentBounded(this.#preferredLaunchModes, harness, mode);
|
|
664
|
+
this.#emit();
|
|
665
|
+
return true;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
snapshot() {
|
|
669
|
+
const attention = this.#attention.snapshot();
|
|
670
|
+
return {
|
|
671
|
+
version: 1,
|
|
672
|
+
attention: attention.attention,
|
|
673
|
+
observedCursors: attention.observedCursors,
|
|
674
|
+
drafts: Object.fromEntries(this.#drafts),
|
|
675
|
+
preferredLaunchModes: Object.fromEntries(this.#preferredLaunchModes),
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
#emit() {
|
|
680
|
+
this.#onChange?.(this.snapshot());
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
export function createNativeMessengerState(options) {
|
|
685
|
+
return new NativeMessengerState(options);
|
|
686
|
+
}
|
|
687
|
+
|
|
565
688
|
/** Browser binding for any transport that can post one intent and return an
|
|
566
689
|
* optional authoritative frame. Local host actions may intercept an intent. */
|
|
567
690
|
export function createRemoteUiBinding(options) {
|
package/index.d.ts
CHANGED
|
@@ -10,6 +10,51 @@ export type ExecutionMode = 'headless' | 'terminal';
|
|
|
10
10
|
export type ContinuationMode = ExecutionMode;
|
|
11
11
|
export type SessionActivity = 'idle' | 'recent' | 'running' | 'working' | 'needs-input' | 'finished' | 'failed' | 'unseen';
|
|
12
12
|
|
|
13
|
+
export type ContributionJson = null | boolean | number | string | ContributionJson[] | { [key: string]: ContributionJson };
|
|
14
|
+
|
|
15
|
+
export interface AgentContributionModel {
|
|
16
|
+
schema: 'supercode/contribution-v1';
|
|
17
|
+
id: string;
|
|
18
|
+
kind: string;
|
|
19
|
+
title?: string;
|
|
20
|
+
placement?: { surface?: string; region?: string };
|
|
21
|
+
data: ContributionJson;
|
|
22
|
+
/** Workspace-relative declaration path; never an absolute host path. */
|
|
23
|
+
source: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface AgentPackageModel {
|
|
27
|
+
id: string;
|
|
28
|
+
name: string;
|
|
29
|
+
version: string;
|
|
30
|
+
description: string | null;
|
|
31
|
+
capabilities: { required: string[]; optional: string[] };
|
|
32
|
+
/** Workspace-relative durable data location. */
|
|
33
|
+
storage: { relativePath: string };
|
|
34
|
+
resources: Record<string, AgentPackageResourceStateModel>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface AgentPackageResourceModel {
|
|
38
|
+
schema: 'supercode/package-resource-v1';
|
|
39
|
+
contributionId: string;
|
|
40
|
+
format: 'json' | 'text';
|
|
41
|
+
mediaType: string;
|
|
42
|
+
data: ContributionJson;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type AgentPackageResourceStateModel =
|
|
46
|
+
| { schema: 'supercode/package-resource-state-v1'; status: 'ready'; resource: AgentPackageResourceModel }
|
|
47
|
+
| { schema: 'supercode/package-resource-state-v1'; status: 'absent'; contributionId: string }
|
|
48
|
+
| { schema: 'supercode/package-resource-state-v1'; status: 'error'; contributionId: string; message: string };
|
|
49
|
+
|
|
50
|
+
export interface AgentPackageCapabilityStateModel {
|
|
51
|
+
ready: boolean;
|
|
52
|
+
availableRequired: string[];
|
|
53
|
+
missingRequired: string[];
|
|
54
|
+
enabledOptional: string[];
|
|
55
|
+
unavailableOptional: string[];
|
|
56
|
+
}
|
|
57
|
+
|
|
13
58
|
export interface AgentActivityModel {
|
|
14
59
|
harness: HarnessId | '';
|
|
15
60
|
sessionKey: string | null;
|
|
@@ -347,6 +392,12 @@ export interface SupercodeUiState {
|
|
|
347
392
|
attached: AttachedSessionModel | null;
|
|
348
393
|
owned: AttachedSessionModel | null;
|
|
349
394
|
attachError: { key: string; message: string } | null;
|
|
395
|
+
/** Browser-safe package metadata; host filesystem paths are deliberately omitted. */
|
|
396
|
+
agentPackage: AgentPackageModel | null;
|
|
397
|
+
contributions: AgentContributionModel[];
|
|
398
|
+
agentPackageError: string | null;
|
|
399
|
+
agentPackageCapabilityState: AgentPackageCapabilityStateModel | null;
|
|
400
|
+
agentPackageCapabilityError: string | null;
|
|
350
401
|
}
|
|
351
402
|
|
|
352
403
|
export type SupercodeUiIntent =
|
|
@@ -561,6 +612,11 @@ export interface MessengerProps {
|
|
|
561
612
|
export const EMPTY_UI_STATE: Readonly<SupercodeUiState>;
|
|
562
613
|
export const DEFAULT_LABELS: Readonly<MessengerLabels>;
|
|
563
614
|
export function normalizeUiState(value: unknown): SupercodeUiState;
|
|
615
|
+
export function normalizeContributions(value: unknown): AgentContributionModel[];
|
|
616
|
+
export function selectContributions(
|
|
617
|
+
contributions: unknown,
|
|
618
|
+
query?: { kind?: string; surface?: string; region?: string },
|
|
619
|
+
): AgentContributionModel[];
|
|
564
620
|
export function harnessDisplayName(id: string): string;
|
|
565
621
|
export function sessionDisplayName(session: Pick<SessionRowModel, 'name' | 'title'>): string;
|
|
566
622
|
export function relativeAge(updatedAt: number | null | undefined, now?: number): string;
|
package/messenger.mjs
CHANGED
|
@@ -69,7 +69,12 @@ var EMPTY_UI_STATE = Object.freeze({
|
|
|
69
69
|
subagentInspector: null,
|
|
70
70
|
attached: null,
|
|
71
71
|
owned: null,
|
|
72
|
-
attachError: null
|
|
72
|
+
attachError: null,
|
|
73
|
+
agentPackage: null,
|
|
74
|
+
contributions: Object.freeze([]),
|
|
75
|
+
agentPackageError: null,
|
|
76
|
+
agentPackageCapabilityState: null,
|
|
77
|
+
agentPackageCapabilityError: null
|
|
73
78
|
});
|
|
74
79
|
var ROLES = /* @__PURE__ */ new Set(["system", "user", "assistant", "tool", "reasoning", "request", "notice"]);
|
|
75
80
|
var MODES = /* @__PURE__ */ new Set(["none", "control", "mirror"]);
|
|
@@ -82,6 +87,10 @@ var AUTHENTICATION_PHASES = /* @__PURE__ */ new Set(["idle", "checking", "requir
|
|
|
82
87
|
var MAX_TOOL_FIELDS = 8;
|
|
83
88
|
var MAX_TOOL_FIELD_CHARS = 800;
|
|
84
89
|
var MAX_TOOL_PREVIEW_CHARS = 4e3;
|
|
90
|
+
var MAX_CONTRIBUTIONS = 64;
|
|
91
|
+
var MAX_CONTRIBUTION_JSON_DEPTH = 12;
|
|
92
|
+
var MAX_CONTRIBUTION_COLLECTION = 100;
|
|
93
|
+
var MAX_CONTRIBUTION_STRING_CHARS = 16e3;
|
|
85
94
|
function record(value) {
|
|
86
95
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
87
96
|
}
|
|
@@ -114,6 +123,126 @@ function number(value, fallback = 0) {
|
|
|
114
123
|
function nullableNumber(value) {
|
|
115
124
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
116
125
|
}
|
|
126
|
+
function relativeContributionPath(value) {
|
|
127
|
+
if (typeof value !== "string" || value.startsWith("/") || /^[a-z]:[\\/]/i.test(value)) return null;
|
|
128
|
+
return value.replaceAll("\\", "/").split("/").some((segment) => segment === "..") ? null : boundedString(value, 1e3);
|
|
129
|
+
}
|
|
130
|
+
function contributionJson(value, depth = 0, seen = /* @__PURE__ */ new WeakSet()) {
|
|
131
|
+
if (value === null || typeof value === "boolean" || typeof value === "string") {
|
|
132
|
+
return typeof value === "string" ? boundedString(value, MAX_CONTRIBUTION_STRING_CHARS) : value;
|
|
133
|
+
}
|
|
134
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : void 0;
|
|
135
|
+
if (depth >= MAX_CONTRIBUTION_JSON_DEPTH || !value || typeof value !== "object" || seen.has(value)) {
|
|
136
|
+
return void 0;
|
|
137
|
+
}
|
|
138
|
+
seen.add(value);
|
|
139
|
+
if (Array.isArray(value)) {
|
|
140
|
+
const result2 = [];
|
|
141
|
+
for (const item of value.slice(0, MAX_CONTRIBUTION_COLLECTION)) {
|
|
142
|
+
const normalized = contributionJson(item, depth + 1, seen);
|
|
143
|
+
if (normalized !== void 0) result2.push(normalized);
|
|
144
|
+
}
|
|
145
|
+
seen.delete(value);
|
|
146
|
+
return result2;
|
|
147
|
+
}
|
|
148
|
+
const source = record(value);
|
|
149
|
+
if (!source) return void 0;
|
|
150
|
+
const result = {};
|
|
151
|
+
for (const [key, item] of Object.entries(source).slice(0, MAX_CONTRIBUTION_COLLECTION)) {
|
|
152
|
+
const normalized = contributionJson(item, depth + 1, seen);
|
|
153
|
+
if (normalized !== void 0) result[boundedString(key, 200)] = normalized;
|
|
154
|
+
}
|
|
155
|
+
seen.delete(value);
|
|
156
|
+
return result;
|
|
157
|
+
}
|
|
158
|
+
function normalizeContributions(value) {
|
|
159
|
+
if (!Array.isArray(value)) return [];
|
|
160
|
+
return value.slice(0, MAX_CONTRIBUTIONS).flatMap((candidate) => {
|
|
161
|
+
const item = record(candidate);
|
|
162
|
+
const placement = record(item?.placement);
|
|
163
|
+
const source = relativeContributionPath(item?.source);
|
|
164
|
+
if (item?.schema !== "supercode/contribution-v1" || typeof item.id !== "string" || !item.id.trim() || typeof item.kind !== "string" || !item.kind.trim() || source === null) return [];
|
|
165
|
+
const data = contributionJson(item.data);
|
|
166
|
+
if (data === void 0) return [];
|
|
167
|
+
return [{
|
|
168
|
+
schema: "supercode/contribution-v1",
|
|
169
|
+
id: boundedString(item.id, 200),
|
|
170
|
+
kind: boundedString(item.kind, 100),
|
|
171
|
+
...typeof item.title === "string" ? { title: boundedString(item.title, 500) } : {},
|
|
172
|
+
...placement ? { placement: {
|
|
173
|
+
...typeof placement.surface === "string" ? { surface: boundedString(placement.surface, 100) } : {},
|
|
174
|
+
...typeof placement.region === "string" ? { region: boundedString(placement.region, 100) } : {}
|
|
175
|
+
} } : {},
|
|
176
|
+
data,
|
|
177
|
+
source
|
|
178
|
+
}];
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
function readAgentPackage(value) {
|
|
182
|
+
const agentPackage = record(value);
|
|
183
|
+
const capabilities = record(agentPackage?.capabilities);
|
|
184
|
+
const storage = record(agentPackage?.storage);
|
|
185
|
+
const relativePath = relativeContributionPath(storage?.relativePath);
|
|
186
|
+
if (agentPackage?.schemaVersion !== 1 || typeof agentPackage.id !== "string" || typeof agentPackage.name !== "string" || typeof agentPackage.version !== "string" || !capabilities || !Array.isArray(capabilities.required) || !Array.isArray(capabilities.optional) || !storage || relativePath === null) return null;
|
|
187
|
+
const resources = record(agentPackage.resources);
|
|
188
|
+
return {
|
|
189
|
+
id: boundedString(agentPackage.id, 200),
|
|
190
|
+
name: boundedString(agentPackage.name, 500),
|
|
191
|
+
version: boundedString(agentPackage.version, 100),
|
|
192
|
+
description: typeof agentPackage.description === "string" ? boundedString(agentPackage.description, 2e3) : null,
|
|
193
|
+
capabilities: {
|
|
194
|
+
required: capabilities.required.filter((item) => typeof item === "string").slice(0, 100).map((item) => boundedString(item, 200)),
|
|
195
|
+
optional: capabilities.optional.filter((item) => typeof item === "string").slice(0, 100).map((item) => boundedString(item, 200))
|
|
196
|
+
},
|
|
197
|
+
storage: { relativePath },
|
|
198
|
+
resources: resources ? Object.fromEntries(Object.entries(resources).flatMap(([id, candidate]) => {
|
|
199
|
+
const state = record(candidate);
|
|
200
|
+
if (state?.schema !== "supercode/package-resource-state-v1") return [];
|
|
201
|
+
if (state.status === "absent" && typeof state.contributionId === "string") {
|
|
202
|
+
return [[boundedString(id, 200), {
|
|
203
|
+
schema: "supercode/package-resource-state-v1",
|
|
204
|
+
status: "absent",
|
|
205
|
+
contributionId: boundedString(state.contributionId, 200)
|
|
206
|
+
}]];
|
|
207
|
+
}
|
|
208
|
+
if (state.status === "error" && typeof state.contributionId === "string" && typeof state.message === "string") {
|
|
209
|
+
return [[boundedString(id, 200), {
|
|
210
|
+
schema: "supercode/package-resource-state-v1",
|
|
211
|
+
status: "error",
|
|
212
|
+
contributionId: boundedString(state.contributionId, 200),
|
|
213
|
+
message: boundedString(state.message)
|
|
214
|
+
}]];
|
|
215
|
+
}
|
|
216
|
+
const resource = record(state?.resource);
|
|
217
|
+
const data = contributionJson(resource?.data);
|
|
218
|
+
if (state?.status !== "ready" || resource?.schema !== "supercode/package-resource-v1" || typeof resource.contributionId !== "string" || !["json", "text"].includes(resource.format) || typeof resource.mediaType !== "string" || data === void 0) return [];
|
|
219
|
+
return [[boundedString(id, 200), {
|
|
220
|
+
schema: "supercode/package-resource-state-v1",
|
|
221
|
+
status: "ready",
|
|
222
|
+
resource: {
|
|
223
|
+
schema: "supercode/package-resource-v1",
|
|
224
|
+
contributionId: boundedString(resource.contributionId, 200),
|
|
225
|
+
format: resource.format,
|
|
226
|
+
mediaType: boundedString(resource.mediaType, 200),
|
|
227
|
+
data
|
|
228
|
+
}
|
|
229
|
+
}]];
|
|
230
|
+
})) : {}
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
function readAgentPackageCapabilityState(value) {
|
|
234
|
+
const state = record(value);
|
|
235
|
+
if (!state) return null;
|
|
236
|
+
const fields = ["availableRequired", "missingRequired", "enabledOptional", "unavailableOptional"];
|
|
237
|
+
if (!fields.every((field) => Array.isArray(state[field]))) return null;
|
|
238
|
+
return {
|
|
239
|
+
ready: state.ready === true,
|
|
240
|
+
...Object.fromEntries(fields.map((field) => [
|
|
241
|
+
field,
|
|
242
|
+
state[field].filter((item) => typeof item === "string").slice(0, 100).map((item) => boundedString(item, 200))
|
|
243
|
+
]))
|
|
244
|
+
};
|
|
245
|
+
}
|
|
117
246
|
function relativeAge(updatedAt, now = Date.now()) {
|
|
118
247
|
if (typeof updatedAt !== "number" || !Number.isFinite(updatedAt) || updatedAt <= 0) return "";
|
|
119
248
|
const delta = Math.max(0, now - updatedAt);
|
|
@@ -831,7 +960,12 @@ function normalizeUiState(value) {
|
|
|
831
960
|
subagentInspector: readSubagentInspector(raw.subagentInspector),
|
|
832
961
|
attached: readAttached(raw.attached),
|
|
833
962
|
owned: readAttached(raw.owned),
|
|
834
|
-
attachError: attachError && typeof attachError.key === "string" && typeof attachError.message === "string" ? { key: attachError.key, message: attachError.message } : null
|
|
963
|
+
attachError: attachError && typeof attachError.key === "string" && typeof attachError.message === "string" ? { key: attachError.key, message: attachError.message } : null,
|
|
964
|
+
agentPackage: readAgentPackage(raw.agentPackage),
|
|
965
|
+
contributions: normalizeContributions(raw.contributions),
|
|
966
|
+
agentPackageError: typeof raw.agentPackageError === "string" ? boundedString(raw.agentPackageError) : null,
|
|
967
|
+
agentPackageCapabilityState: readAgentPackageCapabilityState(raw.agentPackageCapabilityState),
|
|
968
|
+
agentPackageCapabilityError: typeof raw.agentPackageCapabilityError === "string" ? boundedString(raw.agentPackageCapabilityError) : null
|
|
835
969
|
};
|
|
836
970
|
}
|
|
837
971
|
function harnessDisplayName(id) {
|