@volter-ai-dev/supercode-ui 0.1.30 → 0.1.32
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/components.mjs +13 -4
- package/composer.mjs +7 -1
- package/controller.d.ts +4 -0
- package/controller.mjs +9 -1
- package/conversation.mjs +2 -0
- package/core.d.ts +1 -0
- package/core.mjs +11 -2
- package/embed.mjs +13 -4
- package/index.d.ts +5 -1
- package/messenger.mjs +13 -4
- package/package.json +1 -1
- package/sessions.mjs +3 -1
- package/settings.mjs +3 -1
- package/styles.css +1 -1
package/README.md
CHANGED
|
@@ -98,6 +98,11 @@ drafts, and artifact materialization remain explicit callbacks. A
|
|
|
98
98
|
browser should receive projected state from a trusted host rather than instantiate a local
|
|
99
99
|
controller or gain filesystem authority.
|
|
100
100
|
|
|
101
|
+
Native continuation is headless by default. A host with a real terminal provider can add
|
|
102
|
+
`terminal` to `continuationModes` and handle `onResumeTerminal`; the continuation bar then exposes
|
|
103
|
+
that strategy beside “Continue here.” The UI never infers terminal support from a generic runtime
|
|
104
|
+
handoff, and never presents a terminal strategy when the host cannot create one.
|
|
105
|
+
|
|
101
106
|
The default controller projection is display-bounded: 120 visible transcript rows, at most 480
|
|
102
107
|
native entries inspected to fill that tail, 16,000 characters per independent entry field, 100
|
|
103
108
|
session rows, 20 fidelity-residue details, and 50 subagents. It filters harness-injected context
|
package/components.mjs
CHANGED
|
@@ -17,6 +17,7 @@ var DEFAULT_LABELS = Object.freeze({
|
|
|
17
17
|
searchChats: "Search chats",
|
|
18
18
|
askAgent: "Ask your agent\u2026",
|
|
19
19
|
continueHere: "Continue here",
|
|
20
|
+
continueWithTerminal: "Continue with terminal",
|
|
20
21
|
joinLive: "Join live",
|
|
21
22
|
forkHere: "Fork here"
|
|
22
23
|
});
|
|
@@ -32,6 +33,7 @@ var EMPTY_UI_STATE = Object.freeze({
|
|
|
32
33
|
strategy: null,
|
|
33
34
|
canSend: false,
|
|
34
35
|
canResume: false,
|
|
36
|
+
continuationModes: Object.freeze([]),
|
|
35
37
|
canBranch: false,
|
|
36
38
|
canAttach: false,
|
|
37
39
|
canDetach: false,
|
|
@@ -704,6 +706,8 @@ function normalizeUiState(value) {
|
|
|
704
706
|
const pill = record(raw.pill);
|
|
705
707
|
const history = record(raw.history);
|
|
706
708
|
const attachError = record(raw.attachError);
|
|
709
|
+
const canResume = raw.canResume === true;
|
|
710
|
+
const continuationModes = Array.isArray(raw.continuationModes) ? [...new Set(raw.continuationModes.filter((mode) => mode === "headless" || mode === "terminal"))] : canResume ? ["headless"] : [];
|
|
707
711
|
return {
|
|
708
712
|
pill: { tone: ["live", "warn", "dead"].includes(pill?.tone) ? pill.tone : "off", label: string(pill?.label, "connecting\u2026") },
|
|
709
713
|
startup: STARTUP.has(raw.startup) ? raw.startup : "connecting",
|
|
@@ -715,7 +719,8 @@ function normalizeUiState(value) {
|
|
|
715
719
|
mode: MODES.has(raw.mode) ? raw.mode : "none",
|
|
716
720
|
strategy: STRATEGIES.has(raw.strategy) ? raw.strategy : null,
|
|
717
721
|
canSend: raw.canSend === true,
|
|
718
|
-
canResume
|
|
722
|
+
canResume,
|
|
723
|
+
continuationModes,
|
|
719
724
|
canBranch: raw.canBranch === true,
|
|
720
725
|
canAttach: raw.canAttach === true,
|
|
721
726
|
canDetach: raw.canDetach === true,
|
|
@@ -763,9 +768,9 @@ function sessionDisplayName(session) {
|
|
|
763
768
|
function sessionActivity(state, row) {
|
|
764
769
|
if (state.needsInput && row.active) return "needs-input";
|
|
765
770
|
if (state.busy && row.active) return "working";
|
|
771
|
+
if (row.runtimeStatus === "busy") return "working";
|
|
766
772
|
const attention = state.attention.find((item) => item.key === row.key)?.kind;
|
|
767
773
|
if (attention) return attention;
|
|
768
|
-
if (row.runtimeStatus === "busy") return "working";
|
|
769
774
|
if (row.runtimeStatus === "running") return "running";
|
|
770
775
|
if (row.live || row.runtimeStatus === "idle") return "recent";
|
|
771
776
|
return "idle";
|
|
@@ -1173,6 +1178,7 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
|
|
|
1173
1178
|
const row = attached?.key ? state.sessions.find((item) => item.key === attached.key) : null;
|
|
1174
1179
|
const activeElsewhere = row?.runtimeStatus === "running" || row?.runtimeStatus === "busy" || row?.runtimeStatus === "idle";
|
|
1175
1180
|
const resume = canContinueHere(state);
|
|
1181
|
+
const terminal = resume && state.continuationModes?.includes("terminal");
|
|
1176
1182
|
const join = state.canAttach;
|
|
1177
1183
|
const branch = state.canBranch;
|
|
1178
1184
|
if (!resume && !join && !branch) return /* @__PURE__ */ jsx3("div", { class: "scui-continuation", children: /* @__PURE__ */ jsxs3("span", { children: [
|
|
@@ -1184,7 +1190,10 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
|
|
|
1184
1190
|
/* @__PURE__ */ jsx3("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
|
|
1185
1191
|
/* @__PURE__ */ jsx3("small", { children: join ? "Join the proven live runtime without taking it over." : resume ? `Resume this ${harnessDisplayName(state.harness)} session here.` : "Start an independent continuation." })
|
|
1186
1192
|
] }),
|
|
1187
|
-
/* @__PURE__ */
|
|
1193
|
+
/* @__PURE__ */ jsxs3("span", { class: "scui-continuation-actions", children: [
|
|
1194
|
+
/* @__PURE__ */ jsx3("button", { type: "button", disabled: Boolean(state.operation), onClick: () => adapter.onIntent(join ? { action: "join" } : resume ? { action: "resume", mode: "headless" } : { action: "branch" }), children: join ? labels.joinLive : resume ? labels.continueHere : labels.forkHere }),
|
|
1195
|
+
terminal ? /* @__PURE__ */ jsx3("button", { type: "button", class: "scui-secondary", disabled: Boolean(state.operation), onClick: () => adapter.onIntent({ action: "resume", mode: "terminal" }), children: labels.continueWithTerminal ?? DEFAULT_LABELS.continueWithTerminal }) : null
|
|
1196
|
+
] })
|
|
1188
1197
|
] });
|
|
1189
1198
|
}
|
|
1190
1199
|
function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`, pendingStatus = null, restoreDraft = null, onPending, onDraftRestored }) {
|
|
@@ -2186,7 +2195,7 @@ import { jsx as jsx8, jsxs as jsxs7 } from "preact/jsx-runtime";
|
|
|
2186
2195
|
function HarnessAdvisory({ state, onReview }) {
|
|
2187
2196
|
const advisory = state.interopSettings?.advisories[0];
|
|
2188
2197
|
if (!advisory && !state.interopSettingsError) return null;
|
|
2189
|
-
return /* @__PURE__ */ jsxs7("
|
|
2198
|
+
return /* @__PURE__ */ jsxs7("div", { class: "scui-advisory", "data-severity": advisory?.severity ?? "error", role: advisory?.severity === "error" || !advisory ? "alert" : "status", children: [
|
|
2190
2199
|
/* @__PURE__ */ jsx8("span", { "aria-hidden": "true", children: "!" }),
|
|
2191
2200
|
/* @__PURE__ */ jsxs7("span", { children: [
|
|
2192
2201
|
/* @__PURE__ */ jsx8("strong", { children: advisory?.title ?? "Could not inspect harness settings" }),
|
package/composer.mjs
CHANGED
|
@@ -17,6 +17,7 @@ var DEFAULT_LABELS = Object.freeze({
|
|
|
17
17
|
searchChats: "Search chats",
|
|
18
18
|
askAgent: "Ask your agent\u2026",
|
|
19
19
|
continueHere: "Continue here",
|
|
20
|
+
continueWithTerminal: "Continue with terminal",
|
|
20
21
|
joinLive: "Join live",
|
|
21
22
|
forkHere: "Fork here"
|
|
22
23
|
});
|
|
@@ -32,6 +33,7 @@ var EMPTY_UI_STATE = Object.freeze({
|
|
|
32
33
|
strategy: null,
|
|
33
34
|
canSend: false,
|
|
34
35
|
canResume: false,
|
|
36
|
+
continuationModes: Object.freeze([]),
|
|
35
37
|
canBranch: false,
|
|
36
38
|
canAttach: false,
|
|
37
39
|
canDetach: false,
|
|
@@ -257,6 +259,7 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
|
|
|
257
259
|
const row = attached?.key ? state.sessions.find((item) => item.key === attached.key) : null;
|
|
258
260
|
const activeElsewhere = row?.runtimeStatus === "running" || row?.runtimeStatus === "busy" || row?.runtimeStatus === "idle";
|
|
259
261
|
const resume = canContinueHere(state);
|
|
262
|
+
const terminal = resume && state.continuationModes?.includes("terminal");
|
|
260
263
|
const join = state.canAttach;
|
|
261
264
|
const branch = state.canBranch;
|
|
262
265
|
if (!resume && !join && !branch) return /* @__PURE__ */ jsx3("div", { class: "scui-continuation", children: /* @__PURE__ */ jsxs3("span", { children: [
|
|
@@ -268,7 +271,10 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
|
|
|
268
271
|
/* @__PURE__ */ jsx3("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
|
|
269
272
|
/* @__PURE__ */ jsx3("small", { children: join ? "Join the proven live runtime without taking it over." : resume ? `Resume this ${harnessDisplayName(state.harness)} session here.` : "Start an independent continuation." })
|
|
270
273
|
] }),
|
|
271
|
-
/* @__PURE__ */
|
|
274
|
+
/* @__PURE__ */ jsxs3("span", { class: "scui-continuation-actions", children: [
|
|
275
|
+
/* @__PURE__ */ jsx3("button", { type: "button", disabled: Boolean(state.operation), onClick: () => adapter.onIntent(join ? { action: "join" } : resume ? { action: "resume", mode: "headless" } : { action: "branch" }), children: join ? labels.joinLive : resume ? labels.continueHere : labels.forkHere }),
|
|
276
|
+
terminal ? /* @__PURE__ */ jsx3("button", { type: "button", class: "scui-secondary", disabled: Boolean(state.operation), onClick: () => adapter.onIntent({ action: "resume", mode: "terminal" }), children: labels.continueWithTerminal ?? DEFAULT_LABELS.continueWithTerminal }) : null
|
|
277
|
+
] })
|
|
272
278
|
] });
|
|
273
279
|
}
|
|
274
280
|
function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`, pendingStatus = null, restoreDraft = null, onPending, onDraftRestored }) {
|
package/controller.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
} from '@volter-ai-dev/supercode-client';
|
|
9
9
|
import type {
|
|
10
10
|
AttachedSessionModel,
|
|
11
|
+
ContinuationMode,
|
|
11
12
|
SessionAttention,
|
|
12
13
|
SessionRowModel,
|
|
13
14
|
StartupPhase,
|
|
@@ -40,6 +41,8 @@ export interface ClientProjectionOptions {
|
|
|
40
41
|
exportBackTarget?: SessionFormat | null;
|
|
41
42
|
exportReceipt?: SupercodeUiState['exportReceipt'];
|
|
42
43
|
reductionReceipt?: SupercodeUiState['reductionReceipt'];
|
|
44
|
+
/** Host-provided execution strategies. A plain controller projects headless resume only. */
|
|
45
|
+
continuationModes?: ContinuationMode[];
|
|
43
46
|
/** Trusted-host inventory and lifecycle overlays (for example a machine-wide session catalog). */
|
|
44
47
|
sessions?: SessionRowModel[];
|
|
45
48
|
attached?: AttachedSessionModel | null;
|
|
@@ -81,6 +84,7 @@ export interface ControllerBindingOptions {
|
|
|
81
84
|
onAcknowledge?: (key: string) => void | Promise<void>;
|
|
82
85
|
onLoadSessions?: () => void | Promise<void>;
|
|
83
86
|
onLoadEarlier?: () => void | Promise<void>;
|
|
87
|
+
onResumeTerminal?: (intent: Extract<SupercodeUiIntent, { action: 'resume' }>) => void | Promise<void>;
|
|
84
88
|
copyText?: UiAdapter['copyText'];
|
|
85
89
|
resolveImage?: UiAdapter['resolveImage'];
|
|
86
90
|
}
|
package/controller.mjs
CHANGED
|
@@ -366,6 +366,7 @@ function projectClientSnapshotInternal(snapshot, options, imageRegistry) {
|
|
|
366
366
|
strategy: snapshot.connection?.strategy ?? null,
|
|
367
367
|
canSend: actions.send === true,
|
|
368
368
|
canResume: actions.resume === true,
|
|
369
|
+
continuationModes: options.continuationModes ?? (actions.resume === true ? ['headless'] : []),
|
|
369
370
|
canBranch: actions.branch === true,
|
|
370
371
|
canAttach: actions.attach === true,
|
|
371
372
|
canDetach: actions.detach === true,
|
|
@@ -449,7 +450,14 @@ async function dispatchStandard(controller, intent, options) {
|
|
|
449
450
|
await controller.dispatch({ type: 'start', harness: intent.harness });
|
|
450
451
|
return controller.dispatch({ type: 'send', text: intent.text, ...(intent.context?.length ? { context: intent.context } : {}), ...(intent.images?.length ? { images: intent.images } : {}) });
|
|
451
452
|
}
|
|
452
|
-
if (intent.action === 'resume' && active)
|
|
453
|
+
if (intent.action === 'resume' && active) {
|
|
454
|
+
if (intent.mode === 'terminal') {
|
|
455
|
+
return options.onResumeTerminal
|
|
456
|
+
? options.onResumeTerminal(intent)
|
|
457
|
+
: options.onUnsupported?.(intent);
|
|
458
|
+
}
|
|
459
|
+
return controller.dispatch({ type: 'resume', sessionKey: active });
|
|
460
|
+
}
|
|
453
461
|
if (intent.action === 'join' && active) return controller.dispatch({ type: 'attach', sessionKey: active });
|
|
454
462
|
if (intent.action === 'detach') return controller.dispatch({ type: 'detach' });
|
|
455
463
|
if (intent.action === 'branch' && active) return controller.dispatch({ type: 'branch', sessionKey: active, ...(intent.targetHarness ? { targetHarness: intent.targetHarness } : {}) });
|
package/conversation.mjs
CHANGED
|
@@ -18,6 +18,7 @@ var DEFAULT_LABELS = Object.freeze({
|
|
|
18
18
|
searchChats: "Search chats",
|
|
19
19
|
askAgent: "Ask your agent\u2026",
|
|
20
20
|
continueHere: "Continue here",
|
|
21
|
+
continueWithTerminal: "Continue with terminal",
|
|
21
22
|
joinLive: "Join live",
|
|
22
23
|
forkHere: "Fork here"
|
|
23
24
|
});
|
|
@@ -33,6 +34,7 @@ var EMPTY_UI_STATE = Object.freeze({
|
|
|
33
34
|
strategy: null,
|
|
34
35
|
canSend: false,
|
|
35
36
|
canResume: false,
|
|
37
|
+
continuationModes: Object.freeze([]),
|
|
36
38
|
canBranch: false,
|
|
37
39
|
canAttach: false,
|
|
38
40
|
canDetach: false,
|
package/core.d.ts
CHANGED
package/core.mjs
CHANGED
|
@@ -14,6 +14,7 @@ export const DEFAULT_LABELS = Object.freeze({
|
|
|
14
14
|
searchChats: 'Search chats',
|
|
15
15
|
askAgent: 'Ask your agent…',
|
|
16
16
|
continueHere: 'Continue here',
|
|
17
|
+
continueWithTerminal: 'Continue with terminal',
|
|
17
18
|
joinLive: 'Join live',
|
|
18
19
|
forkHere: 'Fork here',
|
|
19
20
|
});
|
|
@@ -30,6 +31,7 @@ export const EMPTY_UI_STATE = Object.freeze({
|
|
|
30
31
|
strategy: null,
|
|
31
32
|
canSend: false,
|
|
32
33
|
canResume: false,
|
|
34
|
+
continuationModes: Object.freeze([]),
|
|
33
35
|
canBranch: false,
|
|
34
36
|
canAttach: false,
|
|
35
37
|
canDetach: false,
|
|
@@ -754,6 +756,10 @@ export function normalizeUiState(value) {
|
|
|
754
756
|
const pill = record(raw.pill);
|
|
755
757
|
const history = record(raw.history);
|
|
756
758
|
const attachError = record(raw.attachError);
|
|
759
|
+
const canResume = raw.canResume === true;
|
|
760
|
+
const continuationModes = Array.isArray(raw.continuationModes)
|
|
761
|
+
? [...new Set(raw.continuationModes.filter((mode) => mode === 'headless' || mode === 'terminal'))]
|
|
762
|
+
: canResume ? ['headless'] : [];
|
|
757
763
|
return {
|
|
758
764
|
pill: { tone: ['live', 'warn', 'dead'].includes(pill?.tone) ? pill.tone : 'off', label: string(pill?.label, 'connecting…') },
|
|
759
765
|
startup: STARTUP.has(raw.startup) ? raw.startup : 'connecting',
|
|
@@ -765,7 +771,8 @@ export function normalizeUiState(value) {
|
|
|
765
771
|
mode: MODES.has(raw.mode) ? raw.mode : 'none',
|
|
766
772
|
strategy: STRATEGIES.has(raw.strategy) ? raw.strategy : null,
|
|
767
773
|
canSend: raw.canSend === true,
|
|
768
|
-
canResume
|
|
774
|
+
canResume,
|
|
775
|
+
continuationModes,
|
|
769
776
|
canBranch: raw.canBranch === true,
|
|
770
777
|
canAttach: raw.canAttach === true,
|
|
771
778
|
canDetach: raw.canDetach === true,
|
|
@@ -818,9 +825,11 @@ export function sessionDisplayName(session) {
|
|
|
818
825
|
export function sessionActivity(state, row) {
|
|
819
826
|
if (state.needsInput && row.active) return 'needs-input';
|
|
820
827
|
if (state.busy && row.active) return 'working';
|
|
828
|
+
// Unread/finished attention has its own badge. It must not suppress the
|
|
829
|
+
// transient working indicator when the harness proves this row is busy.
|
|
830
|
+
if (row.runtimeStatus === 'busy') return 'working';
|
|
821
831
|
const attention = state.attention.find((item) => item.key === row.key)?.kind;
|
|
822
832
|
if (attention) return attention;
|
|
823
|
-
if (row.runtimeStatus === 'busy') return 'working';
|
|
824
833
|
if (row.runtimeStatus === 'running') return 'running';
|
|
825
834
|
if (row.live || row.runtimeStatus === 'idle') return 'recent';
|
|
826
835
|
return 'idle';
|
package/embed.mjs
CHANGED
|
@@ -20,6 +20,7 @@ var DEFAULT_LABELS = Object.freeze({
|
|
|
20
20
|
searchChats: "Search chats",
|
|
21
21
|
askAgent: "Ask your agent\u2026",
|
|
22
22
|
continueHere: "Continue here",
|
|
23
|
+
continueWithTerminal: "Continue with terminal",
|
|
23
24
|
joinLive: "Join live",
|
|
24
25
|
forkHere: "Fork here"
|
|
25
26
|
});
|
|
@@ -35,6 +36,7 @@ var EMPTY_UI_STATE = Object.freeze({
|
|
|
35
36
|
strategy: null,
|
|
36
37
|
canSend: false,
|
|
37
38
|
canResume: false,
|
|
39
|
+
continuationModes: Object.freeze([]),
|
|
38
40
|
canBranch: false,
|
|
39
41
|
canAttach: false,
|
|
40
42
|
canDetach: false,
|
|
@@ -707,6 +709,8 @@ function normalizeUiState(value) {
|
|
|
707
709
|
const pill = record(raw.pill);
|
|
708
710
|
const history = record(raw.history);
|
|
709
711
|
const attachError = record(raw.attachError);
|
|
712
|
+
const canResume = raw.canResume === true;
|
|
713
|
+
const continuationModes = Array.isArray(raw.continuationModes) ? [...new Set(raw.continuationModes.filter((mode) => mode === "headless" || mode === "terminal"))] : canResume ? ["headless"] : [];
|
|
710
714
|
return {
|
|
711
715
|
pill: { tone: ["live", "warn", "dead"].includes(pill?.tone) ? pill.tone : "off", label: string(pill?.label, "connecting\u2026") },
|
|
712
716
|
startup: STARTUP.has(raw.startup) ? raw.startup : "connecting",
|
|
@@ -718,7 +722,8 @@ function normalizeUiState(value) {
|
|
|
718
722
|
mode: MODES.has(raw.mode) ? raw.mode : "none",
|
|
719
723
|
strategy: STRATEGIES.has(raw.strategy) ? raw.strategy : null,
|
|
720
724
|
canSend: raw.canSend === true,
|
|
721
|
-
canResume
|
|
725
|
+
canResume,
|
|
726
|
+
continuationModes,
|
|
722
727
|
canBranch: raw.canBranch === true,
|
|
723
728
|
canAttach: raw.canAttach === true,
|
|
724
729
|
canDetach: raw.canDetach === true,
|
|
@@ -766,9 +771,9 @@ function sessionDisplayName(session) {
|
|
|
766
771
|
function sessionActivity(state, row) {
|
|
767
772
|
if (state.needsInput && row.active) return "needs-input";
|
|
768
773
|
if (state.busy && row.active) return "working";
|
|
774
|
+
if (row.runtimeStatus === "busy") return "working";
|
|
769
775
|
const attention = state.attention.find((item) => item.key === row.key)?.kind;
|
|
770
776
|
if (attention) return attention;
|
|
771
|
-
if (row.runtimeStatus === "busy") return "working";
|
|
772
777
|
if (row.runtimeStatus === "running") return "running";
|
|
773
778
|
if (row.live || row.runtimeStatus === "idle") return "recent";
|
|
774
779
|
return "idle";
|
|
@@ -1179,6 +1184,7 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
|
|
|
1179
1184
|
const row = attached?.key ? state.sessions.find((item) => item.key === attached.key) : null;
|
|
1180
1185
|
const activeElsewhere = row?.runtimeStatus === "running" || row?.runtimeStatus === "busy" || row?.runtimeStatus === "idle";
|
|
1181
1186
|
const resume = canContinueHere(state);
|
|
1187
|
+
const terminal = resume && state.continuationModes?.includes("terminal");
|
|
1182
1188
|
const join = state.canAttach;
|
|
1183
1189
|
const branch = state.canBranch;
|
|
1184
1190
|
if (!resume && !join && !branch) return /* @__PURE__ */ jsx3("div", { class: "scui-continuation", children: /* @__PURE__ */ jsxs3("span", { children: [
|
|
@@ -1190,7 +1196,10 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
|
|
|
1190
1196
|
/* @__PURE__ */ jsx3("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
|
|
1191
1197
|
/* @__PURE__ */ jsx3("small", { children: join ? "Join the proven live runtime without taking it over." : resume ? `Resume this ${harnessDisplayName(state.harness)} session here.` : "Start an independent continuation." })
|
|
1192
1198
|
] }),
|
|
1193
|
-
/* @__PURE__ */
|
|
1199
|
+
/* @__PURE__ */ jsxs3("span", { class: "scui-continuation-actions", children: [
|
|
1200
|
+
/* @__PURE__ */ jsx3("button", { type: "button", disabled: Boolean(state.operation), onClick: () => adapter.onIntent(join ? { action: "join" } : resume ? { action: "resume", mode: "headless" } : { action: "branch" }), children: join ? labels.joinLive : resume ? labels.continueHere : labels.forkHere }),
|
|
1201
|
+
terminal ? /* @__PURE__ */ jsx3("button", { type: "button", class: "scui-secondary", disabled: Boolean(state.operation), onClick: () => adapter.onIntent({ action: "resume", mode: "terminal" }), children: labels.continueWithTerminal ?? DEFAULT_LABELS.continueWithTerminal }) : null
|
|
1202
|
+
] })
|
|
1194
1203
|
] });
|
|
1195
1204
|
}
|
|
1196
1205
|
function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`, pendingStatus = null, restoreDraft = null, onPending, onDraftRestored }) {
|
|
@@ -2165,7 +2174,7 @@ import { jsx as jsx8, jsxs as jsxs7 } from "preact/jsx-runtime";
|
|
|
2165
2174
|
function HarnessAdvisory({ state, onReview }) {
|
|
2166
2175
|
const advisory = state.interopSettings?.advisories[0];
|
|
2167
2176
|
if (!advisory && !state.interopSettingsError) return null;
|
|
2168
|
-
return /* @__PURE__ */ jsxs7("
|
|
2177
|
+
return /* @__PURE__ */ jsxs7("div", { class: "scui-advisory", "data-severity": advisory?.severity ?? "error", role: advisory?.severity === "error" || !advisory ? "alert" : "status", children: [
|
|
2169
2178
|
/* @__PURE__ */ jsx8("span", { "aria-hidden": "true", children: "!" }),
|
|
2170
2179
|
/* @__PURE__ */ jsxs7("span", { children: [
|
|
2171
2180
|
/* @__PURE__ */ jsx8("strong", { children: advisory?.title ?? "Could not inspect harness settings" }),
|
package/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export type UiTone = 'live' | 'warn' | 'dead' | 'off';
|
|
|
5
5
|
export type StartupPhase = 'connecting' | 'starting' | 'discovering' | 'ready';
|
|
6
6
|
export type SessionMode = 'none' | 'control' | 'mirror';
|
|
7
7
|
export type ControlStrategy = 'start' | 'resume' | 'attach' | 'branch' | 'reduce' | null;
|
|
8
|
+
export type ContinuationMode = 'headless' | 'terminal';
|
|
8
9
|
export type SessionActivity = 'idle' | 'recent' | 'running' | 'working' | 'needs-input' | 'finished' | 'failed' | 'unseen';
|
|
9
10
|
|
|
10
11
|
export interface HarnessOption {
|
|
@@ -234,6 +235,8 @@ export interface SupercodeUiState {
|
|
|
234
235
|
strategy: ControlStrategy;
|
|
235
236
|
canSend: boolean;
|
|
236
237
|
canResume: boolean;
|
|
238
|
+
/** Execution strategies the current host can actually provide. Headless is the default. */
|
|
239
|
+
continuationModes: ContinuationMode[];
|
|
237
240
|
canBranch: boolean;
|
|
238
241
|
canAttach: boolean;
|
|
239
242
|
canDetach: boolean;
|
|
@@ -285,7 +288,7 @@ export type SupercodeUiIntent =
|
|
|
285
288
|
| { action: 'draft'; text: string }
|
|
286
289
|
| { action: 'send'; text: string; context?: TranscriptContext[]; images?: Array<TranscriptImage & { url: string }> }
|
|
287
290
|
| { action: 'new'; harness: HarnessId; text: string; context?: TranscriptContext[]; images?: Array<TranscriptImage & { url: string }> }
|
|
288
|
-
| { action: 'resume' }
|
|
291
|
+
| { action: 'resume'; mode?: ContinuationMode }
|
|
289
292
|
| { action: 'join' }
|
|
290
293
|
| { action: 'detach' }
|
|
291
294
|
| { action: 'branch'; targetHarness?: HarnessId }
|
|
@@ -316,6 +319,7 @@ export interface MessengerLabels {
|
|
|
316
319
|
searchChats: string;
|
|
317
320
|
askAgent: string;
|
|
318
321
|
continueHere: string;
|
|
322
|
+
continueWithTerminal?: string;
|
|
319
323
|
joinLive: string;
|
|
320
324
|
forkHere: string;
|
|
321
325
|
}
|
package/messenger.mjs
CHANGED
|
@@ -17,6 +17,7 @@ var DEFAULT_LABELS = Object.freeze({
|
|
|
17
17
|
searchChats: "Search chats",
|
|
18
18
|
askAgent: "Ask your agent\u2026",
|
|
19
19
|
continueHere: "Continue here",
|
|
20
|
+
continueWithTerminal: "Continue with terminal",
|
|
20
21
|
joinLive: "Join live",
|
|
21
22
|
forkHere: "Fork here"
|
|
22
23
|
});
|
|
@@ -32,6 +33,7 @@ var EMPTY_UI_STATE = Object.freeze({
|
|
|
32
33
|
strategy: null,
|
|
33
34
|
canSend: false,
|
|
34
35
|
canResume: false,
|
|
36
|
+
continuationModes: Object.freeze([]),
|
|
35
37
|
canBranch: false,
|
|
36
38
|
canAttach: false,
|
|
37
39
|
canDetach: false,
|
|
@@ -704,6 +706,8 @@ function normalizeUiState(value) {
|
|
|
704
706
|
const pill = record(raw.pill);
|
|
705
707
|
const history = record(raw.history);
|
|
706
708
|
const attachError = record(raw.attachError);
|
|
709
|
+
const canResume = raw.canResume === true;
|
|
710
|
+
const continuationModes = Array.isArray(raw.continuationModes) ? [...new Set(raw.continuationModes.filter((mode) => mode === "headless" || mode === "terminal"))] : canResume ? ["headless"] : [];
|
|
707
711
|
return {
|
|
708
712
|
pill: { tone: ["live", "warn", "dead"].includes(pill?.tone) ? pill.tone : "off", label: string(pill?.label, "connecting\u2026") },
|
|
709
713
|
startup: STARTUP.has(raw.startup) ? raw.startup : "connecting",
|
|
@@ -715,7 +719,8 @@ function normalizeUiState(value) {
|
|
|
715
719
|
mode: MODES.has(raw.mode) ? raw.mode : "none",
|
|
716
720
|
strategy: STRATEGIES.has(raw.strategy) ? raw.strategy : null,
|
|
717
721
|
canSend: raw.canSend === true,
|
|
718
|
-
canResume
|
|
722
|
+
canResume,
|
|
723
|
+
continuationModes,
|
|
719
724
|
canBranch: raw.canBranch === true,
|
|
720
725
|
canAttach: raw.canAttach === true,
|
|
721
726
|
canDetach: raw.canDetach === true,
|
|
@@ -763,9 +768,9 @@ function sessionDisplayName(session) {
|
|
|
763
768
|
function sessionActivity(state, row) {
|
|
764
769
|
if (state.needsInput && row.active) return "needs-input";
|
|
765
770
|
if (state.busy && row.active) return "working";
|
|
771
|
+
if (row.runtimeStatus === "busy") return "working";
|
|
766
772
|
const attention = state.attention.find((item) => item.key === row.key)?.kind;
|
|
767
773
|
if (attention) return attention;
|
|
768
|
-
if (row.runtimeStatus === "busy") return "working";
|
|
769
774
|
if (row.runtimeStatus === "running") return "running";
|
|
770
775
|
if (row.live || row.runtimeStatus === "idle") return "recent";
|
|
771
776
|
return "idle";
|
|
@@ -1176,6 +1181,7 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
|
|
|
1176
1181
|
const row = attached?.key ? state.sessions.find((item) => item.key === attached.key) : null;
|
|
1177
1182
|
const activeElsewhere = row?.runtimeStatus === "running" || row?.runtimeStatus === "busy" || row?.runtimeStatus === "idle";
|
|
1178
1183
|
const resume = canContinueHere(state);
|
|
1184
|
+
const terminal = resume && state.continuationModes?.includes("terminal");
|
|
1179
1185
|
const join = state.canAttach;
|
|
1180
1186
|
const branch = state.canBranch;
|
|
1181
1187
|
if (!resume && !join && !branch) return /* @__PURE__ */ jsx3("div", { class: "scui-continuation", children: /* @__PURE__ */ jsxs3("span", { children: [
|
|
@@ -1187,7 +1193,10 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
|
|
|
1187
1193
|
/* @__PURE__ */ jsx3("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
|
|
1188
1194
|
/* @__PURE__ */ jsx3("small", { children: join ? "Join the proven live runtime without taking it over." : resume ? `Resume this ${harnessDisplayName(state.harness)} session here.` : "Start an independent continuation." })
|
|
1189
1195
|
] }),
|
|
1190
|
-
/* @__PURE__ */
|
|
1196
|
+
/* @__PURE__ */ jsxs3("span", { class: "scui-continuation-actions", children: [
|
|
1197
|
+
/* @__PURE__ */ jsx3("button", { type: "button", disabled: Boolean(state.operation), onClick: () => adapter.onIntent(join ? { action: "join" } : resume ? { action: "resume", mode: "headless" } : { action: "branch" }), children: join ? labels.joinLive : resume ? labels.continueHere : labels.forkHere }),
|
|
1198
|
+
terminal ? /* @__PURE__ */ jsx3("button", { type: "button", class: "scui-secondary", disabled: Boolean(state.operation), onClick: () => adapter.onIntent({ action: "resume", mode: "terminal" }), children: labels.continueWithTerminal ?? DEFAULT_LABELS.continueWithTerminal }) : null
|
|
1199
|
+
] })
|
|
1191
1200
|
] });
|
|
1192
1201
|
}
|
|
1193
1202
|
function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`, pendingStatus = null, restoreDraft = null, onPending, onDraftRestored }) {
|
|
@@ -2162,7 +2171,7 @@ import { jsx as jsx8, jsxs as jsxs7 } from "preact/jsx-runtime";
|
|
|
2162
2171
|
function HarnessAdvisory({ state, onReview }) {
|
|
2163
2172
|
const advisory = state.interopSettings?.advisories[0];
|
|
2164
2173
|
if (!advisory && !state.interopSettingsError) return null;
|
|
2165
|
-
return /* @__PURE__ */ jsxs7("
|
|
2174
|
+
return /* @__PURE__ */ jsxs7("div", { class: "scui-advisory", "data-severity": advisory?.severity ?? "error", role: advisory?.severity === "error" || !advisory ? "alert" : "status", children: [
|
|
2166
2175
|
/* @__PURE__ */ jsx8("span", { "aria-hidden": "true", children: "!" }),
|
|
2167
2176
|
/* @__PURE__ */ jsxs7("span", { children: [
|
|
2168
2177
|
/* @__PURE__ */ jsx8("strong", { children: advisory?.title ?? "Could not inspect harness settings" }),
|
package/package.json
CHANGED
package/sessions.mjs
CHANGED
|
@@ -17,6 +17,7 @@ var DEFAULT_LABELS = Object.freeze({
|
|
|
17
17
|
searchChats: "Search chats",
|
|
18
18
|
askAgent: "Ask your agent\u2026",
|
|
19
19
|
continueHere: "Continue here",
|
|
20
|
+
continueWithTerminal: "Continue with terminal",
|
|
20
21
|
joinLive: "Join live",
|
|
21
22
|
forkHere: "Fork here"
|
|
22
23
|
});
|
|
@@ -32,6 +33,7 @@ var EMPTY_UI_STATE = Object.freeze({
|
|
|
32
33
|
strategy: null,
|
|
33
34
|
canSend: false,
|
|
34
35
|
canResume: false,
|
|
36
|
+
continuationModes: Object.freeze([]),
|
|
35
37
|
canBranch: false,
|
|
36
38
|
canAttach: false,
|
|
37
39
|
canDetach: false,
|
|
@@ -81,9 +83,9 @@ function sessionDisplayName(session) {
|
|
|
81
83
|
function sessionActivity(state, row) {
|
|
82
84
|
if (state.needsInput && row.active) return "needs-input";
|
|
83
85
|
if (state.busy && row.active) return "working";
|
|
86
|
+
if (row.runtimeStatus === "busy") return "working";
|
|
84
87
|
const attention = state.attention.find((item) => item.key === row.key)?.kind;
|
|
85
88
|
if (attention) return attention;
|
|
86
|
-
if (row.runtimeStatus === "busy") return "working";
|
|
87
89
|
if (row.runtimeStatus === "running") return "running";
|
|
88
90
|
if (row.live || row.runtimeStatus === "idle") return "recent";
|
|
89
91
|
return "idle";
|
package/settings.mjs
CHANGED
|
@@ -17,6 +17,7 @@ var DEFAULT_LABELS = Object.freeze({
|
|
|
17
17
|
searchChats: "Search chats",
|
|
18
18
|
askAgent: "Ask your agent\u2026",
|
|
19
19
|
continueHere: "Continue here",
|
|
20
|
+
continueWithTerminal: "Continue with terminal",
|
|
20
21
|
joinLive: "Join live",
|
|
21
22
|
forkHere: "Fork here"
|
|
22
23
|
});
|
|
@@ -32,6 +33,7 @@ var EMPTY_UI_STATE = Object.freeze({
|
|
|
32
33
|
strategy: null,
|
|
33
34
|
canSend: false,
|
|
34
35
|
canResume: false,
|
|
36
|
+
continuationModes: Object.freeze([]),
|
|
35
37
|
canBranch: false,
|
|
36
38
|
canAttach: false,
|
|
37
39
|
canDetach: false,
|
|
@@ -120,7 +122,7 @@ import { jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
|
|
|
120
122
|
function HarnessAdvisory({ state, onReview }) {
|
|
121
123
|
const advisory = state.interopSettings?.advisories[0];
|
|
122
124
|
if (!advisory && !state.interopSettingsError) return null;
|
|
123
|
-
return /* @__PURE__ */ jsxs2("
|
|
125
|
+
return /* @__PURE__ */ jsxs2("div", { class: "scui-advisory", "data-severity": advisory?.severity ?? "error", role: advisory?.severity === "error" || !advisory ? "alert" : "status", children: [
|
|
124
126
|
/* @__PURE__ */ jsx2("span", { "aria-hidden": "true", children: "!" }),
|
|
125
127
|
/* @__PURE__ */ jsxs2("span", { children: [
|
|
126
128
|
/* @__PURE__ */ jsx2("strong", { children: advisory?.title ?? "Could not inspect harness settings" }),
|
package/styles.css
CHANGED
|
@@ -179,7 +179,7 @@
|
|
|
179
179
|
.scui-plan > summary { display:flex; justify-content:space-between; padding:6px 8px; border:1px solid var(--scui-border); border-radius:7px; background:var(--scui-bg-raised) }.scui-plan ol { display:grid; gap:4px; box-sizing:border-box; max-height:140px; margin:5px 0 0; padding:7px 8px 7px 25px; overflow:auto; border:1px solid var(--scui-border); border-radius:7px }.scui-plan li[data-status="completed"] { color:var(--scui-fg); text-decoration:line-through }
|
|
180
180
|
.scui-working { display:flex; align-items:center; gap:4px; color:var(--scui-muted) }.scui-working > span { color:var(--scui-accent) }.scui-working > i { width:4px; height:4px; border-radius:50%; background:currentColor; animation:scui-dots 1.2s infinite }.scui-working > i:nth-of-type(2) { animation-delay:.15s }.scui-working > i:nth-of-type(3) { animation-delay:.3s }
|
|
181
181
|
|
|
182
|
-
.scui-continuation { display:flex; align-items:center; gap:8px; padding:7px 9px; border-top:1px solid var(--scui-border); background:var(--scui-bg-raised) }.scui-continuation > span { display:grid; flex:1 }.scui-continuation small { color:var(--scui-muted); font-size:10px }.scui-continuation button { padding:5px 8px; border:1px solid var(--scui-accent); border-radius:7px; background:transparent; color:var(--scui-accent); cursor:pointer }
|
|
182
|
+
.scui-continuation { display:flex; align-items:center; gap:8px; padding:7px 9px; border-top:1px solid var(--scui-border); background:var(--scui-bg-raised) }.scui-continuation > span:not(.scui-continuation-actions) { display:grid; flex:1 }.scui-continuation small { color:var(--scui-muted); font-size:10px }.scui-continuation-actions { display:flex; align-items:center; gap:5px; flex-wrap:wrap; justify-content:flex-end }.scui-continuation button { padding:5px 8px; border:1px solid var(--scui-accent); border-radius:7px; background:transparent; color:var(--scui-accent); cursor:pointer; white-space:nowrap }.scui-continuation button.scui-secondary { border-color:var(--scui-border-strong); color:var(--scui-fg) }
|
|
183
183
|
.scui-compose { flex:none; padding:8px; border-top:1px solid var(--scui-border); background:var(--scui-bg-raised) }
|
|
184
184
|
.scui-envelope { display:flex; align-items:flex-end; gap:7px; padding:7px; border:1px solid var(--scui-border-strong); border-radius:16px; background:var(--scui-bg) }.scui-envelope textarea { flex:1; min-width:0; min-height:34px; max-height:150px; resize:none; overflow-y:hidden; border:0; outline:0; background:transparent; color:var(--scui-fg) }.scui-envelope > span { display:flex; gap:4px }
|
|
185
185
|
.scui-envelope.scui-drop-target { border-color:var(--scui-accent); background:color-mix(in srgb,var(--scui-accent) 7%,var(--scui-bg)); box-shadow:0 0 0 2px color-mix(in srgb,var(--scui-accent) 16%,transparent) }
|