@kasenri/dsh-orbit 0.5.1 → 0.5.3
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/lib/activation.js +72 -5
- package/lib/client.js +220 -85
- package/lib/index.js +12 -2
- package/lib/session-state.js +80 -0
- package/package.json +1 -1
package/lib/activation.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* tool + `OrbitService` + Supervisor remain the sole Orbit runtime.
|
|
13
13
|
*/
|
|
14
14
|
import { createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
15
|
+
import { ORBIT_TOGGLE_COMMAND, parseOrbitToggle } from "./session-state.js";
|
|
15
16
|
export const AGENT_ORBIT_COMMAND = 'agent-orbit';
|
|
16
17
|
/** Strict gesture: the command must begin a genuine user message line. */
|
|
17
18
|
const GESTURE = /^\/agent-orbit(?=$|[\t\n\r ])/u;
|
|
@@ -38,6 +39,22 @@ export function invokedOrbitActivation(messages) {
|
|
|
38
39
|
}
|
|
39
40
|
return undefined;
|
|
40
41
|
}
|
|
42
|
+
/** The newest genuine user message text, as the implicit activation's goal. */
|
|
43
|
+
export function invokedOrbitMessage(messages) {
|
|
44
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
45
|
+
const message = messages[index];
|
|
46
|
+
if (message === undefined || message.source.kind !== 'user')
|
|
47
|
+
continue;
|
|
48
|
+
const text = message.content
|
|
49
|
+
.filter((block) => block.type === 'text')
|
|
50
|
+
.map((block) => block.text)
|
|
51
|
+
.join('\n')
|
|
52
|
+
.trim();
|
|
53
|
+
if (text !== '')
|
|
54
|
+
return { goal: text };
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
41
58
|
/**
|
|
42
59
|
* The one deterministic directive injected for an explicit activation. It only
|
|
43
60
|
* states the activation and carries the goal verbatim; every Orbit rule still
|
|
@@ -53,6 +70,17 @@ export function buildOrbitActivationDirective(goal) {
|
|
|
53
70
|
: `Goal: ${goal}`);
|
|
54
71
|
return lines.join('\n');
|
|
55
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* The directive for a Session whose toggle is ON: an ordinary user message
|
|
75
|
+
* becomes the Orbit goal without changing the activation machinery.
|
|
76
|
+
*/
|
|
77
|
+
export function buildOrbitSessionDirective(goal) {
|
|
78
|
+
return [
|
|
79
|
+
'Orbit is enabled for this chat, so treat this ordinary user message as the requested goal. Start or resume Orbit through the existing orbit_controller tool; do not ask the user to confirm the mode.',
|
|
80
|
+
'Use the message as the goal, without summarizing or rewriting it.',
|
|
81
|
+
`Goal: ${goal}`,
|
|
82
|
+
].join('\n');
|
|
83
|
+
}
|
|
56
84
|
/**
|
|
57
85
|
* Register the closed-namespace `/agent-orbit` host command.
|
|
58
86
|
*
|
|
@@ -79,29 +107,68 @@ export function registerOrbitCommand(ctx) {
|
|
|
79
107
|
},
|
|
80
108
|
}), 'dsh-orbit: /agent-orbit host command');
|
|
81
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Register the `/orbit-toggle on|off` host command.
|
|
112
|
+
*
|
|
113
|
+
* The command writes nothing itself: the commands runtime logs its own
|
|
114
|
+
* `command/run` record, and the `orbitSession` projection folds that record
|
|
115
|
+
* into the per-Session state the toggle UI reads. Registering it also gives
|
|
116
|
+
* CLI surfaces the same switch.
|
|
117
|
+
*/
|
|
118
|
+
export function registerOrbitToggleCommand(ctx) {
|
|
119
|
+
ctx.effect(() => ctx.commands.register({
|
|
120
|
+
name: ORBIT_TOGGLE_COMMAND,
|
|
121
|
+
description: 'Turn the per-chat Orbit default on or off without starting a run',
|
|
122
|
+
input: { hint: 'on or off' },
|
|
123
|
+
handler(invocation) {
|
|
124
|
+
const enabled = parseOrbitToggle(invocation.rawInput);
|
|
125
|
+
if (enabled === undefined) {
|
|
126
|
+
return { kind: 'error', text: `Usage: /${ORBIT_TOGGLE_COMMAND} on|off — 请输入 on 或 off。` };
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
kind: 'success',
|
|
130
|
+
text: enabled
|
|
131
|
+
? 'Orbit is on for this chat — ordinary messages will enter Orbit.'
|
|
132
|
+
: 'Orbit is off for this chat — ordinary messages stay native.',
|
|
133
|
+
};
|
|
134
|
+
},
|
|
135
|
+
}), 'dsh-orbit: /orbit-toggle host command');
|
|
136
|
+
}
|
|
82
137
|
/**
|
|
83
138
|
* Install the gesture boundary. It runs for every proposed step and injects
|
|
84
139
|
* the activation directive once, for the step that carries the genuine user
|
|
85
140
|
* message. The in-step guard keeps a step that already holds an Orbit
|
|
86
141
|
* directive from gaining a second one, without any durable state.
|
|
142
|
+
*
|
|
143
|
+
* An explicit `/agent-orbit` always wins and always activates, whatever the
|
|
144
|
+
* Session toggle says. Only when no explicit activation is present does the
|
|
145
|
+
* Session toggle (when the composition provides it) promote an ordinary user
|
|
146
|
+
* message into an Orbit goal.
|
|
87
147
|
*/
|
|
88
|
-
export function installOrbitGestureBoundary(ctx) {
|
|
89
|
-
ctx.on('agent/pre-step', async ({ messages, signal }, next) => {
|
|
148
|
+
export function installOrbitGestureBoundary(ctx, options = {}) {
|
|
149
|
+
ctx.on('agent/pre-step', async ({ agent, messages, signal }, next) => {
|
|
90
150
|
const decision = await next();
|
|
91
151
|
if (decision.kind === 'reject')
|
|
92
152
|
return decision;
|
|
93
153
|
if (decision.messages.some((message) => message.source.kind === 'orbit-command'))
|
|
94
154
|
return decision;
|
|
95
|
-
const
|
|
96
|
-
|
|
155
|
+
const explicit = invokedOrbitActivation(messages);
|
|
156
|
+
const implicit = explicit === undefined && options.sessionEnabled?.(agent.session) === true
|
|
157
|
+
? invokedOrbitMessage(messages)
|
|
158
|
+
: undefined;
|
|
159
|
+
if (explicit === undefined && implicit === undefined)
|
|
97
160
|
return decision;
|
|
98
161
|
signal.throwIfAborted();
|
|
162
|
+
const activation = explicit ?? implicit ?? { goal: '' };
|
|
163
|
+
const directive = explicit === undefined
|
|
164
|
+
? buildOrbitSessionDirective(activation.goal)
|
|
165
|
+
: buildOrbitActivationDirective(activation.goal);
|
|
99
166
|
return {
|
|
100
167
|
kind: 'enter',
|
|
101
168
|
messages: [
|
|
102
169
|
...decision.messages,
|
|
103
170
|
createUserMessage({
|
|
104
|
-
content: [{ type: 'text', text:
|
|
171
|
+
content: [{ type: 'text', text: directive }],
|
|
105
172
|
source: { kind: 'orbit-command', ...(activation.goal === '' ? {} : { goal: activation.goal }) },
|
|
106
173
|
}),
|
|
107
174
|
],
|
package/lib/client.js
CHANGED
|
@@ -18,10 +18,47 @@ window.__ModuleLoader__.load({
|
|
|
18
18
|
for (const model of group.models) if (model.id === route.model) return model;
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* UI-only fallback ladder for models whose catalog entry declares no reasoning
|
|
23
|
+
* metadata. The DSH catalog is never modified: this only fills the Orbit
|
|
24
|
+
* picker, and a provider that rejects an effort still fails through the normal
|
|
25
|
+
* selection error path.
|
|
26
|
+
*/
|
|
27
|
+
const FALLBACK_REASONING_EFFORTS = [
|
|
28
|
+
"low",
|
|
29
|
+
"medium",
|
|
30
|
+
"high",
|
|
31
|
+
"xhigh",
|
|
32
|
+
"max"
|
|
33
|
+
];
|
|
34
|
+
const FALLBACK_EFFORT_LABELS = {
|
|
35
|
+
low: "Low",
|
|
36
|
+
medium: "Medium",
|
|
37
|
+
high: "High",
|
|
38
|
+
xhigh: "XHigh",
|
|
39
|
+
max: "Max"
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Effort options for one model: the adapter's explicit list when the catalog
|
|
43
|
+
* declares one, otherwise the Orbit UI fallback ladder.
|
|
44
|
+
*/
|
|
45
|
+
function effortsOf(model) {
|
|
46
|
+
const explicit = model?.reasoning?.efforts ?? [];
|
|
47
|
+
if (explicit.length > 0) return explicit.map((effort) => ({
|
|
48
|
+
id: effort.id,
|
|
49
|
+
name: effort.name
|
|
50
|
+
}));
|
|
51
|
+
return FALLBACK_REASONING_EFFORTS.map((id) => ({
|
|
52
|
+
id,
|
|
53
|
+
name: FALLBACK_EFFORT_LABELS[id] ?? id
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
21
56
|
/** Human effort label for a stored effort id; falls back to the raw id. */
|
|
22
57
|
function effortLabelOf(model, effort) {
|
|
23
58
|
if (effort === void 0 || effort === "") return void 0;
|
|
24
|
-
|
|
59
|
+
const explicit = model?.reasoning?.efforts.find((entry) => entry.id === effort);
|
|
60
|
+
if (explicit !== void 0) return explicit.name;
|
|
61
|
+
return FALLBACK_EFFORT_LABELS[effort] ?? effort;
|
|
25
62
|
}
|
|
26
63
|
/** Display name for a stored route: catalog name, then its model id. */
|
|
27
64
|
function routeLabelOf(state, route) {
|
|
@@ -53,22 +90,20 @@ window.__ModuleLoader__.load({
|
|
|
53
90
|
...effort === void 0 ? {} : { reasoningEffort: effort }
|
|
54
91
|
};
|
|
55
92
|
}
|
|
56
|
-
/**
|
|
93
|
+
/**
|
|
94
|
+
* Effort rows for one model: Provider default (an undefined effort) first,
|
|
95
|
+
* then the model's explicit ladder or the Orbit UI fallback.
|
|
96
|
+
*/
|
|
57
97
|
function effortChoicesOf(model, providerDefaultLabel) {
|
|
58
|
-
|
|
59
|
-
if (reasoning === void 0) return [];
|
|
60
|
-
const choices = [];
|
|
61
|
-
if (reasoning.defaultEffort !== void 0) choices.push({
|
|
98
|
+
return [{
|
|
62
99
|
id: "provider-default",
|
|
63
100
|
label: providerDefaultLabel,
|
|
64
101
|
effort: void 0
|
|
65
|
-
})
|
|
66
|
-
for (const effort of reasoning.efforts) choices.push({
|
|
102
|
+
}, ...effortsOf(model).map((effort) => ({
|
|
67
103
|
id: `effort:${effort.id}`,
|
|
68
104
|
label: effort.name,
|
|
69
105
|
effort: effort.id
|
|
70
|
-
});
|
|
71
|
-
return choices;
|
|
106
|
+
}))];
|
|
72
107
|
}
|
|
73
108
|
/** Normalize one settings wire value into a complete route. */
|
|
74
109
|
function routeFromSettingsValue(value) {
|
|
@@ -87,7 +122,7 @@ window.__ModuleLoader__.load({
|
|
|
87
122
|
}
|
|
88
123
|
//#endregion
|
|
89
124
|
//#region \0dsh-css:/root/code/dsh-pi-parity/packages/orbit/client/OrbitModelSelect.module.css.mjs
|
|
90
|
-
const css = ".f77nca_panel{z-index:60;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-specific-menu);min-width:260px;max-width:420px;max-height:min(55vh,420px);box-shadow:var(--dsw-elevation-prominent);color:var(--dsw-alias-label-primary);border-radius:12px;flex-direction:column;padding:6px;font-size:13px;line-height:1.4;display:flex;position:fixed;overflow:auto}.f77nca_title{color:var(--dsw-alias-label-tertiary);letter-spacing:.04em;text-transform:uppercase;padding:6px 8px 2px;font-size:11px}.f77nca_group{color:var(--dsw-alias-label-tertiary);padding:8px 8px 2px;font-size:11px}.f77nca_row{width:100%;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:8px;align-items:center;gap:8px;padding:7px 8px;display:flex}.f77nca_row:hover{background:var(--dsw-alias-interactive-bg-hover)}.f77nca_rowLabel{flex:none}.f77nca_rowValue{max-width:190px;color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;margin-left:auto;overflow:hidden}.f77nca_rowValueActive{color:var(--dsw-alias-label-primary)}.f77nca_rowDetail{color:var(--dsw-alias-label-tertiary);margin-left:auto;font-size:11px}.f77nca_check{color:var(--dsw-alias-state-business-primary);flex:none;margin-left:6px}.f77nca_chevron{color:var(--dsw-alias-label-tertiary);flex:none;margin-left:6px}.f77nca_separator{background:var(--dsw-alias-border-l1);height:1px;margin:6px 4px}.f77nca_hint{color:var(--dsw-alias-label-tertiary);padding:6px 8px;font-size:11px}.f77nca_error{color:var(--dsw-alias-state-error-primary);align-items:center;gap:8px;padding:8px;font-size:12px;display:flex}.f77nca_back{width:100%;color:var(--dsw-alias-label-tertiary);font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;align-items:center;gap:6px;padding:6px 8px 8px;font-size:12px;display:flex}";
|
|
125
|
+
const css = ".f77nca_panel{z-index:60;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-specific-menu);min-width:260px;max-width:420px;max-height:min(55vh,420px);box-shadow:var(--dsw-elevation-prominent);color:var(--dsw-alias-label-primary);border-radius:12px;flex-direction:column;padding:6px;font-size:13px;line-height:1.4;display:flex;position:fixed;overflow:auto}.f77nca_title{color:var(--dsw-alias-label-tertiary);letter-spacing:.04em;text-transform:uppercase;padding:6px 8px 2px;font-size:11px}.f77nca_group{color:var(--dsw-alias-label-tertiary);padding:8px 8px 2px;font-size:11px}.f77nca_row{width:100%;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:8px;align-items:center;gap:8px;padding:7px 8px;display:flex}.f77nca_row:hover{background:var(--dsw-alias-interactive-bg-hover)}.f77nca_switchRow{align-items:center;gap:8px;padding:4px 8px 6px;display:flex}.f77nca_switchControl{margin-left:auto}.f77nca_rowLabel{flex:none}.f77nca_rowValue{max-width:190px;color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;margin-left:auto;overflow:hidden}.f77nca_rowValueActive{color:var(--dsw-alias-label-primary)}.f77nca_rowDetail{color:var(--dsw-alias-label-tertiary);margin-left:auto;font-size:11px}.f77nca_check{color:var(--dsw-alias-state-business-primary);flex:none;margin-left:6px}.f77nca_chevron{color:var(--dsw-alias-label-tertiary);flex:none;margin-left:6px}.f77nca_separator{background:var(--dsw-alias-border-l1);height:1px;margin:6px 4px}.f77nca_hint{color:var(--dsw-alias-label-tertiary);padding:6px 8px;font-size:11px}.f77nca_error{color:var(--dsw-alias-state-error-primary);align-items:center;gap:8px;padding:8px;font-size:12px;display:flex}.f77nca_back{width:100%;color:var(--dsw-alias-label-tertiary);font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;align-items:center;gap:6px;padding:6px 8px 8px;font-size:12px;display:flex}";
|
|
91
126
|
const tagId = "@kasenri/dsh-orbit/OrbitModelSelect.module.css";
|
|
92
127
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
93
128
|
const tag = document.createElement("style");
|
|
@@ -97,20 +132,22 @@ window.__ModuleLoader__.load({
|
|
|
97
132
|
document.head.appendChild(tag);
|
|
98
133
|
}
|
|
99
134
|
var OrbitModelSelect_module_css_default = {
|
|
100
|
-
"
|
|
135
|
+
"error": "f77nca_error",
|
|
136
|
+
"chevron": "f77nca_chevron",
|
|
137
|
+
"back": "f77nca_back",
|
|
101
138
|
"rowValueActive": "f77nca_rowValueActive",
|
|
102
|
-
"row": "f77nca_row",
|
|
103
139
|
"hint": "f77nca_hint",
|
|
104
|
-
"
|
|
140
|
+
"switchControl": "f77nca_switchControl",
|
|
105
141
|
"group": "f77nca_group",
|
|
106
|
-
"chevron": "f77nca_chevron",
|
|
107
|
-
"error": "f77nca_error",
|
|
108
142
|
"panel": "f77nca_panel",
|
|
109
|
-
"
|
|
110
|
-
"
|
|
143
|
+
"separator": "f77nca_separator",
|
|
144
|
+
"title": "f77nca_title",
|
|
145
|
+
"switchRow": "f77nca_switchRow",
|
|
146
|
+
"rowValue": "f77nca_rowValue",
|
|
111
147
|
"rowDetail": "f77nca_rowDetail",
|
|
112
148
|
"check": "f77nca_check",
|
|
113
|
-
"
|
|
149
|
+
"row": "f77nca_row",
|
|
150
|
+
"rowLabel": "f77nca_rowLabel"
|
|
114
151
|
};
|
|
115
152
|
//#endregion
|
|
116
153
|
//#region client/OrbitModelSelect.tsx
|
|
@@ -143,9 +180,23 @@ window.__ModuleLoader__.load({
|
|
|
143
180
|
* @param props - injected directory/settings faces plus the locale seat.
|
|
144
181
|
* @returns the trigger and, while open, the panel.
|
|
145
182
|
*/
|
|
146
|
-
function OrbitModelSelect({ t, available, directory, settings, loadModels, selectModel, writeRole, reloadSettings }) {
|
|
183
|
+
function OrbitModelSelect({ t, available, directory, settings, loadModels, selectModel, writeRole, setOrbitEnabled, reloadSettings, useProjection }) {
|
|
147
184
|
const dir = (0, react.useSyncExternalStore)((listener) => directory.subscribe(listener), () => directory.getSnapshot());
|
|
148
185
|
const config = (0, react.useSyncExternalStore)((listener) => settings.subscribe(listener), () => settings.getSnapshot());
|
|
186
|
+
const orbitSession = useProjection("orbitSession");
|
|
187
|
+
const [orbitOverride, setOrbitOverride] = (0, react.useState)(null);
|
|
188
|
+
const [orbitPending, setOrbitPending] = (0, react.useState)(false);
|
|
189
|
+
const orbitEnabled = orbitOverride ?? orbitSession?.enabled === true;
|
|
190
|
+
(0, react.useEffect)(() => {
|
|
191
|
+
if (orbitOverride !== null && orbitSession?.enabled === orbitOverride) setOrbitOverride(null);
|
|
192
|
+
}, [orbitOverride, orbitSession?.enabled]);
|
|
193
|
+
const toggleOrbit = async (next) => {
|
|
194
|
+
setOrbitOverride(next);
|
|
195
|
+
setOrbitPending(true);
|
|
196
|
+
const ok = await setOrbitEnabled(next);
|
|
197
|
+
setOrbitPending(false);
|
|
198
|
+
if (!ok) setOrbitOverride(null);
|
|
199
|
+
};
|
|
149
200
|
const [open, setOpen] = (0, react.useState)(false);
|
|
150
201
|
const [pane, setPane] = (0, react.useState)({ kind: "root" });
|
|
151
202
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
@@ -197,25 +248,31 @@ window.__ModuleLoader__.load({
|
|
|
197
248
|
if (ok) setOpen(false);
|
|
198
249
|
};
|
|
199
250
|
const currentRouteOf = (role) => role === "commander" ? config.commander : role === "watchdog" ? config.watchdog : executorRoute;
|
|
251
|
+
const roleLabelOf = (role) => role === "commander" ? commanderLabel : role === "watchdog" ? watchdogLabel : executorLabel;
|
|
252
|
+
const roleEffortLabelOf = (role) => role === "commander" ? commanderEffort : role === "watchdog" ? watchdogEffort : executorEffort;
|
|
253
|
+
const commitEffort = async (role, route, effort) => {
|
|
254
|
+
const next = {
|
|
255
|
+
provider: route.provider,
|
|
256
|
+
model: route.model,
|
|
257
|
+
...effort === void 0 ? {} : { reasoningEffort: effort }
|
|
258
|
+
};
|
|
259
|
+
if (role === "executor") {
|
|
260
|
+
await commitExecutor(next);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
await commitRole(role, next);
|
|
264
|
+
};
|
|
200
265
|
const modelRow = (role, provider, providerName, model) => {
|
|
201
266
|
const current = currentRouteOf(role);
|
|
202
267
|
const active = current?.provider === provider && current.model === model.id;
|
|
203
|
-
const reasoning = model.reasoning;
|
|
204
268
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
205
269
|
type: "button",
|
|
206
270
|
role: "menuitem",
|
|
207
271
|
className: OrbitModelSelect_module_css_default.row,
|
|
208
272
|
disabled: busy,
|
|
209
273
|
onClick: () => {
|
|
210
|
-
if (
|
|
211
|
-
|
|
212
|
-
setPane({
|
|
213
|
-
kind: "efforts",
|
|
214
|
-
role,
|
|
215
|
-
provider,
|
|
216
|
-
modelId: model.id,
|
|
217
|
-
effort
|
|
218
|
-
});
|
|
274
|
+
if (active) {
|
|
275
|
+
setOpen(false);
|
|
219
276
|
return;
|
|
220
277
|
}
|
|
221
278
|
if (role === "executor") {
|
|
@@ -236,38 +293,9 @@ window.__ModuleLoader__.load({
|
|
|
236
293
|
}) : null]
|
|
237
294
|
}, `${provider}/${model.id}`);
|
|
238
295
|
};
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
243
|
-
type: "button",
|
|
244
|
-
role: "menuitem",
|
|
245
|
-
className: OrbitModelSelect_module_css_default.row,
|
|
246
|
-
disabled: busy,
|
|
247
|
-
onClick: () => {
|
|
248
|
-
if (role === "executor") {
|
|
249
|
-
commitExecutor({
|
|
250
|
-
provider,
|
|
251
|
-
model: modelId,
|
|
252
|
-
...effort === void 0 ? {} : { reasoningEffort: effort }
|
|
253
|
-
});
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
commitRole(role, {
|
|
257
|
-
provider,
|
|
258
|
-
model: modelId,
|
|
259
|
-
...effort === void 0 ? {} : { reasoningEffort: effort }
|
|
260
|
-
});
|
|
261
|
-
},
|
|
262
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
263
|
-
className: OrbitModelSelect_module_css_default.rowLabel,
|
|
264
|
-
children: label
|
|
265
|
-
}), active ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
266
|
-
className: OrbitModelSelect_module_css_default.check,
|
|
267
|
-
children: "✓"
|
|
268
|
-
}) : null]
|
|
269
|
-
}, id);
|
|
270
|
-
};
|
|
296
|
+
const effortPaneRoute = pane.kind === "efforts" ? currentRouteOf(pane.role) : void 0;
|
|
297
|
+
const effortPaneModel = modelOf(dir, effortPaneRoute);
|
|
298
|
+
const effortPaneChoices = pane.kind === "efforts" ? effortChoicesOf(effortPaneModel, t("providerDefault")) : [];
|
|
271
299
|
const settingsError = config.error;
|
|
272
300
|
const modelError = dir.status === "error" ? dir.error ?? t("loadFailed") : void 0;
|
|
273
301
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -282,11 +310,11 @@ window.__ModuleLoader__.load({
|
|
|
282
310
|
setOpen((value) => !value);
|
|
283
311
|
},
|
|
284
312
|
children: [
|
|
285
|
-
commanderLabel,
|
|
286
|
-
commanderEffort
|
|
313
|
+
orbitEnabled ? commanderLabel : t("orbitOff"),
|
|
314
|
+
orbitEnabled && commanderEffort !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
287
315
|
style: { color: "var(--dsw-alias-label-tertiary)" },
|
|
288
316
|
children: ` ${commanderEffort}`
|
|
289
|
-
}),
|
|
317
|
+
}) : null,
|
|
290
318
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
291
319
|
className: OrbitModelSelect_module_css_default.chevron,
|
|
292
320
|
children: CARET
|
|
@@ -302,16 +330,19 @@ window.__ModuleLoader__.load({
|
|
|
302
330
|
},
|
|
303
331
|
role: "menu",
|
|
304
332
|
children: [
|
|
305
|
-
pane.kind
|
|
333
|
+
pane.kind === "root" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
334
|
+
className: OrbitModelSelect_module_css_default.title,
|
|
335
|
+
children: t("title")
|
|
336
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
306
337
|
type: "button",
|
|
307
338
|
className: OrbitModelSelect_module_css_default.back,
|
|
308
339
|
onClick: () => {
|
|
309
|
-
setPane({ kind: "root" }
|
|
340
|
+
setPane(pane.kind === "role" ? { kind: "root" } : {
|
|
341
|
+
kind: "role",
|
|
342
|
+
role: pane.role
|
|
343
|
+
});
|
|
310
344
|
},
|
|
311
|
-
children: `${BACK} ${t("back")}`
|
|
312
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
313
|
-
className: OrbitModelSelect_module_css_default.title,
|
|
314
|
-
children: t("title")
|
|
345
|
+
children: `${BACK} ${pane.kind === "role" ? t("back") : t(pane.role)}`
|
|
315
346
|
}),
|
|
316
347
|
settingsError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
317
348
|
className: OrbitModelSelect_module_css_default.error,
|
|
@@ -336,6 +367,22 @@ window.__ModuleLoader__.load({
|
|
|
336
367
|
})]
|
|
337
368
|
}),
|
|
338
369
|
pane.kind === "root" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
370
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
371
|
+
className: OrbitModelSelect_module_css_default.switchRow,
|
|
372
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
373
|
+
className: OrbitModelSelect_module_css_default.rowLabel,
|
|
374
|
+
children: t("orbit")
|
|
375
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
|
|
376
|
+
className: OrbitModelSelect_module_css_default.switchControl,
|
|
377
|
+
checked: orbitEnabled,
|
|
378
|
+
disabled: orbitPending,
|
|
379
|
+
label: t("orbitSwitchLabel"),
|
|
380
|
+
onChange: (next) => {
|
|
381
|
+
toggleOrbit(next);
|
|
382
|
+
}
|
|
383
|
+
})]
|
|
384
|
+
}),
|
|
385
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: OrbitModelSelect_module_css_default.separator }),
|
|
339
386
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
340
387
|
type: "button",
|
|
341
388
|
role: "menuitem",
|
|
@@ -343,7 +390,7 @@ window.__ModuleLoader__.load({
|
|
|
343
390
|
disabled: busy,
|
|
344
391
|
onClick: () => {
|
|
345
392
|
setPane({
|
|
346
|
-
kind: "
|
|
393
|
+
kind: "role",
|
|
347
394
|
role: "commander"
|
|
348
395
|
});
|
|
349
396
|
},
|
|
@@ -369,7 +416,7 @@ window.__ModuleLoader__.load({
|
|
|
369
416
|
disabled: busy,
|
|
370
417
|
onClick: () => {
|
|
371
418
|
setPane({
|
|
372
|
-
kind: "
|
|
419
|
+
kind: "role",
|
|
373
420
|
role: "executor"
|
|
374
421
|
});
|
|
375
422
|
},
|
|
@@ -399,7 +446,7 @@ window.__ModuleLoader__.load({
|
|
|
399
446
|
disabled: busy,
|
|
400
447
|
onClick: () => {
|
|
401
448
|
setPane({
|
|
402
|
-
kind: "
|
|
449
|
+
kind: "role",
|
|
403
450
|
role: "watchdog"
|
|
404
451
|
});
|
|
405
452
|
},
|
|
@@ -419,10 +466,72 @@ window.__ModuleLoader__.load({
|
|
|
419
466
|
]
|
|
420
467
|
})
|
|
421
468
|
] }) : null,
|
|
469
|
+
pane.kind === "role" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
470
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
471
|
+
className: OrbitModelSelect_module_css_default.group,
|
|
472
|
+
children: t(pane.role)
|
|
473
|
+
}),
|
|
474
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
475
|
+
type: "button",
|
|
476
|
+
role: "menuitem",
|
|
477
|
+
className: OrbitModelSelect_module_css_default.row,
|
|
478
|
+
disabled: busy,
|
|
479
|
+
onClick: () => {
|
|
480
|
+
setPane({
|
|
481
|
+
kind: "models",
|
|
482
|
+
role: pane.role
|
|
483
|
+
});
|
|
484
|
+
},
|
|
485
|
+
children: [
|
|
486
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
487
|
+
className: OrbitModelSelect_module_css_default.rowLabel,
|
|
488
|
+
children: t("models")
|
|
489
|
+
}),
|
|
490
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
491
|
+
className: OrbitModelSelect_module_css_default.rowValue,
|
|
492
|
+
children: roleLabelOf(pane.role) ?? t("triggerFallback")
|
|
493
|
+
}),
|
|
494
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
495
|
+
className: OrbitModelSelect_module_css_default.chevron,
|
|
496
|
+
children: CHEVRON
|
|
497
|
+
})
|
|
498
|
+
]
|
|
499
|
+
}),
|
|
500
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
501
|
+
type: "button",
|
|
502
|
+
role: "menuitem",
|
|
503
|
+
className: OrbitModelSelect_module_css_default.row,
|
|
504
|
+
disabled: busy,
|
|
505
|
+
onClick: () => {
|
|
506
|
+
setPane({
|
|
507
|
+
kind: "efforts",
|
|
508
|
+
role: pane.role
|
|
509
|
+
});
|
|
510
|
+
},
|
|
511
|
+
children: [
|
|
512
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
513
|
+
className: OrbitModelSelect_module_css_default.rowLabel,
|
|
514
|
+
children: t("effort")
|
|
515
|
+
}),
|
|
516
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
517
|
+
className: OrbitModelSelect_module_css_default.rowValue,
|
|
518
|
+
children: roleEffortLabelOf(pane.role) ?? t("providerDefault")
|
|
519
|
+
}),
|
|
520
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
521
|
+
className: OrbitModelSelect_module_css_default.chevron,
|
|
522
|
+
children: CHEVRON
|
|
523
|
+
})
|
|
524
|
+
]
|
|
525
|
+
}),
|
|
526
|
+
pane.role === "executor" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
527
|
+
className: OrbitModelSelect_module_css_default.hint,
|
|
528
|
+
children: t("followsSession")
|
|
529
|
+
}) : null
|
|
530
|
+
] }) : null,
|
|
422
531
|
pane.kind === "models" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
423
532
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
424
533
|
className: OrbitModelSelect_module_css_default.group,
|
|
425
|
-
children:
|
|
534
|
+
children: t("models")
|
|
426
535
|
}),
|
|
427
536
|
dir.groups.map((group) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
428
537
|
className: OrbitModelSelect_module_css_default.group,
|
|
@@ -433,15 +542,25 @@ window.__ModuleLoader__.load({
|
|
|
433
542
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: `${failure.name}: ${failure.message}` })
|
|
434
543
|
}, failure.id))
|
|
435
544
|
] }) : null,
|
|
436
|
-
pane.kind === "efforts" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
545
|
+
pane.kind === "efforts" && effortPaneRoute !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
437
546
|
className: OrbitModelSelect_module_css_default.group,
|
|
438
|
-
children:
|
|
439
|
-
}),
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
547
|
+
children: t("effort")
|
|
548
|
+
}), effortPaneChoices.map((choice) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
549
|
+
type: "button",
|
|
550
|
+
role: "menuitem",
|
|
551
|
+
className: OrbitModelSelect_module_css_default.row,
|
|
552
|
+
disabled: busy,
|
|
553
|
+
onClick: () => {
|
|
554
|
+
commitEffort(pane.role, effortPaneRoute, choice.effort);
|
|
555
|
+
},
|
|
556
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
557
|
+
className: OrbitModelSelect_module_css_default.rowLabel,
|
|
558
|
+
children: choice.label
|
|
559
|
+
}), effortPaneRoute.reasoningEffort === choice.effort ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
560
|
+
className: OrbitModelSelect_module_css_default.check,
|
|
561
|
+
children: "✓"
|
|
562
|
+
}) : null]
|
|
563
|
+
}, choice.id))] }) : null,
|
|
445
564
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: OrbitModelSelect_module_css_default.separator }),
|
|
446
565
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
447
566
|
className: OrbitModelSelect_module_css_default.hint,
|
|
@@ -458,11 +577,14 @@ window.__ModuleLoader__.load({
|
|
|
458
577
|
triggerFallback: "Orbit",
|
|
459
578
|
tooltip: "Orbit model configuration",
|
|
460
579
|
title: "Orbit",
|
|
580
|
+
orbit: "Orbit",
|
|
581
|
+
orbitOff: "Orbit Off",
|
|
582
|
+
orbitSwitchLabel: "Orbit for this chat",
|
|
461
583
|
commander: "Commander",
|
|
462
584
|
executor: "Executor",
|
|
463
585
|
watchdog: "Watchdog",
|
|
464
586
|
followsSession: "Follows current session model",
|
|
465
|
-
models: "
|
|
587
|
+
models: "Model",
|
|
466
588
|
effort: "Reasoning effort",
|
|
467
589
|
providerDefault: "Provider default",
|
|
468
590
|
back: "Back",
|
|
@@ -477,12 +599,15 @@ window.__ModuleLoader__.load({
|
|
|
477
599
|
triggerFallback: "Orbit",
|
|
478
600
|
tooltip: "Orbit 模型配置",
|
|
479
601
|
title: "Orbit",
|
|
602
|
+
orbit: "Orbit",
|
|
603
|
+
orbitOff: "Orbit Off",
|
|
604
|
+
orbitSwitchLabel: "本会话 Orbit 开关",
|
|
480
605
|
commander: "指挥官",
|
|
481
606
|
executor: "执行员",
|
|
482
607
|
watchdog: "监控模型",
|
|
483
608
|
followsSession: "跟随当前会话模型",
|
|
484
609
|
models: "模型",
|
|
485
|
-
effort: "
|
|
610
|
+
effort: "推理等级",
|
|
486
611
|
providerDefault: "提供方默认",
|
|
487
612
|
back: "返回",
|
|
488
613
|
retry: "重试",
|
|
@@ -604,6 +729,16 @@ window.__ModuleLoader__.load({
|
|
|
604
729
|
},
|
|
605
730
|
selectModel: (selection) => available ? directory.select(selection).then(() => true, () => false) : Promise.resolve(false),
|
|
606
731
|
writeRole,
|
|
732
|
+
setOrbitEnabled: async (enabled) => {
|
|
733
|
+
const session = sessions.binding(sessionId)?.session;
|
|
734
|
+
if (session === void 0) return false;
|
|
735
|
+
try {
|
|
736
|
+
const result = await session.command(`/orbit-toggle ${enabled ? "on" : "off"}`);
|
|
737
|
+
return result.ok && result.value.matched;
|
|
738
|
+
} catch {
|
|
739
|
+
return false;
|
|
740
|
+
}
|
|
741
|
+
},
|
|
607
742
|
reloadSettings
|
|
608
743
|
};
|
|
609
744
|
};
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import z from '@deepseek-ai/schemastery';
|
|
2
|
-
import { installOrbitGestureBoundary, registerOrbitCommand } from "./activation.js";
|
|
2
|
+
import { installOrbitGestureBoundary, registerOrbitCommand, registerOrbitToggleCommand } from "./activation.js";
|
|
3
3
|
import { createOrbitPreExecuteHandler } from "./pipeline-guard.js";
|
|
4
4
|
import { resolveEffectiveRoutes, sessionSelectionOf } from "./routes.js";
|
|
5
|
+
import { installOrbitSessionProjection, orbitEnabledOf } from "./session-state.js";
|
|
5
6
|
import { OrbitService } from "./service.js";
|
|
6
7
|
import { createOrbitTool } from "./tool.js";
|
|
7
8
|
export const name = 'dsh-orbit';
|
|
@@ -112,9 +113,18 @@ export function apply(ctx, config) {
|
|
|
112
113
|
if (config.slashCommand) {
|
|
113
114
|
ctx.inject(['commands'], (commandCtx) => {
|
|
114
115
|
registerOrbitCommand(commandCtx);
|
|
116
|
+
registerOrbitToggleCommand(commandCtx);
|
|
117
|
+
});
|
|
118
|
+
installOrbitGestureBoundary(ctx, {
|
|
119
|
+
sessionEnabled: (session) => orbitEnabledOf(ctx, session),
|
|
115
120
|
});
|
|
116
|
-
installOrbitGestureBoundary(ctx);
|
|
117
121
|
}
|
|
122
|
+
// Per-Session Orbit enable state: natively durable through the Session log
|
|
123
|
+
// and the projection registry. Minimal compositions without the registry
|
|
124
|
+
// keep the feature inert (OFF) while `/agent-orbit` keeps working.
|
|
125
|
+
ctx.inject(['sessionProjections'], (projectionCtx) => {
|
|
126
|
+
installOrbitSessionProjection(projectionCtx);
|
|
127
|
+
});
|
|
118
128
|
if (config.registerGuards) {
|
|
119
129
|
const handler = createOrbitPreExecuteHandler(service, {
|
|
120
130
|
competingDriver: (agent) => {
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-Session Orbit enable state, native to DeepSeek Harness.
|
|
3
|
+
*
|
|
4
|
+
* The write is the ordinary `/orbit-toggle on|off` command: the commands
|
|
5
|
+
* runtime appends its own `command/run` record to the Session log (a known
|
|
6
|
+
* event type, durable with the Session). The state is a session projection
|
|
7
|
+
* unit folding those records, so it is per-Session by construction, survives
|
|
8
|
+
* reloads with the log, and reaches the browser through the normal projection
|
|
9
|
+
* wire — no separate store, no Orbit settings entry, no `.cx/state.json`.
|
|
10
|
+
*
|
|
11
|
+
* This is UI/runtime preference state for one chat, never the durable
|
|
12
|
+
* execution state of an Orbit run.
|
|
13
|
+
*/
|
|
14
|
+
/** The command whose `command/run` records carry the toggle. */
|
|
15
|
+
export const ORBIT_TOGGLE_COMMAND = 'orbit-toggle';
|
|
16
|
+
/** The projection key carrying the per-Session enable state. */
|
|
17
|
+
export const ORBIT_SESSION_KEY = 'orbitSession';
|
|
18
|
+
/**
|
|
19
|
+
* Parse one `/orbit-toggle` argument; `undefined` when it names neither state,
|
|
20
|
+
* so a malformed toggle never flips the switch as a side effect.
|
|
21
|
+
*/
|
|
22
|
+
export function parseOrbitToggle(rawInput) {
|
|
23
|
+
const argument = rawInput.trim().toLowerCase();
|
|
24
|
+
if (argument === 'on')
|
|
25
|
+
return true;
|
|
26
|
+
if (argument === 'off')
|
|
27
|
+
return false;
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Fold one Session event into the enable state. A new Session starts OFF;
|
|
32
|
+
* every unrelated event keeps the same state reference so the projection
|
|
33
|
+
* drive does no downstream work.
|
|
34
|
+
*/
|
|
35
|
+
export function foldOrbitSession(state, event) {
|
|
36
|
+
if (event.type !== 'command/run')
|
|
37
|
+
return state;
|
|
38
|
+
const data = event.data;
|
|
39
|
+
if (data?.name !== ORBIT_TOGGLE_COMMAND)
|
|
40
|
+
return state;
|
|
41
|
+
const enabled = parseOrbitToggle(typeof data.args === 'string' ? data.args : '');
|
|
42
|
+
if (enabled === undefined || enabled === state.enabled)
|
|
43
|
+
return state;
|
|
44
|
+
return { enabled };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Minimal `ZodType`-shaped validator for the projection schemas. The registry
|
|
48
|
+
* only ever calls `parse`, and Orbit must not add a `zod` runtime dependency
|
|
49
|
+
* that a strict package manager would not hoist for the plugin.
|
|
50
|
+
*/
|
|
51
|
+
function orbitSessionSchema() {
|
|
52
|
+
return {
|
|
53
|
+
parse(value) {
|
|
54
|
+
const enabled = value?.enabled;
|
|
55
|
+
if (typeof enabled !== 'boolean')
|
|
56
|
+
throw new Error('orbitSession projection value must be { enabled: boolean }');
|
|
57
|
+
return { enabled };
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/** Register the per-Session enable projection on the session projection registry. */
|
|
62
|
+
export function installOrbitSessionProjection(ctx) {
|
|
63
|
+
ctx.sessionProjections.register({
|
|
64
|
+
key: ORBIT_SESSION_KEY,
|
|
65
|
+
stateSchema: orbitSessionSchema(),
|
|
66
|
+
init: () => ({ enabled: false }),
|
|
67
|
+
apply: (state, event) => foldOrbitSession(state, event),
|
|
68
|
+
wire: {
|
|
69
|
+
viewSchema: orbitSessionSchema(),
|
|
70
|
+
view: (state) => ({ enabled: state.enabled }),
|
|
71
|
+
},
|
|
72
|
+
stateVersion: 1,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/** Whether the Session currently defaults ordinary messages into Orbit. */
|
|
76
|
+
export function orbitEnabledOf(ctx, session) {
|
|
77
|
+
const reflect = ctx.reflect;
|
|
78
|
+
const registry = reflect?.get('sessionProjections');
|
|
79
|
+
return registry?.stateOf(session, ORBIT_SESSION_KEY)?.enabled === true;
|
|
80
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kasenri/dsh-orbit",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Deterministic engineering orchestration for DeepSeek Harness with Commander, Executor, Smart Watchdog, bounded recovery and durable execution state.",
|
|
6
6
|
"license": "MIT",
|