@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/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, 500).flatMap((candidate) => {
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, 500).filter(
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.set(row.key, current.cursor);
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.set(row.key, prior.cursor);
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.set(key, next);
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter-ai-dev/supercode-ui",
3
- "version": "0.1.65",
3
+ "version": "0.1.67",
4
4
  "type": "module",
5
5
  "description": "Composable default UI kit for Supercode-powered coding-agent experiences",
6
6
  "exports": {