@steerable/agent-shell-web 0.6.68 → 0.6.70
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@steerable/agent-shell-web",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.70",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Steerable framework — product-neutral renderer SPA source for @steerable/agent-shell (React + Vite). Consumed by product web entry points via the `@/` alias (anchored at this package's src/) and the createProductViteConfig factory. Published as a source package (src/), plus the compiled vite.base config factory (vite.base.js + .d.ts) for Node-side config loading.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -45,8 +45,8 @@
|
|
|
45
45
|
"react-router-dom": "^6.30.0",
|
|
46
46
|
"rehype-raw": "^7.0.0",
|
|
47
47
|
"remark-gfm": "^4.0.1",
|
|
48
|
-
"@steerable/agent-protocol": "0.6.
|
|
49
|
-
"@steerable/agent-ui": "0.6.
|
|
48
|
+
"@steerable/agent-protocol": "0.6.70",
|
|
49
|
+
"@steerable/agent-ui": "0.6.70"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@tailwindcss/vite": "^4.3.0",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"typescript": "^5.8.3",
|
|
60
60
|
"vite": "^5.4.21",
|
|
61
61
|
"vitest": "^3.2.4",
|
|
62
|
-
"@steerable/pack-sdk": "0.6.
|
|
62
|
+
"@steerable/pack-sdk": "0.6.70"
|
|
63
63
|
},
|
|
64
64
|
"scripts": {
|
|
65
65
|
"gen": "echo agent-shell-web",
|
|
@@ -6,7 +6,8 @@ import { t } from '@/i18n';
|
|
|
6
6
|
/**
|
|
7
7
|
* ExecPolicyPicker — 输入框上的命令沙箱切换(类 Codex 底部权限档)。
|
|
8
8
|
*
|
|
9
|
-
*
|
|
9
|
+
* 切档立刻记住。排队的下一轮按当时的档开流;插进当前回合的消息会改
|
|
10
|
+
* 这一回合后面的命令沙箱。已经在跑的那条命令保持它开始时的档。
|
|
10
11
|
* 工作区 = Seatbelt/bwrap 只允许写项目家目录、源文件夹(含各自子目录)或本对话工作区;完整权限
|
|
11
12
|
* = 不下发命令沙箱,本机路径(如 Downloads)不再被 Operation not permitted 拦住。
|
|
12
13
|
*/
|
|
@@ -254,10 +254,14 @@ export function createHostChatTransport(chatId: string): HostChatTransport {
|
|
|
254
254
|
},
|
|
255
255
|
// 轮中转向:仅在 CoreLoop 路径(STEERABLE_USE_CORELOOP=1)且回合仍
|
|
256
256
|
// 运行时生效;否则主进程软失败,useChatStream 保留草稿。
|
|
257
|
-
steer: async (content: string) => {
|
|
257
|
+
steer: async (content: string, options?: { execPolicy?: 'workspace' | 'full' }) => {
|
|
258
258
|
const bridge = getHostBridge();
|
|
259
259
|
if (!bridge?.localBackend.steerChat) return false;
|
|
260
|
-
return await bridge.localBackend.steerChat(
|
|
260
|
+
return await bridge.localBackend.steerChat(
|
|
261
|
+
chatId,
|
|
262
|
+
content,
|
|
263
|
+
options?.execPolicy ? { execPolicy: options.execPolicy } : undefined,
|
|
264
|
+
);
|
|
261
265
|
},
|
|
262
266
|
stream: async (input: ChatStreamSendInput, onEvent) => {
|
|
263
267
|
const bridge = getHostBridge();
|
package/src/lib/host-bridge.ts
CHANGED
|
@@ -159,8 +159,15 @@ export interface HostBridge {
|
|
|
159
159
|
onEvent: (payload: LocalBackendStreamEvent) => void,
|
|
160
160
|
) => Promise<string | null>;
|
|
161
161
|
cancelStream: (streamId: string) => void;
|
|
162
|
-
/**
|
|
163
|
-
|
|
162
|
+
/**
|
|
163
|
+
* 轮中转向:注入一条用户消息到运行中的 CoreLoop 回合;false = 无可转向回合。
|
|
164
|
+
* `execPolicy` 改这一回合后面的命令沙箱(工作区 / 完整权限)。
|
|
165
|
+
*/
|
|
166
|
+
steerChat?: (
|
|
167
|
+
chatId: string,
|
|
168
|
+
content: string,
|
|
169
|
+
options?: { execPolicy?: 'workspace' | 'full' },
|
|
170
|
+
) => Promise<boolean>;
|
|
164
171
|
};
|
|
165
172
|
/** 会话附件:把源文件路径 / base64 字节拷贝进会话空间(`attachments:save`)。 */
|
|
166
173
|
attachments?: {
|
package/src/lib/http-bridge.ts
CHANGED
|
@@ -257,8 +257,12 @@ export function createHttpBridge(): HostBridge {
|
|
|
257
257
|
http<T>(input.method, input.path, input.body),
|
|
258
258
|
startStream,
|
|
259
259
|
cancelStream,
|
|
260
|
-
steerChat: async (chatId, content) => {
|
|
261
|
-
const result = await http<{ ok?: boolean }>('POST', '/host/steer', {
|
|
260
|
+
steerChat: async (chatId, content, options) => {
|
|
261
|
+
const result = await http<{ ok?: boolean }>('POST', '/host/steer', {
|
|
262
|
+
chatId,
|
|
263
|
+
content,
|
|
264
|
+
...(options?.execPolicy ? { execPolicy: options.execPolicy } : {}),
|
|
265
|
+
});
|
|
262
266
|
return result.ok === true;
|
|
263
267
|
},
|
|
264
268
|
},
|
package/src/pages/AgentPage.tsx
CHANGED
|
@@ -101,6 +101,31 @@ function readStoredMode(): ChatMode {
|
|
|
101
101
|
return clampWebChatMode(localStorage.getItem(CHAT_MODE_STORAGE_KEY));
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
interface ComposerTurnSettings {
|
|
105
|
+
execPolicy: ExecPolicy;
|
|
106
|
+
mode: ChatMode;
|
|
107
|
+
modelOverride: string | null;
|
|
108
|
+
effortOverride: string | null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* 排队和插队失败后的新回合不经过输入框的正常提交,这里在真正开流时
|
|
113
|
+
* 补上当前输入框的命令沙箱、模式、模型和编排开关。
|
|
114
|
+
*/
|
|
115
|
+
function applyComposerTurnSettings<T extends { metadata?: Record<string, unknown> }>(
|
|
116
|
+
input: T,
|
|
117
|
+
settings: ComposerTurnSettings,
|
|
118
|
+
): T {
|
|
119
|
+
const metadata: Record<string, unknown> = { ...(input.metadata ?? {}) };
|
|
120
|
+
metadata.execPolicy = settings.execPolicy;
|
|
121
|
+
if (settings.mode === "plan") metadata.mode = "plan";
|
|
122
|
+
else delete metadata.mode;
|
|
123
|
+
if (settings.modelOverride) metadata.model = settings.modelOverride;
|
|
124
|
+
if (settings.effortOverride) metadata.reasoningEffort = settings.effortOverride;
|
|
125
|
+
metadata.orchestration = isOrchestrationSettingEnabled();
|
|
126
|
+
return { ...input, metadata };
|
|
127
|
+
}
|
|
128
|
+
|
|
104
129
|
function extractPersistedActions(
|
|
105
130
|
messages: ChatMessageWithMetadata[] | undefined,
|
|
106
131
|
): Record<string, ExecutedAction[]> {
|
|
@@ -388,10 +413,29 @@ function AgentChatView({
|
|
|
388
413
|
const chatProject = chat?.projectId
|
|
389
414
|
? (projects.find((p) => p.id === chat.projectId) ?? null)
|
|
390
415
|
: null;
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
416
|
+
// 插队 / 排队在开流或注入时读这里,而不是排队那一刻的闭包。
|
|
417
|
+
const composerSettingsRef = useRef<ComposerTurnSettings>({
|
|
418
|
+
execPolicy: readStoredExecPolicy(),
|
|
419
|
+
mode: readStoredMode(),
|
|
420
|
+
modelOverride: null,
|
|
421
|
+
effortOverride: null,
|
|
422
|
+
});
|
|
423
|
+
const transport = useMemo(() => {
|
|
424
|
+
const inner = createHostChatTransport(chatId);
|
|
425
|
+
return {
|
|
426
|
+
cancelActive: () => inner.cancelActive(),
|
|
427
|
+
stream: (
|
|
428
|
+
input: { content: string; metadata?: Record<string, unknown> },
|
|
429
|
+
onEvent: (event: SSEEvent) => void,
|
|
430
|
+
) => inner.stream(applyComposerTurnSettings(input, composerSettingsRef.current), onEvent),
|
|
431
|
+
steer: (content: string) => {
|
|
432
|
+
if (!inner.steer) return Promise.resolve(false);
|
|
433
|
+
return inner.steer(content, {
|
|
434
|
+
execPolicy: composerSettingsRef.current.execPolicy,
|
|
435
|
+
});
|
|
436
|
+
},
|
|
437
|
+
};
|
|
438
|
+
}, [chatId]);
|
|
395
439
|
|
|
396
440
|
// 4.6a 后台任务:header 角标与消息列尾部的终态卡共用这一份订阅,两处
|
|
397
441
|
// 因此不会各拉一次任务表、也不会显示互相错位的状态。
|
|
@@ -874,6 +918,12 @@ function AgentChatView({
|
|
|
874
918
|
// ModelPicker 自取(GET /api/v2/llm/models → sidecar models.list)。
|
|
875
919
|
const [modelOverride, setModelOverride] = useState<string | null>(null);
|
|
876
920
|
const [effortOverride, setEffortOverride] = useState<string | null>(null);
|
|
921
|
+
composerSettingsRef.current = {
|
|
922
|
+
execPolicy,
|
|
923
|
+
mode,
|
|
924
|
+
modelOverride,
|
|
925
|
+
effortOverride,
|
|
926
|
+
};
|
|
877
927
|
|
|
878
928
|
const handleSubmit = useCallback(
|
|
879
929
|
(input: { content: string; metadata?: Record<string, unknown> }) => {
|