agent-Specialization/static/src/app/state.ts
JOJO 6ee8cda2a7 feat(security): harden host execution model with sandbox/direct controls
This commit introduces a substantial security and runtime architecture update for host mode, with three major goals: (1) enforce OS-level sandboxing by default, (2) support a controlled temporary direct-execution escape hatch for admin users, and (3) eliminate silent fallback behavior that could hide risk.

Backend/runtime changes:\n- Added a unified host sandbox runner (macOS sandbox-exec / Linux bubblewrap+seccomp / Windows WSL2).\n- Converted host terminal creation to sandboxed interactive shells by default.\n- Kept run_command/run_python on the same execution policy and added host execution mode plumbing (sandbox|direct).\n- Added session-scoped direct mode with TTL auto-revert (default 10 minutes), configurable via environment.\n- Added execution mode APIs (GET/POST /api/execution-mode), host+admin gating, status propagation, and rate limiting.\n- Added runtime refresh hooks so tool calls honor TTL expiration and mode transitions consistently.\n- Removed docker->host silent fallback paths: docker startup/runtime failures now fail fast instead of degrading silently.\n- Added host sandbox prompt injection (host-only) to guide agent behavior under permission constraints.

Configuration and policy changes:\n- Added HOST_EXECUTION_MODE_DEFAULT and HOST_EXECUTION_DIRECT_TTL_SECONDS.\n- Added HOST_SANDBOX_MACOS_WRITABLE_PATHS to support user-defined writable path allowlists.\n- Updated macOS sandbox profile generation to use minimal defaults (workspace + tmp + /dev/null) plus explicit allowlist extensions.\n- Updated .env.example documentation for new execution/sandbox controls.

Frontend changes:\n- Extended permission popover to a two-column model (Permission + Execution Environment) for host admin sessions.\n- Added execution mode state/options to app state, fetching, and status synchronization flows.\n- Added execution mode switching action and user feedback toasts.\n- Kept warning emphasis via text styling only (removed warning border per UX request).

Validation:\n- Python syntax checks passed for modified backend modules.\n- Existing smoke tests passed: python3 -m unittest test.test_server_refactor_smoke\n- Frontend production build passed: npm run build
2026-05-10 19:19:01 +08:00

