@volter-ai-dev/supercode-ui 0.1.35 → 0.1.37
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 +84 -2
- package/activity.d.ts +2 -0
- package/activity.mjs +941 -0
- package/components.d.ts +4 -0
- package/components.mjs +737 -485
- package/composer.mjs +82 -20
- package/controller.d.ts +43 -0
- package/controller.mjs +127 -2
- package/conversation.mjs +75 -64
- package/core.d.ts +3 -0
- package/core.mjs +222 -2
- package/embed.mjs +338 -149
- package/icon.mjs +1 -1
- package/index.d.ts +68 -2
- package/logo.mjs +11 -6
- package/messenger.mjs +338 -149
- package/package.json +54 -3
- package/react/activity.d.ts +4 -0
- package/react/activity.mjs +941 -0
- package/react/components.mjs +3081 -0
- package/react/composer.d.ts +2 -0
- package/react/composer.mjs +518 -0
- package/react/conversation.d.ts +2 -0
- package/react/conversation.mjs +1330 -0
- package/react/icon.d.ts +2 -0
- package/react/icon.mjs +51 -0
- package/react/index.d.ts +122 -0
- package/react/logo.d.ts +2 -0
- package/react/logo.mjs +92 -0
- package/react/messenger.d.ts +2 -0
- package/react/messenger.mjs +2974 -0
- package/react/sessions.d.ts +2 -0
- package/react/sessions.mjs +429 -0
- package/react/settings.d.ts +2 -0
- package/react/settings.mjs +319 -0
- package/sessions.mjs +48 -28
- package/settings.mjs +170 -69
- package/styles.css +9 -0
package/core.mjs
CHANGED
|
@@ -30,6 +30,7 @@ export const EMPTY_UI_STATE = Object.freeze({
|
|
|
30
30
|
mode: 'none',
|
|
31
31
|
strategy: null,
|
|
32
32
|
canSend: false,
|
|
33
|
+
canSteer: false,
|
|
33
34
|
canResume: false,
|
|
34
35
|
continuationModes: Object.freeze([]),
|
|
35
36
|
canBranch: false,
|
|
@@ -771,6 +772,7 @@ export function normalizeUiState(value) {
|
|
|
771
772
|
mode: MODES.has(raw.mode) ? raw.mode : 'none',
|
|
772
773
|
strategy: STRATEGIES.has(raw.strategy) ? raw.strategy : null,
|
|
773
774
|
canSend: raw.canSend === true,
|
|
775
|
+
canSteer: raw.canSteer === true,
|
|
774
776
|
canResume,
|
|
775
777
|
continuationModes,
|
|
776
778
|
canBranch: raw.canBranch === true,
|
|
@@ -804,7 +806,29 @@ export function normalizeUiState(value) {
|
|
|
804
806
|
const preferredLaunchMode = launchModes.includes(item.preferredLaunchMode)
|
|
805
807
|
? item.preferredLaunchMode
|
|
806
808
|
: launchModes[0] ?? null;
|
|
807
|
-
|
|
809
|
+
const capabilities = record(item.capabilities);
|
|
810
|
+
return [{
|
|
811
|
+
id: item.id,
|
|
812
|
+
label: string(item.label, harnessDisplayName(item.id)),
|
|
813
|
+
installed: item.installed === true,
|
|
814
|
+
startable,
|
|
815
|
+
reason: typeof item.reason === 'string' ? boundedString(item.reason) : null,
|
|
816
|
+
auth: ['ready', 'configured', 'required', 'unknown'].includes(item.auth) ? item.auth : 'unknown',
|
|
817
|
+
runtime: ['ready', 'degraded', 'unavailable'].includes(item.runtime) ? item.runtime : startable ? 'ready' : 'unavailable',
|
|
818
|
+
protocol: boundedString(item.protocol, 100),
|
|
819
|
+
repair: typeof item.repair === 'string' ? boundedString(item.repair, 4_000) : null,
|
|
820
|
+
capabilities: {
|
|
821
|
+
start: capabilities?.start === true,
|
|
822
|
+
resume: capabilities?.resume === true,
|
|
823
|
+
attach: capabilities?.attach === true,
|
|
824
|
+
send: capabilities?.send === true,
|
|
825
|
+
interrupt: capabilities?.interrupt === true,
|
|
826
|
+
steer: capabilities?.steer === true,
|
|
827
|
+
respond: capabilities?.respond === true,
|
|
828
|
+
},
|
|
829
|
+
launchModes,
|
|
830
|
+
preferredLaunchMode,
|
|
831
|
+
}];
|
|
808
832
|
}) : [],
|
|
809
833
|
history: { sessionLimit: number(history?.sessionLimit), hasMoreSessions: history?.hasMoreSessions === true, transcriptLimit: number(history?.transcriptLimit, 120), hasEarlier: history?.hasEarlier === true },
|
|
810
834
|
savedDraft: string(raw.savedDraft),
|
|
@@ -843,6 +867,60 @@ export function sessionActivity(state, row) {
|
|
|
843
867
|
return 'idle';
|
|
844
868
|
}
|
|
845
869
|
|
|
870
|
+
const ACTIVITY_PRIORITY = Object.freeze({
|
|
871
|
+
'needs-input': 70,
|
|
872
|
+
failed: 60,
|
|
873
|
+
working: 50,
|
|
874
|
+
unseen: 40,
|
|
875
|
+
finished: 30,
|
|
876
|
+
running: 20,
|
|
877
|
+
recent: 10,
|
|
878
|
+
idle: 0,
|
|
879
|
+
});
|
|
880
|
+
|
|
881
|
+
function activityDetail(activity, preview, pill) {
|
|
882
|
+
if (preview) return preview;
|
|
883
|
+
if (activity === 'needs-input') return 'Needs input';
|
|
884
|
+
if (activity === 'failed') return 'Run failed';
|
|
885
|
+
if (activity === 'working') return 'Working…';
|
|
886
|
+
if (activity === 'finished') return 'Finished';
|
|
887
|
+
if (activity === 'unseen') return 'New reply';
|
|
888
|
+
if (activity === 'running') return 'Session is running';
|
|
889
|
+
return pill || 'Agent chats';
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
/** A stable, host-agnostic projection for overlay launchers and compact activity trays. */
|
|
893
|
+
export function projectAgentActivity(value) {
|
|
894
|
+
const state = normalizeUiState(value);
|
|
895
|
+
const attentionByKey = new Map(state.attention.map((item) => [item.key, item]));
|
|
896
|
+
const unreadCount = state.attention.reduce((total, item) => total + (item.unreadCount ?? 1), 0);
|
|
897
|
+
const ranked = state.sessions.map((row, index) => {
|
|
898
|
+
const activity = sessionActivity(state, row);
|
|
899
|
+
return {
|
|
900
|
+
row,
|
|
901
|
+
index,
|
|
902
|
+
activity,
|
|
903
|
+
priority: ACTIVITY_PRIORITY[activity] ?? 0,
|
|
904
|
+
active: row.active || row.key === state.attached?.key || row.key === state.owned?.key,
|
|
905
|
+
updatedAt: row.previewUpdatedAt ?? row.updatedAt ?? 0,
|
|
906
|
+
};
|
|
907
|
+
}).sort((left, right) => right.priority - left.priority || Number(right.active) - Number(left.active) || right.updatedAt - left.updatedAt || left.index - right.index);
|
|
908
|
+
const selected = ranked[0];
|
|
909
|
+
const fallback = state.attached ?? state.owned;
|
|
910
|
+
const harness = selected?.row.harness ?? fallback?.harness ?? state.harness;
|
|
911
|
+
const activity = selected?.activity ?? (state.needsInput ? 'needs-input' : state.busy ? 'working' : state.startup === 'ready' ? 'idle' : 'recent');
|
|
912
|
+
const attention = selected ? attentionByKey.get(selected.row.key) : null;
|
|
913
|
+
const preview = selected?.row.preview || attention?.preview || '';
|
|
914
|
+
return {
|
|
915
|
+
harness,
|
|
916
|
+
sessionKey: selected?.row.key ?? fallback?.key ?? null,
|
|
917
|
+
title: selected ? sessionDisplayName(selected.row) : fallback ? sessionDisplayName(fallback) : 'Agent chats',
|
|
918
|
+
detail: activityDetail(activity, preview, state.pill.label),
|
|
919
|
+
activity,
|
|
920
|
+
unreadCount,
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
|
|
846
924
|
export function filterSessions(rows, query) {
|
|
847
925
|
const needle = query.trim().toLocaleLowerCase();
|
|
848
926
|
if (!needle) return [...rows];
|
|
@@ -908,7 +986,7 @@ export function canContinueHere(state) {
|
|
|
908
986
|
|
|
909
987
|
export function operationLabel(operation) {
|
|
910
988
|
if (!operation) return '';
|
|
911
|
-
const labels = { discover: 'Refreshing chats…', observe: 'Loading recent messages…', attach: 'Opening chat…', start: 'Starting new chat…', resume: 'Continuing here…', join: 'Joining live session…', detach: 'Detaching to read-only…', branch: 'Starting continuation…', reduce: 'Reducing context and verifying reversibility…', terminal: 'Preparing terminal handoff…', export: 'Exporting losslessly…', interrupt: 'Stopping agent…', respond: 'Sending response…', configureHarness: 'Updating harness settings…', loadEarlier: 'Loading earlier messages…', loadSessions: 'Loading more chats…', refresh: 'Retrying…' };
|
|
989
|
+
const labels = { discover: 'Refreshing chats…', observe: 'Loading recent messages…', attach: 'Opening chat…', start: 'Starting new chat…', resume: 'Continuing here…', join: 'Joining live session…', detach: 'Detaching to read-only…', branch: 'Starting continuation…', reduce: 'Reducing context and verifying reversibility…', terminal: 'Preparing terminal handoff…', export: 'Exporting losslessly…', steer: 'Steering agent…', interrupt: 'Stopping agent…', respond: 'Sending response…', configureHarness: 'Updating harness settings…', loadEarlier: 'Loading earlier messages…', loadSessions: 'Loading more chats…', refresh: 'Retrying…' };
|
|
912
990
|
return labels[operation] ?? `${operation.replaceAll('_', ' ')}…`;
|
|
913
991
|
}
|
|
914
992
|
|
|
@@ -920,3 +998,145 @@ export function terminalCommand(handoff) {
|
|
|
920
998
|
export function isSendKey(event) {
|
|
921
999
|
return event.key === 'Enter' && !event.shiftKey && !event.isComposing;
|
|
922
1000
|
}
|
|
1001
|
+
|
|
1002
|
+
function intentRecord(value) {
|
|
1003
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value) ? value : null;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
function intentKeys(value, allowed) {
|
|
1007
|
+
return Object.keys(value).every((key) => allowed.includes(key));
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
function intentContext(value) {
|
|
1011
|
+
if (value === undefined) return [];
|
|
1012
|
+
if (!Array.isArray(value) || value.length > 32) return null;
|
|
1013
|
+
const items = [];
|
|
1014
|
+
for (const candidate of value) {
|
|
1015
|
+
const item = intentRecord(candidate);
|
|
1016
|
+
if (!item || !intentKeys(item, ['id', 'kind', 'label', 'detail'])) return null;
|
|
1017
|
+
if (typeof item.label !== 'string' || !item.label.trim() || item.label.length > 200) return null;
|
|
1018
|
+
if (typeof item.detail !== 'string' || item.detail.length > 20_000) return null;
|
|
1019
|
+
if (item.id !== undefined && (typeof item.id !== 'string' || !item.id || item.id.length > 2_000)) return null;
|
|
1020
|
+
if (item.kind !== undefined && (typeof item.kind !== 'string' || !item.kind || item.kind.length > 100)) return null;
|
|
1021
|
+
items.push({
|
|
1022
|
+
...(typeof item.id === 'string' ? { id: item.id } : {}),
|
|
1023
|
+
...(typeof item.kind === 'string' ? { kind: item.kind } : {}),
|
|
1024
|
+
label: item.label.trim(),
|
|
1025
|
+
detail: item.detail,
|
|
1026
|
+
});
|
|
1027
|
+
}
|
|
1028
|
+
return items;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
function intentImages(value) {
|
|
1032
|
+
if (value === undefined) return [];
|
|
1033
|
+
if (!Array.isArray(value) || value.length > 4) return null;
|
|
1034
|
+
let totalChars = 0;
|
|
1035
|
+
const items = [];
|
|
1036
|
+
for (const candidate of value) {
|
|
1037
|
+
const item = intentRecord(candidate);
|
|
1038
|
+
if (!item || !intentKeys(item, ['id', 'label', 'url'])) return null;
|
|
1039
|
+
if (typeof item.label !== 'string' || !item.label.trim() || item.label.length > 200) return null;
|
|
1040
|
+
if (typeof item.url !== 'string' || !/^(?:data:image\/|https?:\/\/)/.test(item.url)) return null;
|
|
1041
|
+
if (item.url.length > 12 * 1024 * 1024) return null;
|
|
1042
|
+
totalChars += item.url.length;
|
|
1043
|
+
if (totalChars > 32 * 1024 * 1024) return null;
|
|
1044
|
+
if (item.id !== undefined && (typeof item.id !== 'string' || !item.id || item.id.length > 2_000)) return null;
|
|
1045
|
+
items.push({ ...(typeof item.id === 'string' ? { id: item.id } : {}), label: item.label.trim(), url: item.url });
|
|
1046
|
+
}
|
|
1047
|
+
return items;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
function intentJson(value, seen = new Set()) {
|
|
1051
|
+
if (value === null || typeof value === 'string' || typeof value === 'boolean') return true;
|
|
1052
|
+
if (typeof value === 'number') return Number.isFinite(value);
|
|
1053
|
+
if (typeof value !== 'object' || seen.has(value)) return false;
|
|
1054
|
+
seen.add(value);
|
|
1055
|
+
const valid = Array.isArray(value)
|
|
1056
|
+
? value.length <= 1_000 && value.every((item) => intentJson(item, seen))
|
|
1057
|
+
: Object.keys(value).length <= 1_000 && Object.values(value).every((item) => intentJson(item, seen));
|
|
1058
|
+
seen.delete(value);
|
|
1059
|
+
return valid;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
/** Runtime validation for the transport-safe intent union emitted by the default UI. */
|
|
1063
|
+
export function parseSupercodeUiIntent(value) {
|
|
1064
|
+
const intent = intentRecord(value);
|
|
1065
|
+
if (!intent || typeof intent.action !== 'string') return null;
|
|
1066
|
+
const exact = (...keys) => intentKeys(intent, ['action', ...keys]);
|
|
1067
|
+
const actionOnly = ['mounted', 'loadSessions', 'loadEarlier', 'join', 'detach', 'terminal', 'interrupt', 'refresh', 'release'];
|
|
1068
|
+
if (actionOnly.includes(intent.action)) return exact() ? { action: intent.action } : null;
|
|
1069
|
+
if (intent.action === 'attach' || intent.action === 'ack') {
|
|
1070
|
+
return exact('key') && typeof intent.key === 'string' && intent.key.trim() && intent.key.length <= 4_000
|
|
1071
|
+
? { action: intent.action, key: intent.key.trim() }
|
|
1072
|
+
: null;
|
|
1073
|
+
}
|
|
1074
|
+
if (intent.action === 'draft') {
|
|
1075
|
+
return exact('text') && typeof intent.text === 'string' && intent.text.length <= 50_000
|
|
1076
|
+
? { action: 'draft', text: intent.text }
|
|
1077
|
+
: null;
|
|
1078
|
+
}
|
|
1079
|
+
if (intent.action === 'steer') {
|
|
1080
|
+
return exact('text') && typeof intent.text === 'string' && intent.text.trim() && intent.text.length <= 50_000
|
|
1081
|
+
? { action: 'steer', text: intent.text }
|
|
1082
|
+
: null;
|
|
1083
|
+
}
|
|
1084
|
+
if (intent.action === 'send' || intent.action === 'new') {
|
|
1085
|
+
const allowed = intent.action === 'new'
|
|
1086
|
+
? exact('harness', 'mode', 'text', 'context', 'images')
|
|
1087
|
+
: exact('text', 'context', 'images');
|
|
1088
|
+
const context = intentContext(intent.context);
|
|
1089
|
+
const images = intentImages(intent.images);
|
|
1090
|
+
if (!allowed || typeof intent.text !== 'string' || context === null || images === null) return null;
|
|
1091
|
+
if (!intent.text.trim() && images.length === 0) return null;
|
|
1092
|
+
const extras = {
|
|
1093
|
+
...(context.length ? { context } : {}),
|
|
1094
|
+
...(images.length ? { images } : {}),
|
|
1095
|
+
};
|
|
1096
|
+
if (intent.action === 'send') return { action: 'send', text: intent.text, ...extras };
|
|
1097
|
+
if (typeof intent.harness !== 'string' || !intent.harness.trim() || intent.harness.length > 100) return null;
|
|
1098
|
+
const mode = intent.mode === undefined ? undefined : intent.mode;
|
|
1099
|
+
if (mode !== undefined && mode !== 'headless' && mode !== 'terminal') return null;
|
|
1100
|
+
if (mode === 'terminal' && !intent.text.trim()) return null;
|
|
1101
|
+
return { action: 'new', harness: intent.harness.trim(), ...(mode ? { mode } : {}), text: intent.text, ...extras };
|
|
1102
|
+
}
|
|
1103
|
+
if (intent.action === 'resume') {
|
|
1104
|
+
return exact('mode') && (intent.mode === undefined || intent.mode === 'headless' || intent.mode === 'terminal')
|
|
1105
|
+
? { action: 'resume', ...(intent.mode ? { mode: intent.mode } : {}) }
|
|
1106
|
+
: null;
|
|
1107
|
+
}
|
|
1108
|
+
if (intent.action === 'branch' || intent.action === 'reduce') {
|
|
1109
|
+
if (!exact('targetHarness')) return null;
|
|
1110
|
+
if (intent.targetHarness === undefined) return { action: intent.action };
|
|
1111
|
+
return typeof intent.targetHarness === 'string' && intent.targetHarness.trim() && intent.targetHarness.length <= 100
|
|
1112
|
+
? { action: intent.action, targetHarness: intent.targetHarness.trim() }
|
|
1113
|
+
: null;
|
|
1114
|
+
}
|
|
1115
|
+
if (intent.action === 'export') {
|
|
1116
|
+
return exact('targetHarness') && typeof intent.targetHarness === 'string' && intent.targetHarness.trim() && intent.targetHarness.length <= 100
|
|
1117
|
+
? { action: 'export', targetHarness: intent.targetHarness.trim() }
|
|
1118
|
+
: null;
|
|
1119
|
+
}
|
|
1120
|
+
if (intent.action === 'respond') {
|
|
1121
|
+
if (!exact('requestId', 'optionId') || !intentJson(intent.requestId)) return null;
|
|
1122
|
+
return intent.optionId === null || typeof intent.optionId === 'string'
|
|
1123
|
+
? { action: 'respond', requestId: intent.requestId, optionId: intent.optionId }
|
|
1124
|
+
: null;
|
|
1125
|
+
}
|
|
1126
|
+
if (intent.action === 'configureHarness') {
|
|
1127
|
+
if (!exact('harness', 'changes', 'expectedRevision')) return null;
|
|
1128
|
+
if (typeof intent.harness !== 'string' || !intent.harness || intent.harness.length > 100) return null;
|
|
1129
|
+
if (typeof intent.expectedRevision !== 'string' || !intent.expectedRevision || intent.expectedRevision.length > 500) return null;
|
|
1130
|
+
if (!Array.isArray(intent.changes) || !intent.changes.length || intent.changes.length > 20) return null;
|
|
1131
|
+
const changes = [];
|
|
1132
|
+
for (const candidate of intent.changes) {
|
|
1133
|
+
const change = intentRecord(candidate);
|
|
1134
|
+
if (!change || !intentKeys(change, ['key', 'value'])) return null;
|
|
1135
|
+
if (typeof change.key !== 'string' || !change.key || change.key.length > 200) return null;
|
|
1136
|
+
if (change.value !== null && (typeof change.value !== 'string' || change.value.length > 500)) return null;
|
|
1137
|
+
changes.push({ key: change.key, value: change.value });
|
|
1138
|
+
}
|
|
1139
|
+
return { action: 'configureHarness', harness: intent.harness, changes, expectedRevision: intent.expectedRevision };
|
|
1140
|
+
}
|
|
1141
|
+
return null;
|
|
1142
|
+
}
|