chore: land tree view-state, vault, Pi module split, and repo hygiene

Persist PageTree expand state via control-plane view-state and align
chevron/DOM with restored expansion; keep Sidex-style shallow page-tree
scan and drop the unused recursive scanner that only added cargo noise.

Add password vault workbench routes/runtime/skill/CLI, split page_ai_pi
into a module package, and retire Hermes/ACP/OpenHub recycle + root
harness evidence from the index while gitignoring recycle and local
diag dumps.

Archive superseded design/bugs docs under old/, point architecture at
ARCHITECTURE.md, and refresh smokes for Pi S1–S7, vault, and editor
regressions so the working tree can stay clean.
This commit is contained in:
Agent Board
2026-07-21 05:13:05 +08:00
parent 6f9c7d3b58
commit b798f628ee
264 changed files with 17480 additions and 17314 deletions
@@ -300,7 +300,14 @@
return vm;
}
if (event.type === 'assistant_abort') {
// S4: abort is idempotent — second apply (SSE after optimistic) must not mutate again.
if (vm.runtimeStatus === STATE_ABORTED && vm.abortResponse) {
return vm;
}
var abortMsg = vm.currentAssistant || findLastAssistant(vm) || ensurePiRunAssistant(vm);
if (abortMsg.status === 'aborted' && !vm.currentAssistant) {
return vm;
}
abortMsg.status = 'aborted';
abortMsg.abortReason = event.reason || payload.reason || payload.stopReason || payload.error || payload.message || '已中止';
abortMsg.abortResponse = payload || { reason: abortMsg.abortReason };
@@ -3572,15 +3579,31 @@
])
.then(function (results) {
var data = results[0] || {};
var effectiveData = results[1] || null;
piLabState.enabled = !!data.enabled;
piLabState.sessionId = data.sessionId || piLabState.sessionId || null;
piLabState.providerSessionId = data.providerSessionId || piLabState.providerSessionId || null;
// Consume backend status/default fields when present.
if (data.defaultModelProvider) piLabState.defaultModelProvider = data.defaultModelProvider;
if (data.defaultModelId) piLabState.defaultModelId = data.defaultModelId;
if (data.defaultThinkingLevel) piLabState.defaultThinkingLevel = normalizeThinkingLevel(data.defaultThinkingLevel);
piLabState.modelProvider = (data.session && data.session.modelProvider) || piLabState.modelProvider || null;
piLabState.modelId = (data.session && data.session.modelId) || piLabState.modelId || null;
// S1: prefer effective AI settings as management-surface truth; status is
// fallback only when effective load failed. Do not let env-stale status
// overwrite a successfully loaded effective defaultModel.
if (effectiveData) {
applyEffectiveConfig(effectiveData);
} else {
if (data.defaultModelProvider) piLabState.defaultModelProvider = data.defaultModelProvider;
if (data.defaultModelId) piLabState.defaultModelId = data.defaultModelId;
}
if (data.defaultThinkingLevel && !piLabState.defaultThinkingLevel) {
piLabState.defaultThinkingLevel = normalizeThinkingLevel(data.defaultThinkingLevel);
}
// Active session model wins; otherwise keep effective defaults already applied.
piLabState.modelProvider = (data.session && data.session.modelProvider)
|| piLabState.modelProvider
|| piLabState.defaultModelProvider
|| null;
piLabState.modelId = (data.session && data.session.modelId)
|| piLabState.modelId
|| piLabState.defaultModelId
|| null;
var sessionThinkingLevel = data.session && data.session.thinkingLevel;
if (sessionThinkingLevel) {
piLabState.thinkingLevel = normalizeThinkingLevel(sessionThinkingLevel);
@@ -3851,6 +3874,8 @@
es.close();
piLabEventSource = null;
}
// S4: optimistic abort already applied UI; ignore duplicate SSE abort.
if (piLabState.status === STATE_ABORTED) return;
applyPiRunEvent({ type: 'assistant_abort', payload: payload });
setState(STATE_ABORTED);
updateMessages();
@@ -4056,9 +4081,12 @@
var deltaToolPayload = assistantToolCallPayload(msgData);
if (deltaToolPayload) upsertToolCall(deltaToolPayload, 'running', { args: deltaToolPayload.args || {} });
} else if (eventType === 'response' && payload.command === 'abort') {
applyPiRunEvent({ type: 'assistant_abort', payload: payload });
setState(STATE_ABORTED);
updateMessages();
// S4: ignore duplicate abort after optimistic UI apply.
if (piLabState.status !== STATE_ABORTED) {
applyPiRunEvent({ type: 'assistant_abort', payload: payload });
setState(STATE_ABORTED);
updateMessages();
}
} else if (eventType === 'citation' || msgDataType === 'citation') {
applyPiRunEvent({ type: 'citation', payload: { source: payload.source || msgData.source || '', title: payload.title || msgData.title || '', url: payload.url || msgData.url || '#' } });
updateMessages();
@@ -4088,15 +4116,132 @@
}
}
function pageAiBlockingDirtyStateLocal(dirtyState) {
var state = String(dirtyState || '').trim();
var normalized = state.toLowerCase();
if (normalized === 'dirty') return 'Dirty';
if (normalized === 'stale') return 'Stale';
if (normalized === 'deleted') return 'Deleted';
if (normalized === 'externalmodified' || normalized === 'external-change-conflict' || normalized === 'hasexternalconflict') return 'ExternalModified';
return '';
}
function buildPiLabAgentTargetPackage(context) {
var active = activeEditorSnapshot() || {};
var activeWorkspacePath = active.workspacePath && typeof active.workspacePath === 'object' ? active.workspacePath : {};
var relativePath = normalizeSlashes(context && context.pagePath || activeWorkspacePath.relativePath || active.path || '');
var rootUri = String(context && context.rootUri || activeWorkspacePath.rootUri || active.rootUri || '').trim();
var workspaceId = String(context && context.workspaceId || activeWorkspacePath.workspaceId || active.workspaceId || '').trim();
var documentId = String(context && context.pageId || active.documentId || activeWorkspacePath.documentId || '').trim();
var resourceKind = String(activeWorkspacePath.resourceKind || active.resourceKind || active.kind || 'markdown_page').trim() || 'markdown_page';
var objectIdentity = String(active.objectIdentity || activeWorkspacePath.objectIdentity || documentId || relativePath || '').trim();
var onlyofficeSessionId = String(active.onlyofficeSessionId || active.bridgeSessionId || '').trim();
var dirtyState = String(active.dirtyState || active.dirtyGuard || '').trim();
var primaryTargetId = objectIdentity || documentId || relativePath;
var workspacePath = {
schema: 'mnote.workspace_path.v1',
workspaceId: workspaceId,
sourceKind: 'local_folder',
rootUri: rootUri,
relativePath: relativePath,
documentId: documentId,
objectIdentity: objectIdentity,
resourceKind: resourceKind,
};
var targetEntry = {
targetId: primaryTargetId,
objectIdentity: objectIdentity,
documentId: documentId,
workspaceId: workspaceId,
sourceKind: 'local_folder',
rootUri: rootUri,
relativePath: relativePath,
resourceKind: resourceKind,
dirtyState: dirtyState,
onlyofficeSessionId: onlyofficeSessionId,
bridgeSessionId: onlyofficeSessionId,
paneRole: active.paneRole || 'primary',
title: context && context.pageTitle || active.title || '',
policy: {
permission: relativePath ? 'read_write' : 'read',
writeRequiresCleanBuffer: true,
conflictPolicy: 'fail_on_dirty_or_stale'
}
};
return {
schema: 'mnote.agent_target_package.v1',
source: 'pi_lab_send_target_snapshot',
frozenAt: Date.now(),
primaryTargetId: primaryTargetId,
onlyofficeSessionId: onlyofficeSessionId,
bridgeSessionId: onlyofficeSessionId,
workspaceId: workspaceId,
sourceKind: 'local_folder',
rootUri: rootUri,
documentId: documentId,
objectIdentity: objectIdentity,
resourceKind: resourceKind,
workspacePath: workspacePath,
currentFile: relativePath ? {
rootUri: rootUri,
relativePath: relativePath,
documentId: documentId,
objectIdentity: objectIdentity,
resourceKind: resourceKind
} : null,
allowedFiles: relativePath ? [relativePath] : [],
targets: [targetEntry],
policy: {
writeRequiresExplicitTarget: true,
allowedFilesSource: 'selected_page_ai_target',
conflictPolicy: 'fail_on_dirty_or_stale'
}
};
}
function fetchPiLabTargetBufferState(context, targetPackage) {
var workspacePath = targetPackage && targetPackage.workspacePath || {};
var documentId = String(context && context.pageId || workspacePath.documentId || '').trim();
var rootUri = String(context && context.rootUri || workspacePath.rootUri || '').trim();
if (!documentId || !rootUri) return Promise.resolve(null);
var relativePath = String(workspacePath.relativePath || context && context.pagePath || '').trim();
var url = new URL('/api/documents/buffer-state', window.location.origin);
url.searchParams.set('documentId', documentId);
url.searchParams.set('sourceKind', 'local_folder');
url.searchParams.set('rootUri', rootUri);
var workspaceId = String(context && context.workspaceId || workspacePath.workspaceId || '').trim();
if (workspaceId) url.searchParams.set('workspaceId', workspaceId);
if (relativePath) url.searchParams.set('relativePath', relativePath);
return fetch(url.toString(), { cache: 'no-store', credentials: 'same-origin', headers: { accept: 'application/json' } })
.then(function (response) {
return response.json().catch(function () { return null; }).then(function (payload) {
if (!response.ok || !payload || payload.ok !== true) return null;
return payload.result || null;
});
})
.catch(function () { return null; });
}
function assertPiLabTargetWritable(context, targetPackage) {
var active = activeEditorSnapshot() || {};
var snapshotDirty = pageAiBlockingDirtyStateLocal(active.dirtyState || active.dirtyGuard);
return fetchPiLabTargetBufferState(context, targetPackage).then(function (bufferState) {
var bufferDirty = pageAiBlockingDirtyStateLocal(bufferState && bufferState.dirtyState);
var blockedState = bufferDirty || snapshotDirty;
if (!blockedState) return bufferState;
var error = new Error('目标文档存在未保存或外部变更状态(' + blockedState + '),请先保存、解决冲突或刷新后再让 AI 写入。');
error.code = 'page_ai_target_buffer_not_writable';
error.documentId = String(context && context.pageId || '').trim();
error.dirtyState = blockedState;
throw error;
});
}
function sendPrompt(text, options) {
var prompt = String(text || '').trim();
if (!prompt) return;
if (!prompt) return Promise.resolve();
var streamingBehavior = options && options.streamingBehavior;
var midStream = piLabState.status === STATE_STREAMING && streamingAssistantMsg;
applyPiRunEvent({ type: 'user_prompt', text: prompt, meta: midStream ? (streamingBehavior === 'followUp' ? 'follow-up queued' : 'steer') : '' });
if (!midStream) applyPiRunEvent({ type: 'assistant_begin' });
setState(STATE_STREAMING);
updateMessages();
var context = applyCurrentContextToState();
var contextSelection = selectedContextPayload(context);
var refs = contextSelection.refs || [];
@@ -4104,27 +4249,39 @@
var includeAnyContext = refs.length > 0;
var includePageContext = refs.indexOf('current_page') >= 0 || refs.indexOf('selection') >= 0 || refs.indexOf('lightrag') >= 0;
var includeFolderContext = refs.indexOf('folder') >= 0;
var body = {
sessionId: piLabState.sessionId,
message: prompt,
rootUri: includeAnyContext ? (context.rootUri || undefined) : undefined,
workspaceId: includeAnyContext ? (context.workspaceId || undefined) : undefined,
pagePath: includePageContext ? (context.pagePath || undefined) : undefined,
pageTitle: includePageContext ? (context.pageTitle || undefined) : undefined,
folderPath: includeFolderContext && selectedFolderPath !== null ? selectedFolderPath : undefined,
contextRefs: contextSelection.refs,
selectedContext: contextSelection,
};
if (streamingBehavior) body.streamingBehavior = streamingBehavior;
if (piLabState.pendingImages && piLabState.pendingImages.length) {
body.images = piLabState.pendingImages;
piLabState.pendingImages = [];
}
return fetch(API.SEND, {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
var targetPackage = buildPiLabAgentTargetPackage(context);
// S7: block send when open target buffer is dirty/stale (fail_on_dirty_or_stale).
var writableGate = (context && context.pagePath)
? assertPiLabTargetWritable(context, targetPackage)
: Promise.resolve(null);
return writableGate.then(function () {
applyPiRunEvent({ type: 'user_prompt', text: prompt, meta: midStream ? (streamingBehavior === 'followUp' ? 'follow-up queued' : 'steer') : '' });
if (!midStream) applyPiRunEvent({ type: 'assistant_begin' });
setState(STATE_STREAMING);
updateMessages();
var body = {
sessionId: piLabState.sessionId,
message: prompt,
rootUri: includeAnyContext ? (context.rootUri || undefined) : undefined,
workspaceId: includeAnyContext ? (context.workspaceId || undefined) : undefined,
pagePath: includePageContext ? (context.pagePath || undefined) : undefined,
pageTitle: includePageContext ? (context.pageTitle || undefined) : undefined,
folderPath: includeFolderContext && selectedFolderPath !== null ? selectedFolderPath : undefined,
contextRefs: contextSelection.refs,
selectedContext: contextSelection,
targetPackage: targetPackage,
};
if (streamingBehavior) body.streamingBehavior = streamingBehavior;
if (piLabState.pendingImages && piLabState.pendingImages.length) {
body.images = piLabState.pendingImages;
piLabState.pendingImages = [];
}
return fetch(API.SEND, {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
})
.then(function (r) {
return responseJsonOrError(r, 'Send failed');
@@ -4134,7 +4291,15 @@
if (!piLabEventSource || piLabEventSource.readyState === EventSource.CLOSED) connectEventSource(data.sessionId || piLabState.sessionId);
})
.catch(function (err) {
if (streamingAssistantMsg) applyPiRunEvent({ type: 'assistant_error', message: err.message });
var message = err && err.message ? err.message : 'Send failed';
if (err && err.code === 'page_ai_target_buffer_not_writable') {
showPiToast(message, 'warning');
updateDiagnostics(message);
setState(piLabState.sessionId ? STATE_STARTED : STATE_IDLE);
updateMessages();
return;
}
if (streamingAssistantMsg) applyPiRunEvent({ type: 'assistant_error', message: message });
setState(STATE_STARTED);
updateMessages();
});
@@ -4330,23 +4495,19 @@
piLabEventSource.close();
piLabEventSource = null;
}
if (piLabState.sessionId) {
fetch(API.ABORT, {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ sessionId: piLabState.sessionId }),
}).then(function (response) {
return response.json().catch(function () { return {}; });
}).then(function (payload) {
applyPiRunEvent({ type: 'assistant_abort', payload: Object.assign({ stopReason: 'aborted' }, payload || {}) });
updateMessages();
}).catch(function () {});
}
applyPiRunEvent({ type: 'assistant_abort', payload: { reason: '已中止', stopReason: 'aborted' } });
setState(STATE_ABORTED);
updateMessages();
}
// S4: apply abort UI state exactly once (optimistic). Do not re-apply from ABORT response.
applyPiRunEvent({ type: 'assistant_abort', payload: { reason: '已中止', stopReason: 'aborted' } });
setState(STATE_ABORTED);
updateMessages();
if (piLabState.sessionId) {
fetch(API.ABORT, {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ sessionId: piLabState.sessionId }),
}).catch(function () {});
}
}
function startNewConversation() {
var previousSessionId = piLabState.sessionId;