223 lines
7.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// @ts-nocheck
import { ICONS, TOOL_CATEGORY_ICON_MAP } from '../utils/icons';
export function dataState() {
return {
// 路由相关
initialRouteResolved: false,
dropToolEvents: false,
// 轮询模式标志(禁用 WebSocket 事件处理)
usePollingMode: true,
// 后台子智能体等待状态
waitingForSubAgent: false,
// 后台 run_command 等待状态(用于文案区分)
waitingForBackgroundCommand: false,
// 是否在对话区展示 system 消息
hideSystemMessages: true,
// 工具状态跟踪
preparingTools: new Map(),
activeTools: new Map(),
toolActionIndex: new Map(),
toolStacks: new Map(),
// 当前任务是否仍在进行中(用于保持输入区的"停止"状态)
taskInProgress: false,
// 等待后台通知期间,用于发现并接管新建任务的轮询定时器
waitingTaskProbeTimer: null,
// 运行期消息堆积(提前发送 / 引导对话)
runtimeQueuedMessages: [],
runtimeGuidanceFallbackQueue: [],
runtimeQueueSuppressedMessageIds: new Set(),
runtimeGuidanceSuppressedTextCounts: {},
runtimeQueueLimit: 5,
runtimeQueueAutoSendInProgress: false,
runtimeQueueSyncLockKey: '',
runtimeQueueSyncLockUntil: 0,
// 输入区动态保留高度(用于同步扩大消息区可滚动范围)
composerReservedHeight: 80,
// 记录上一次成功加载历史的对话ID防止初始化阶段重复加载导致动画播放两次
lastHistoryLoadedConversationId: null,
// ==========================================
// 对话管理相关状态
// ==========================================
// 搜索功能
// ==========================================
searchRequestSeq: 0,
searchActiveQuery: '',
searchResultIdSet: new Set(),
searchPreviewCache: {},
// Token统计相关状态修复版
// ==========================================
// 对话压缩状态
compressing: false,
compressionInProgress: false,
compressionMode: '',
compressionStage: '',
compressionError: '',
compressionToastId: null,
skipConversationLoadedEvent: false,
skipConversationHistoryReload: false,
_scrollListenerReady: false,
historyLoading: false,
historyLoadingFor: null,
historyLoadSeq: 0,
blankHeroActive: false,
blankHeroExiting: false,
blankWelcomeText: '',
lastBlankConversationId: null,
// 对话标题打字效果
titleTypingText: '',
titleTypingTarget: '',
titleTypingTimer: null,
titleReady: false,
suppressTitleTyping: false,
headerMenuOpen: false,
blankWelcomePool: [
'有什么可以帮忙的?',
'想了解些热点吗?',
'要我帮你完成作业吗?',
'整点代码?',
'随便聊点什么?',
'想让我帮你整理一下思路吗?',
'要不要我帮你写个小工具?',
'发我一句话,我来接着做。',
'我来给你画幅画?',
'想看小猫吗?',
'有什么难题丢过来吧。',
'想不想看个有意思的?',
'来,说说你的需求~'
],
mobileViewportQuery: null,
modeMenuOpen: false,
modelMenuOpen: false,
permissionMenuOpen: false,
currentPermissionMode: 'unrestricted',
executionModeEnabled: false,
currentExecutionMode: 'sandbox',
executionModeDirectUntil: null,
executionModeTtlSeconds: 600,
versioningHostMode: false,
hostWorkspaces: [],
currentHostWorkspaceId: '',
defaultHostWorkspaceId: '',
hostWorkspaceSwitching: false,
hostWorkspaceCreateDialogOpen: false,
hostWorkspaceCreatePath: '',
hostWorkspaceCreateLabel: '',
hostWorkspaceCreateSubmitting: false,
hostWorkspaceCreateError: '',
versioningEnabled: false,
newConversationVersioningEnabled: false,
versioningTrackingMode: 'workspace_and_conversation',
newConversationVersioningTrackingMode: 'workspace_and_conversation',
versioningMode: 'overwrite',
versioningMismatch: false,
versioningWorkspaceMatched: true,
versioningDialogOpen: false,
versioningLoading: false,
versioningCheckpoints: [],
versioningSelectedSeq: null,
versioningSelectedDetail: null,
versioningDetailLoading: false,
versioningRestoring: false,
versioningRestoreMode: 'overwrite',
permissionModeOptions: [
{
value: 'readonly',
label: '只读',
description: '仅允许读取/搜索类工具,禁止修改工作区'
},
{
value: 'approval',
label: '批准',
description: '对工作区文件进行修改的工具需用户批准后才会执行'
},
{
value: 'unrestricted',
label: '无限制',
description: '保持当前默认行为,不额外拦截'
}
],
executionModeOptions: [
{
value: 'sandbox',
label: '沙箱',
description: '所有指令会在系统沙箱中执行'
},
{
value: 'direct',
label: '完全访问权限',
description: '所有指令会在宿主机直接执行'
}
],
pendingToolApprovals: [],
decidingApprovalIds: [],
imageEntries: [],
imageLoading: false,
videoEntries: [],
videoLoading: false,
conversationHasImages: false,
conversationHasVideos: false,
conversationListRequestSeq: 0,
conversationListRefreshToken: 0,
connectionHeartbeatTimer: null,
connectionHeartbeatActive: false,
connectionHeartbeatFailCount: 0,
connectionHeartbeatSeq: 0,
connectionHeartbeatLastLatencyMs: 0,
connectionHeartbeatLastError: '',
connectionHeartbeatLastStatusCode: null,
connectionHeartbeatLastChangeAt: 0,
connectionHeartbeatInFlight: false,
connectionHeartbeatFailThreshold: 3,
connectionHeartbeatRequestTimeoutMs: 5000,
connectionHeartbeatIntervalMs: 8000,
connectionHeartbeatDisconnectedIntervalMs: 1000,
composerDraftSaveTimer: null,
composerDraftDirty: false,
composerDraftLastSyncedContent: '',
composerDraftFetchSeq: 0,
// 工具控制菜单
icons: ICONS,
toolCategoryIcons: TOOL_CATEGORY_ICON_MAP,
// 对话回顾
reviewDialogOpen: false,
reviewSelectedConversationId: null,
reviewSubmitting: false,
reviewPreviewLines: [],
reviewPreviewLoading: false,
reviewPreviewError: null,
reviewPreviewLimit: 20,
reviewSendToModel: true,
reviewGeneratedPath: null,
// 新手教程首次引导弹窗
tutorialPromptVisible: false,
tutorialPromptLoading: false,
tutorialPromptUsername: '',
// 拖拽上传状态
dragOverActive: false,
// 拖拽事件绑定函数(用于正确移除监听)
_boundDragEnter: null,
_boundDragOver: null,
_boundDragLeave: null,
_boundDrop: null,
_manualScrollSuppressUntil: 0,
_escapedByUserScroll: false,
_autoRelockCooldownUntil: 0,
// stick-to-bottom 状态(用于“回到底部”按钮显隐)
stickIsAtBottom: true,
stickIsNearBottom: true
};
}