@lsamien/yaovi 0.1.0 → 0.1.2
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/build-info.json +44 -3
- package/desktop/main.mjs +18 -9
- package/dist/assets/{AboutDialog-BW25cB-W.js → AboutDialog-CJ9tCjSs.js} +2 -2
- package/dist/assets/AboutDialog-CJ9tCjSs.js.map +1 -0
- package/dist/assets/{BotAutomationsView-BoK2R918.js → BotAutomationsView-CsoI3js3.js} +2 -2
- package/dist/assets/{BotAutomationsView-BoK2R918.js.map → BotAutomationsView-CsoI3js3.js.map} +1 -1
- package/dist/assets/{BotControlSettingsPanel-BbhD2eC0.js → BotControlSettingsPanel-DIuBZlLu.js} +2 -2
- package/dist/assets/{BotControlSettingsPanel-BbhD2eC0.js.map → BotControlSettingsPanel-DIuBZlLu.js.map} +1 -1
- package/dist/assets/{BotPluginsDialog-DjwFw-lt.js → BotPluginsDialog-BsGszDMH.js} +2 -2
- package/dist/assets/{BotPluginsDialog-DjwFw-lt.js.map → BotPluginsDialog-BsGszDMH.js.map} +1 -1
- package/dist/assets/{BotSettingsPanel-DLmnL6pH.js → BotSettingsPanel-j3ThYgt2.js} +3 -3
- package/dist/assets/{BotSettingsPanel-DLmnL6pH.js.map → BotSettingsPanel-j3ThYgt2.js.map} +1 -1
- package/dist/assets/{ComputerPanel-CRIF1YEd.js → ComputerPanel-DdlgOm-B.js} +2 -2
- package/dist/assets/{ComputerPanel-CRIF1YEd.js.map → ComputerPanel-DdlgOm-B.js.map} +1 -1
- package/dist/assets/{ComputerViewerView-CizYWH2R.js → ComputerViewerView-I3adPI6O.js} +2 -2
- package/dist/assets/{ComputerViewerView-CizYWH2R.js.map → ComputerViewerView-I3adPI6O.js.map} +1 -1
- package/dist/assets/ConversationsView-CBhzg-9w.css +1 -0
- package/dist/assets/{ConversationsView-1igFR9VY.js → ConversationsView-CxVFk5sQ.js} +73 -73
- package/dist/assets/ConversationsView-CxVFk5sQ.js.map +1 -0
- package/dist/assets/{DevicePairingDialog-orOS4xqu.js → DevicePairingDialog-C307ARt-.js} +2 -2
- package/dist/assets/{DevicePairingDialog-orOS4xqu.js.map → DevicePairingDialog-C307ARt-.js.map} +1 -1
- package/dist/assets/{LocalVmSettingsPanel-DzM--Lpg.js → LocalVmSettingsPanel-Zs7CiXdf.js} +2 -2
- package/dist/assets/{LocalVmSettingsPanel-DzM--Lpg.js.map → LocalVmSettingsPanel-Zs7CiXdf.js.map} +1 -1
- package/dist/assets/{ProviderLoginDialog-Bw5KVMfi.js → ProviderLoginDialog-D3mqdAlo.js} +2 -2
- package/dist/assets/{ProviderLoginDialog-Bw5KVMfi.js.map → ProviderLoginDialog-D3mqdAlo.js.map} +1 -1
- package/dist/assets/{RunnerSettingsPanel-B-fwuBEs.js → RunnerSettingsPanel-DjMGnXGP.js} +2 -2
- package/dist/assets/{RunnerSettingsPanel-B-fwuBEs.js.map → RunnerSettingsPanel-DjMGnXGP.js.map} +1 -1
- package/dist/assets/{StandaloneDialog-7GkhtRlT.js → StandaloneDialog-BXCK22Dx.js} +2 -2
- package/dist/assets/{StandaloneDialog-7GkhtRlT.js.map → StandaloneDialog-BXCK22Dx.js.map} +1 -1
- package/dist/assets/{UpdateCheckDialog-DYiM4xBv.js → UpdateCheckDialog-DAY0VEQm.js} +2 -2
- package/dist/assets/{UpdateCheckDialog-DYiM4xBv.js.map → UpdateCheckDialog-DAY0VEQm.js.map} +1 -1
- package/dist/assets/{UsageView-D1cjS1hy.js → UsageView-DNwG8LAx.js} +2 -2
- package/dist/assets/{UsageView-D1cjS1hy.js.map → UsageView-DNwG8LAx.js.map} +1 -1
- package/dist/assets/{WorkspaceKnowledgePanel-DcGdLRS1.js → WorkspaceKnowledgePanel-hy_IkIJI.js} +2 -2
- package/dist/assets/{WorkspaceKnowledgePanel-DcGdLRS1.js.map → WorkspaceKnowledgePanel-hy_IkIJI.js.map} +1 -1
- package/dist/assets/{WorkspaceRoutinesPanel-DLntimKs.js → WorkspaceRoutinesPanel-B7PkBP7x.js} +2 -2
- package/dist/assets/{WorkspaceRoutinesPanel-DLntimKs.js.map → WorkspaceRoutinesPanel-B7PkBP7x.js.map} +1 -1
- package/dist/assets/{WorkstreamsView-DMoeV8Eu.js → WorkstreamsView-BeE2TRXK.js} +2 -2
- package/dist/assets/{WorkstreamsView-DMoeV8Eu.js.map → WorkstreamsView-BeE2TRXK.js.map} +1 -1
- package/dist/assets/{index-D0pMYnOi.js → index-D11cgXI9.js} +3 -3
- package/dist/assets/{index-D0pMYnOi.js.map → index-D11cgXI9.js.map} +1 -1
- package/dist/assets/{pdf-BCisgslg.js → pdf-BjTcV6As.js} +2 -2
- package/dist/assets/{pdf-BCisgslg.js.map → pdf-BjTcV6As.js.map} +1 -1
- package/dist/index.html +1 -1
- package/dist-server/server/botControl.js +13 -1
- package/dist-server/server/desktopEnvironments.js +10 -9
- package/dist-server/server/desktopHostProtocol.js +1 -1
- package/dist-server/server/harness/agentRuntime.js +22 -8
- package/dist-server/server/workspaceDialoguePolicy.js +3 -1
- package/dist-server/server/workspaceKnowledge.js +25 -16
- package/dist-server/server/workspaceMemoryContext.js +75 -19
- package/dist-server/server/workspacePrompt.js +11 -4
- package/dist-server/server/workspaceRuntime.js +35 -13
- package/dist-server/shared/botEnvironment.js +10 -4
- package/dist-server/shared/fileTransferEndpoint.mjs +5 -5
- package/dist-server/shared/hostPaths.mjs +14 -4
- package/package.json +4 -3
- package/release.json +3 -3
- package/runtime/hermes/hermes_state_messages.py +17 -0
- package/runtime/worker.py +34 -1
- package/dist/assets/AboutDialog-BW25cB-W.js.map +0 -1
- package/dist/assets/ConversationsView-1igFR9VY.js.map +0 -1
- package/dist/assets/ConversationsView-h94_iPtv.css +0 -1
- package/dist-server/server/harness/context.js +0 -88
|
@@ -59,7 +59,7 @@ import { createWorkspaceToolLease } from './workspaceToolLease.js';
|
|
|
59
59
|
import { WorkspaceTaskCoordinator } from './taskCoordinator.js';
|
|
60
60
|
import { isDiscussion, discussionRounds } from './workspaceDiscussion.js';
|
|
61
61
|
import { WorkspaceKnowledge } from './workspaceKnowledge.js';
|
|
62
|
-
import { emptyMemoryContext, memoryResetReason,
|
|
62
|
+
import { emptyMemoryContext, memoryResetReason, advanceMemory, renderMemoryCorrections } from './workspaceMemoryContext.js';
|
|
63
63
|
import { WorkspaceCollaboration } from './workspaceCollaboration.js';
|
|
64
64
|
import { WorkspaceKnowledgeTools, BOT_KNOWLEDGE_RULES } from './workspaceKnowledgeTools.js';
|
|
65
65
|
export { mentionedAgents } from './workspaceMentions.js';
|
|
@@ -406,7 +406,8 @@ export class WorkspaceRuntime extends WorkspaceScheduler {
|
|
|
406
406
|
}
|
|
407
407
|
async deliverForegroundInsertion(owner, conversation, agent, body, targetRunId) {
|
|
408
408
|
const text = body.content.trim();
|
|
409
|
-
|
|
409
|
+
// Insertion only corrects the running task; the message keeps its own source and the run keeps its snapshot.
|
|
410
|
+
if (!text || body.fileIds.length || body.mentionIds.length)
|
|
410
411
|
return undefined;
|
|
411
412
|
const foreground = targetRunId ? [this.store.require(owner, 'run', targetRunId)] : this.foregroundMainRuns(owner, conversation.id).filter(run => run.conversationTaskId === body.taskId);
|
|
412
413
|
const candidates = foreground.filter(run => !run.stopRequested && ['queued', 'running', 'waiting'].includes(run.status))
|
|
@@ -467,11 +468,13 @@ export class WorkspaceRuntime extends WorkspaceScheduler {
|
|
|
467
468
|
const message = {
|
|
468
469
|
id: messageId, clientRequestId: body.requestId, conversationId: conversation.id, conversationTaskId: root.conversationTaskId, seq: 0, role: 'user',
|
|
469
470
|
content: text, reasoning: '', runId, status: 'complete', attachments: [], tools: [], createdAt: now, steered: true, steeredInto: root.id,
|
|
471
|
+
...(body.deviceHost ? { deviceHost: body.deviceHost } : {}),
|
|
470
472
|
};
|
|
471
473
|
this.store.saveMessage(owner, message);
|
|
472
474
|
const run = {
|
|
473
475
|
id: runId, conversationId: conversation.id, conversationTaskId: root.conversationTaskId, messageId,
|
|
474
476
|
mentionIds: [], activeAgentId: agent.id, status: 'complete', round: 0, steeredInto: root.id,
|
|
477
|
+
...(body.deviceHost ? { deviceHost: body.deviceHost } : {}),
|
|
475
478
|
createdAt: now, updatedAt: now, authorizationVersion: this.authorizationVersion(owner),
|
|
476
479
|
};
|
|
477
480
|
this.store.saveRun(owner, run);
|
|
@@ -517,6 +520,7 @@ export class WorkspaceRuntime extends WorkspaceScheduler {
|
|
|
517
520
|
mentionIds: [], activeAgentId: conversation.administratorId, status: 'queued', round: 0,
|
|
518
521
|
createdAt: now, updatedAt: now, authorizationVersion: this.authorizationVersion(owner),
|
|
519
522
|
routeIntent: 'main_only', steerText: text.trim(),
|
|
523
|
+
...(trigger.deviceHost ? { deviceHost: trigger.deviceHost } : {}),
|
|
520
524
|
};
|
|
521
525
|
this.store.atomic(() => {
|
|
522
526
|
this.admit(owner, run, conversation, trigger);
|
|
@@ -918,10 +922,12 @@ export class WorkspaceRuntime extends WorkspaceScheduler {
|
|
|
918
922
|
}
|
|
919
923
|
const memorySettings = new AgentRuntimeConfiguration(this.store).resolve(owner, agent.id).memory;
|
|
920
924
|
const memoryEnabled = memorySettings.enabled;
|
|
921
|
-
//
|
|
922
|
-
//
|
|
923
|
-
//
|
|
924
|
-
const
|
|
925
|
+
// Unmanaged context/recall combines self, shared and project memories. Managed
|
|
926
|
+
// Bots receive only scopes their rules explicitly allow (specs/021); tools keep
|
|
927
|
+
// their own review flow, and native self recall stays with the Harness.
|
|
928
|
+
const memoryAllowance = this.botControl.state(owner).enabled ? this.botControl.memoryReadScopes(owner, agent.id, { groupId: this.botControl.permissions.group(owner, run.id), projectId: root.projectId }) : undefined;
|
|
929
|
+
const memorySkip = !botCapabilities.memory ? 'unsupported' : !memoryEnabled ? 'disabled' : recovering ? 'recovering' : temporaryBackground ? 'temporary' : undefined;
|
|
930
|
+
const memory = !memorySkip ? await this.knowledge.context(owner, agent.id, root.projectId, memoryAllowance && { self: memoryAllowance.self.allowed, shared: memoryAllowance.shared.allowed, project: memoryAllowance.project.allowed }) : emptyMemoryContext('unsupported');
|
|
925
931
|
const memoryStatus = botCapabilities.memory ? 'ready' : 'upgrade_required';
|
|
926
932
|
if (agent.memoryStatus !== memoryStatus) {
|
|
927
933
|
agent = { ...agent, memoryStatus };
|
|
@@ -929,20 +935,28 @@ export class WorkspaceRuntime extends WorkspaceScheduler {
|
|
|
929
935
|
this.store.event(owner, 'agent.changed', this.store.agentSummary(agent));
|
|
930
936
|
}
|
|
931
937
|
let binding = this.store.get(owner, 'binding', key);
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
938
|
+
// Memory content changes are delivered as corrections (user decision 2026-10-22);
|
|
939
|
+
// only a different authorized scope rebuilds the session. An uncertain admission
|
|
940
|
+
// recomputes from the last admitted state, whose ledger is cumulative.
|
|
941
|
+
const memoryReset = !recovering && binding ? memoryResetReason(memory, binding.memoryState) : undefined;
|
|
935
942
|
const rebuild = this.store.get(owner, 'binding-reset', key);
|
|
936
943
|
const sourceReset = !!rebuild;
|
|
937
944
|
const movedRunner = !!rebuild && rebuild.reason !== 'configuration_changed' || !!binding && (binding.deviceHost !== root.deviceHost || binding.computerEnvironmentId !== agent.computerEnvironmentId || binding.runnerId !== target.runner?.id || (binding.execution ?? 'profile') !== chatExecution || !!binding.nativeComputer !== !!target.runner?.nativeComputer);
|
|
938
945
|
// Rebuild old exclusive-environment sessions once: their 模型执行器 Agent may
|
|
939
946
|
// still retain restricted native context even after a new tool binding.
|
|
947
|
+
// A rebuilt session still replays recaps of older messages; keep the
|
|
948
|
+
// correction ledger (a different scope starts a fresh one in advanceMemory).
|
|
949
|
+
let carriedMemory;
|
|
940
950
|
const resetReason = (execution && (binding?.executionRevision !== execution.revision || binding?.executionPolicyVersion !== 2) ? 'execution_environment_changed' : undefined) ?? memoryReset ?? (sourceReset ? rebuild?.reason ?? 'source_changed' : movedRunner ? 'runner_changed' : undefined);
|
|
941
951
|
if (resetReason) {
|
|
942
952
|
if (recovering)
|
|
943
953
|
throw new HttpError(409, '执行节点已变化,不能在另一节点重放原执行', 'runner_target_changed');
|
|
944
954
|
this.inspector?.record(owner, c.id, { method: 'bot.session-reset', direction: 'event', agentId: agent.id, runId: run.runId, taskId: run.conversationTaskId,
|
|
945
955
|
data: { reason: resetReason } });
|
|
956
|
+
// Count only sessions actually discarded; a first turn has nothing to reset.
|
|
957
|
+
if (binding)
|
|
958
|
+
this.store.event(owner, 'runtime.event', { botId: agent.id, runId: run.runId, workId: run.id, name: 'session.reset', value: { reason: resetReason } }, c.id);
|
|
959
|
+
carriedMemory = binding?.memoryState;
|
|
946
960
|
binding = undefined;
|
|
947
961
|
}
|
|
948
962
|
let message = run.currentMessageId
|
|
@@ -1527,6 +1541,7 @@ export class WorkspaceRuntime extends WorkspaceScheduler {
|
|
|
1527
1541
|
const message = error instanceof Error ? error.message : '';
|
|
1528
1542
|
if (recovering || (!['computer_session_mode_changed', 'model_session_reset_required'].includes(code) && !message.includes('执行方式已改变')))
|
|
1529
1543
|
throw error;
|
|
1544
|
+
carriedMemory ??= binding?.memoryState;
|
|
1530
1545
|
binding = undefined;
|
|
1531
1546
|
opened = await createSession();
|
|
1532
1547
|
}
|
|
@@ -1542,7 +1557,7 @@ export class WorkspaceRuntime extends WorkspaceScheduler {
|
|
|
1542
1557
|
binding = {
|
|
1543
1558
|
toolsBound: recovering ? binding?.toolsBound : undefined,
|
|
1544
1559
|
memoryVersion: binding?.memoryVersion,
|
|
1545
|
-
memoryState: binding?.memoryState,
|
|
1560
|
+
memoryState: binding?.memoryState ?? carriedMemory,
|
|
1546
1561
|
memoryPending: binding?.memoryPending,
|
|
1547
1562
|
deviceHost: root.deviceHost,
|
|
1548
1563
|
runnerId: target.runner?.id,
|
|
@@ -1841,7 +1856,7 @@ export class WorkspaceRuntime extends WorkspaceScheduler {
|
|
|
1841
1856
|
}
|
|
1842
1857
|
catch { /* keep original query */ }
|
|
1843
1858
|
}
|
|
1844
|
-
const recalled = this.knowledge.nativePeerMode || ['unsupported', 'temporary'].includes(memory.version) || !memorySettings.recall ? '' : await this.knowledge.recall(owner, agent.id, root.projectId, recallText, memory).catch(error => { this.store.event(owner, 'memory.recall.failed', { agentId: agent.id, runId: root.id, code: error instanceof HttpError ? error.code : 'memory_recall_failed', at: Date.now() }); return '记忆召回暂时失败;不能据此判断没有历史事实,可重试记忆工具。'; });
|
|
1859
|
+
const recalled = this.knowledge.nativePeerMode || ['unsupported', 'temporary'].includes(memory.version) || !memorySettings.recall ? '' : await this.knowledge.recall(owner, agent.id, root.projectId, recallText, memory, memoryAllowance && { self: memoryAllowance.self.allowed, shared: memoryAllowance.shared.allowed, project: memoryAllowance.project.allowed }).catch(error => { this.store.event(owner, 'memory.recall.failed', { agentId: agent.id, runId: root.id, code: error instanceof HttpError ? error.code : 'memory_recall_failed', at: Date.now() }); return '记忆召回暂时失败;不能据此判断没有历史事实,可重试记忆工具。'; });
|
|
1845
1860
|
if (environment.tools.plugins && this.mcpPrepare) {
|
|
1846
1861
|
const skipped = await this.mcpPrepare(owner, agent).catch(() => [{ service: 'MCP 服务', code: 'mcp_unavailable', message: 'MCP 配置读取失败,本轮未连接' }]);
|
|
1847
1862
|
if (skipped.length) {
|
|
@@ -1863,6 +1878,8 @@ export class WorkspaceRuntime extends WorkspaceScheduler {
|
|
|
1863
1878
|
.filter(message => message.role === 'user' && message.conversationId === c.id && message.conversationTaskId === run.conversationTaskId && message.steeredInto === run.runId)
|
|
1864
1879
|
.sort((a, b) => a.seq - b.seq);
|
|
1865
1880
|
const currentText = insertions.length ? `${text}\n\n[以下是用户在本轮准备期间按发送顺序追加的消息,请一并处理;有更正时以后面的要求为准]\n${insertions.map(message => message.content).join('\n\n')}` : text;
|
|
1881
|
+
// Persisted only after the prompt is admitted, like the previous baseline.
|
|
1882
|
+
const nextMemory = advanceMemory(memory, binding.memoryState);
|
|
1866
1883
|
const promptInput = {
|
|
1867
1884
|
style: dialogue.style,
|
|
1868
1885
|
permissionMode: dialogue.style || dialogue.context ? (this.botControl.permissions.enabled ? (this.botControl.permissions.settings(owner).fullAccess ? 'allow' : 'ask') : agent.approvalPolicy ?? 'ask') : undefined,
|
|
@@ -1874,7 +1891,7 @@ export class WorkspaceRuntime extends WorkspaceScheduler {
|
|
|
1874
1891
|
teamRules: team ? TEAM_TOOL_RULES : undefined,
|
|
1875
1892
|
routineRules: routines ? ROUTINE_TOOL_RULES : undefined,
|
|
1876
1893
|
knowledgeRules: knowledge ? BOT_KNOWLEDGE_RULES : undefined,
|
|
1877
|
-
memory: [memory.text, recalled].filter(Boolean).join('\n\n'), noReply: NO_REPLY,
|
|
1894
|
+
memory: [memory.text, renderMemoryCorrections(memory, nextMemory), recalled].filter(Boolean).join('\n\n'), noReply: NO_REPLY,
|
|
1878
1895
|
marker: `[yaovi-run:${run.runId}:${resultMessage.id}]`,
|
|
1879
1896
|
content: currentText + (systemTurn?.description.length ? '\n[本轮系统服务目录,仅含元数据]\n' + JSON.stringify(systemTurn.description) : ''), contentKind: c.kind === 'group' || !!resetReason || !!run.sessionScopeId || externalReplies ? 'history' : 'user', attachmentRefs,
|
|
1880
1897
|
};
|
|
@@ -1885,6 +1902,11 @@ export class WorkspaceRuntime extends WorkspaceScheduler {
|
|
|
1885
1902
|
}) : undefined;
|
|
1886
1903
|
if (dialogue.context && target.model && !workspacePrompt)
|
|
1887
1904
|
this.store.event(owner, 'dialogue.context.fallback', { runId: root.id, reason: 'context_budget_fallback' }, c.id);
|
|
1905
|
+
// Character counts of what this turn sends; provider usage is reported separately.
|
|
1906
|
+
// Audit what memory rode along: scopes, record IDs and counts, never content.
|
|
1907
|
+
const memoryScopes = Object.fromEntries(['self', 'shared', 'project'].map(name => [name, memory.scopes?.includes({ self: 'agent', shared: 'user', project: 'project' }[name]) ? 'injected' : `skipped:${memorySkip ?? (memory.version === 'temporary' ? 'temporary' : name === 'project' && !root.projectId ? 'no_project' : name === 'self' && this.knowledge.nativePeerMode ? 'native_peer' : memoryAllowance?.[name].reason ?? 'not_allowed')}`]));
|
|
1908
|
+
this.inspector?.record(owner, c.id, { method: 'bot.memory', direction: 'request', agentId: agent.id, runId: run.runId, taskId: run.conversationTaskId, data: { scopes: memoryScopes, factIds: memory.facts.map(fact => fact.id), recalledIds: [...recalled.matchAll(/; id:([^\]\s]+)\]/g)].map(match => match[1]), corrections: nextMemory.corrections?.length ?? 0 } });
|
|
1909
|
+
this.store.event(owner, 'runtime.event', { botId: agent.id, runId: run.runId, workId: run.id, name: 'context.turn', value: { layered: !!workspacePrompt, contentChars: currentText.length, turnContextChars: workspacePrompt ? workspacePrompt.turnContext.length : submittedText.length, memoryChars: promptInput.memory.length, memoryScopes, correctionCount: nextMemory.corrections?.length ?? 0, estimate: true } }, c.id);
|
|
1888
1910
|
this.inspector?.record(owner, c.id, { method: 'bot.environment', direction: 'request', agentId: agent.id, runId: run.runId, taskId: run.conversationTaskId,
|
|
1889
1911
|
data: { snapshot: environment, computerToolIds: environmentCatalog.map(tool => tool.id), pluginWarnings: resultMessage.serviceWarnings } });
|
|
1890
1912
|
binding.memoryPending = true;
|
|
@@ -1915,7 +1937,7 @@ export class WorkspaceRuntime extends WorkspaceScheduler {
|
|
|
1915
1937
|
// Only an admitted prompt may advance the durable injection baseline.
|
|
1916
1938
|
// A rejected/lost receipt must not make the next turn omit new facts.
|
|
1917
1939
|
binding.memoryVersion = memory.version;
|
|
1918
|
-
binding.memoryState =
|
|
1940
|
+
binding.memoryState = nextMemory;
|
|
1919
1941
|
binding.memoryPending = undefined;
|
|
1920
1942
|
binding.contextSeq = Math.max(binding.contextSeq ?? 0, dispatch.contextThroughSeq);
|
|
1921
1943
|
this.store.put(owner, 'binding', key, binding);
|
|
@@ -2,14 +2,19 @@ export const deviceCapabilityLabels = {
|
|
|
2
2
|
ready: '就绪', offline: '离线', disabled: '未开放', unsupported: '不支持', not_authorized: '未授权',
|
|
3
3
|
system_permission_required: '系统权限未就绪', human_control: '等待人工交还', paused: '接管中断,需重新接管并交还', agent_unavailable: '当前 Bot 不可用',
|
|
4
4
|
};
|
|
5
|
+
export const CURRENT_DEVICE_ALIASES = ['本机', '这台设备', '这台电脑'];
|
|
6
|
+
export function isCurrentDeviceAlias(value) {
|
|
7
|
+
return CURRENT_DEVICE_ALIASES.some(alias => alias === value.trim());
|
|
8
|
+
}
|
|
5
9
|
export function deviceSourceText(snapshot) {
|
|
6
10
|
if (!snapshot.sourceHost)
|
|
7
|
-
return '
|
|
11
|
+
return '本轮未收到客户端设备身份,「本机」无法确定;「这台设备」「这台电脑」同样无法识别。请明确说明来源身份缺失,提示在当前电脑的桌面客户端检查服务器连接、重新登录或更新客户端后重试;不要列出其他电脑让用户反复选择。历史消息的设备来源不适用于本轮。';
|
|
8
12
|
const host = snapshot.hosts.find(host => host.id === snapshot.sourceHost);
|
|
9
13
|
const name = host?.name || snapshot.sourceHost;
|
|
14
|
+
const aliases = '「这台设备」「这台电脑」与「本机」同义,均指本轮消息来源电脑;身份已确定时不要再次要求用户选择设备。';
|
|
10
15
|
if (!host?.online || !host.open)
|
|
11
|
-
return `本轮用户消息来源设备:${JSON.stringify(name)}(${!host?.online ? '当前离线或未连接' : '全局未开放'}
|
|
12
|
-
return `本轮用户消息来自${host.kind === 'server' ? '服务器' : '电脑'}「${name}」。对本轮而言,「本机」指这台电脑(host=${JSON.stringify(host.target)}
|
|
16
|
+
return `本轮用户消息来源设备:${JSON.stringify(name)}(${!host?.online ? '当前离线或未连接' : '全局未开放'})。「本机」仍指这台电脑,不能回退到服务器或其他电脑。${aliases}直接说明当前不可用的原因。`;
|
|
17
|
+
return `本轮用户消息来自${host.kind === 'server' ? '服务器' : '电脑'}「${name}」。对本轮而言,「本机」指这台电脑(host=${JSON.stringify(host.target)})。${aliases}`;
|
|
13
18
|
}
|
|
14
19
|
export function deviceInventoryText(snapshot) {
|
|
15
20
|
if (!snapshot.hosts.length)
|
|
@@ -19,7 +24,8 @@ export function deviceInventoryText(snapshot) {
|
|
|
19
24
|
const details = m ? [
|
|
20
25
|
m.osRelease && `系统版本=${JSON.stringify(m.osRelease)}`, m.arch && `架构=${JSON.stringify(m.arch)}`,
|
|
21
26
|
m.shell && `Shell=${JSON.stringify(m.shell)}`, m.homeDirectory && `主目录=${JSON.stringify(m.homeDirectory)}`,
|
|
22
|
-
m.defaultCwd ? `默认工作目录=${JSON.stringify(m.defaultCwd)}` : '运行路径未上报或无文件与命令授权',
|
|
27
|
+
m.defaultCwd ? `默认工作目录=${JSON.stringify(m.defaultCwd)}` : '运行路径未上报或无文件与命令授权',
|
|
28
|
+
m.fileScope === 'all' ? '文件工具范围=当前系统用户可访问的所有目录(包括其他磁盘和已连接的共享目录,受系统权限限制)' : m.fileRoots && `文件工具允许根目录=${JSON.stringify(m.fileRoots)}`,
|
|
23
29
|
m.shellScope && 'Shell范围=桌面用户的系统权限(不受文件工具根目录约束)', m.timezone && `时区=${JSON.stringify(m.timezone)}`,
|
|
24
30
|
].filter(Boolean).join(';') : '运行路径未上报或无文件与命令授权';
|
|
25
31
|
const state = !host.online ? '离线' : !host.open ? '未开放' : '在线';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { open, realpath, mkdir, mkdtemp, unlink, rename, link, rm } from 'node:fs/promises'
|
|
2
2
|
import { constants } from 'node:fs'
|
|
3
3
|
import { resolve, dirname, join } from 'node:path'
|
|
4
|
-
import { hostPathInput, pathInside } from './hostPaths.mjs'
|
|
4
|
+
import { hostPathInput, pathInside, hostFileError } from './hostPaths.mjs'
|
|
5
5
|
import { tmpdir } from 'node:os'
|
|
6
6
|
import { createHash } from 'node:crypto'
|
|
7
7
|
|
|
@@ -11,13 +11,13 @@ const require = (value, message) => { if (!value) throw new Error(message) }
|
|
|
11
11
|
|
|
12
12
|
/** Bounded, replay-safe file operations shared by native desktops and runner hosts. */
|
|
13
13
|
export class FileTransferFiles {
|
|
14
|
-
constructor(root, { confined = true } = {}) {
|
|
15
|
-
this.root = root; this.confined = confined; this.transfers = new Map(); this.lanes = new Map(); this.closed = false
|
|
14
|
+
constructor(root, { confined = true, allowUNC = false } = {}) {
|
|
15
|
+
this.root = root; this.confined = confined; this.allowUNC = allowUNC; this.transfers = new Map(); this.lanes = new Map(); this.closed = false
|
|
16
16
|
this.timer = setInterval(() => { void this.expire() }, 60000); this.timer.unref()
|
|
17
17
|
}
|
|
18
18
|
async path(input, writing = false) {
|
|
19
19
|
require(typeof input === 'string' && input.length > 0 && input.length <= 4096 && !input.includes('\0'), '文件路径无效')
|
|
20
|
-
const root = await realpath(this.root), raw = resolve(root, hostPathInput(input))
|
|
20
|
+
const root = await realpath(this.root), raw = resolve(root, hostPathInput(input, process.platform, { allowUNC: this.allowUNC }))
|
|
21
21
|
const inside = value => !this.confined || pathInside(root, value)
|
|
22
22
|
require(inside(raw), '路径超出允许范围')
|
|
23
23
|
if (!writing) { const file = await realpath(raw); require(inside(file), '路径超出允许范围'); return file }
|
|
@@ -36,7 +36,7 @@ export class FileTransferFiles {
|
|
|
36
36
|
const id = action.transferId, previous = this.lanes.get(id) ?? Promise.resolve()
|
|
37
37
|
const next = previous.catch(() => {}).then(() => this.perform(action, authorize))
|
|
38
38
|
this.lanes.set(id, next)
|
|
39
|
-
try { return await next } finally { if (this.lanes.get(id) === next) this.lanes.delete(id) }
|
|
39
|
+
try { return await next } catch (error) { throw hostFileError(error) } finally { if (this.lanes.get(id) === next) this.lanes.delete(id) }
|
|
40
40
|
}
|
|
41
41
|
async perform(action, authorize) {
|
|
42
42
|
authorize(); require(!this.closed, '传输通道已关闭')
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
2
|
|
|
3
|
-
export function hostPathInput(input, platform = process.platform) {
|
|
3
|
+
export function hostPathInput(input, platform = process.platform, { allowUNC = false } = {}) {
|
|
4
4
|
const value = String(input ?? '')
|
|
5
5
|
if (value.includes('\0')) throw new Error('路径无效')
|
|
6
6
|
if (platform === 'win32') {
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
if (/^[\\/]{2}
|
|
7
|
+
// Only explicitly authorized desktop calls accept ordinary UNC shares.
|
|
8
|
+
// Device namespaces, drive-relative paths and alternate streams stay invalid.
|
|
9
|
+
if (/^[\\/]{2}/.test(value)) {
|
|
10
|
+
const [server, share] = value.slice(2).split(/[\\/]/)
|
|
11
|
+
if (!allowUNC || !server || !share || [server, share].some(part => ['.', '..', '?'].includes(part))) throw new Error('路径无效')
|
|
12
|
+
}
|
|
13
|
+
if (/^[A-Za-z]:(?![\\/])/.test(value) || /[<>"|?*]/.test(value)) throw new Error('路径无效')
|
|
10
14
|
const rest = value.replace(/^[A-Za-z]:/, '')
|
|
11
15
|
if (rest.includes(':') || rest.split(/[\\/]/).some(part =>
|
|
12
16
|
(part !== '.' && part !== '..' && /[ .]$/.test(part)) || /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(part))) throw new Error('路径无效')
|
|
@@ -15,6 +19,12 @@ export function hostPathInput(input, platform = process.platform) {
|
|
|
15
19
|
return value.replace(/^~\//, '').replace(/^~$/, '')
|
|
16
20
|
}
|
|
17
21
|
|
|
22
|
+
/** Keep OS failures distinct from application path confinement. */
|
|
23
|
+
export function hostFileError(error) {
|
|
24
|
+
const label = { EACCES: '系统权限拒绝访问', EPERM: '系统权限拒绝访问', EROFS: '文件系统只读', ENOENT: '文件或目录不存在', ENOTDIR: '路径不是目录' }[error?.code]
|
|
25
|
+
return label ? Object.assign(new Error(`${label}(${error.code})`, { cause: error }), { code: error.code }) : error
|
|
26
|
+
}
|
|
27
|
+
|
|
18
28
|
export function pathInside(root, target, platform = process.platform) {
|
|
19
29
|
const api = platform === 'win32' ? path.win32 : path.posix
|
|
20
30
|
const relative = api.relative(root, target)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lsamien/yaovi",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Yaovi:单用户、多 Bot、多设备服务器",
|
|
6
6
|
"type": "module",
|
|
@@ -43,9 +43,10 @@
|
|
|
43
43
|
"desktop:pack:win": "npm run desktop:build:win && node scripts/windows-desktop-release.mjs",
|
|
44
44
|
"desktop:test:win": "node scripts/test-windows-desktop.mjs",
|
|
45
45
|
"agent:setup": "node scripts/agent-setup.mjs",
|
|
46
|
-
"agent:test": ".venv/bin/python tests/runtime/test_kernel.py && .venv/bin/python tests/runtime/test_compression.py && .venv/bin/python tests/runtime/test_backup.py && .venv/bin/python tests/runtime/test_hermes_web.py && .venv/bin/python tests/runtime/test_native_openviking.py && .venv/bin/python tests/runtime/test_native_openviking_config.py && .venv/bin/python tests/runtime/test_vault_env.py && vitest run tests/server/embeddedAgent.test.ts tests/server/singleUserDevices.test.ts tests/server/harnessWeb.test.ts",
|
|
46
|
+
"agent:test": ".venv/bin/python tests/runtime/test_kernel.py && .venv/bin/python tests/runtime/test_compression.py && (cd tests/runtime && ../../.venv/bin/python test_turn_context.py) && .venv/bin/python tests/runtime/test_backup.py && .venv/bin/python tests/runtime/test_hermes_web.py && .venv/bin/python tests/runtime/test_native_openviking.py && .venv/bin/python tests/runtime/test_native_openviking_config.py && .venv/bin/python tests/runtime/test_vault_env.py && vitest run tests/server/embeddedAgent.test.ts tests/server/singleUserDevices.test.ts tests/server/harnessWeb.test.ts",
|
|
47
47
|
"agent:pack": "npm run build && node scripts/build-agent-host.mjs",
|
|
48
|
-
"postinstall": "node scripts/agent-setup.mjs --postinstall"
|
|
48
|
+
"postinstall": "node scripts/agent-setup.mjs --postinstall",
|
|
49
|
+
"desktop:release:client": "npm run desktop:build:client && node scripts/client-release.mjs"
|
|
49
50
|
},
|
|
50
51
|
"dependencies": {
|
|
51
52
|
"@koa/bodyparser": "^5.0.0",
|
package/release.json
CHANGED
|
@@ -1125,6 +1125,23 @@ class SessionMessagesMixin:
|
|
|
1125
1125
|
"AND role = 'user' AND active = 1 AND content IS ?",
|
|
1126
1126
|
(_scrub_surrogates(api_content), row_id, session_id, self._encode_content(content)))
|
|
1127
1127
|
|
|
1128
|
+
def clear_user_api_content_with_prefix(self, session_id: str, prefix: str) -> int:
|
|
1129
|
+
"""Drop user-row ``api_content`` sidecars that begin with ``prefix`` across the resume lineage.
|
|
1130
|
+
|
|
1131
|
+
Yaovi sends its per-turn application state only with the live request; later turns must
|
|
1132
|
+
replay the clean ``content`` rather than that state. Sidecars without the prefix
|
|
1133
|
+
(cache-fidelity stamps, sanitize divergences) and rows written before the prefix existed
|
|
1134
|
+
are left untouched.
|
|
1135
|
+
"""
|
|
1136
|
+
ids = [sid for sid in dict.fromkeys(self._resume_lineage_ids(session_id)) if sid]
|
|
1137
|
+
if not ids or not prefix:
|
|
1138
|
+
return 0
|
|
1139
|
+
marks = ",".join("?" * len(ids))
|
|
1140
|
+
return self._write_rowcount(
|
|
1141
|
+
f"UPDATE messages SET api_content = NULL WHERE session_id IN ({marks}) AND role = 'user' "
|
|
1142
|
+
"AND api_content IS NOT NULL AND substr(api_content, 1, ?) = ?",
|
|
1143
|
+
(*ids, len(prefix), prefix))
|
|
1144
|
+
|
|
1128
1145
|
def set_user_message_content(self, session_id: str, row_id: int, content: Any) -> int:
|
|
1129
1146
|
"""Rewrite the content of ONE known active user row. Used when a user turn was written at submit
|
|
1130
1147
|
time (before the agent ran) and the turn prologue then rewrote the prompt it persists (@-file
|
package/runtime/worker.py
CHANGED
|
@@ -22,6 +22,8 @@ steer_lock = threading.Lock()
|
|
|
22
22
|
turn_closed = False
|
|
23
23
|
steer_ready = False
|
|
24
24
|
startup_steers = []
|
|
25
|
+
# Prefix of the per-turn application state Yaovi renders (src/server/workspacePrompt.ts).
|
|
26
|
+
TURN_CONTEXT_SENTINEL = "[[YAOVI_TURN_CONTEXT_V1]]"
|
|
25
27
|
|
|
26
28
|
|
|
27
29
|
def emit(kind, **payload):
|
|
@@ -106,6 +108,26 @@ def read_replies():
|
|
|
106
108
|
interrupt()
|
|
107
109
|
|
|
108
110
|
|
|
111
|
+
def strip_turn_context_sidecars(history):
|
|
112
|
+
"""Earlier turns replay their user content only, never the per-turn state sent with them."""
|
|
113
|
+
for message in history:
|
|
114
|
+
sidecar = message.get("api_content")
|
|
115
|
+
if message.get("role") == "user" and isinstance(sidecar, str) and sidecar.startswith(TURN_CONTEXT_SENTINEL):
|
|
116
|
+
message.pop("api_content", None)
|
|
117
|
+
return history
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def compose_turn(text, turn_context):
|
|
121
|
+
"""``(model-facing message, persisted message)``; state reaches this turn's calls only."""
|
|
122
|
+
if not turn_context:
|
|
123
|
+
return text, None
|
|
124
|
+
if not str(turn_context).startswith(TURN_CONTEXT_SENTINEL):
|
|
125
|
+
raise ValueError("Turn context is missing its sentinel")
|
|
126
|
+
if isinstance(text, list):
|
|
127
|
+
return [{"type": "text", "text": turn_context}, *text], text
|
|
128
|
+
return turn_context + "\n\n" + str(text), text
|
|
129
|
+
|
|
130
|
+
|
|
109
131
|
def clarify(questions):
|
|
110
132
|
answers = {}
|
|
111
133
|
for question in questions:
|
|
@@ -224,6 +246,10 @@ def main():
|
|
|
224
246
|
pending_calls -= {m.get("tool_call_id") for m in history if m.get("role") == "tool"}
|
|
225
247
|
for call in pending_calls:
|
|
226
248
|
history.append({"role":"tool", "tool_call_id":call, "content":json.dumps({"error":"Interrupted before the result was confirmed. Inspect effects before retrying.","outcomeUnknown":True})})
|
|
249
|
+
# A turn that crashed before its cleanup must not leak its state into this one,
|
|
250
|
+
# including rows reloaded from the store after in-turn compaction.
|
|
251
|
+
db.clear_user_api_content_with_prefix(session, TURN_CONTEXT_SENTINEL)
|
|
252
|
+
strip_turn_context_sidecars(history)
|
|
227
253
|
# Existing sessions retain their admitted tools and context. Only the known
|
|
228
254
|
# application identity prefix changes, at a turn boundary before model use.
|
|
229
255
|
row = db.get_session(session)
|
|
@@ -287,7 +313,14 @@ def main():
|
|
|
287
313
|
if not result:
|
|
288
314
|
raise RuntimeError("Native skill review did not finish")
|
|
289
315
|
else:
|
|
290
|
-
|
|
316
|
+
message, persisted = compose_turn(request["text"], request.get("turnContext"))
|
|
317
|
+
try:
|
|
318
|
+
result = agent.run_conversation(message, conversation_history=history,
|
|
319
|
+
**({"persist_user_message": persisted} if persisted is not None else {}))
|
|
320
|
+
finally:
|
|
321
|
+
if persisted is not None:
|
|
322
|
+
for lineage in dict.fromkeys((session, agent.session_id)):
|
|
323
|
+
db.clear_user_api_content_with_prefix(lineage, TURN_CONTEXT_SENTINEL)
|
|
291
324
|
with steer_lock:
|
|
292
325
|
turn_closed = True
|
|
293
326
|
with agent._pending_redirect_lock:
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AboutDialog-BW25cB-W.js","names":[],"sources":["../../package.json","../../src/client/components/app/AboutDialog.vue","../../src/client/components/app/AboutDialog.vue"],"sourcesContent":["{\n \"name\": \"@lsamien/yaovi\",\n \"version\": \"0.1.0\",\n \"license\": \"Apache-2.0\",\n \"description\": \"Yaovi:单用户、多 Bot、多设备服务器\",\n \"type\": \"module\",\n \"main\": \"desktop/main.mjs\",\n \"engines\": {\n \"node\": \">=24.0.0\"\n },\n \"bin\": {\n \"yaovi\": \"bin/yaovi-agent.mjs\",\n \"yaovi-agent\": \"bin/yaovi-agent.mjs\"\n },\n \"scripts\": {\n \"dev\": \"cross-env NODE_ENV=development tsx watch src/server/index.ts\",\n \"build\": \"npm run brand:verify && npm run release:verify && vue-tsc -b tsconfig.client.build.json && vite build && node scripts/clean-server.mjs && tsc -p tsconfig.server.json && node scripts/copy-worker.mjs && node scripts/write-build-info.mjs\",\n \"start\": \"node bin/yaovi-agent.mjs start\",\n \"typecheck\": \"vue-tsc -b && tsc --noEmit -p tsconfig.server.json\",\n \"test\": \"vitest run\",\n \"prepack\": \"npm run build\",\n \"test:watch\": \"vitest\",\n \"test:e2e\": \"npm run build && playwright test\",\n \"test:live\": \"cross-env YAOVI_LIVE_TEST=1 vitest run tests/live\",\n \"brand:verify\": \"node scripts/verify-brand.mjs\",\n \"release:verify\": \"node scripts/verify-release.mjs\",\n \"verify:lifecycle\": \"node scripts/verify-lifecycle.mjs\",\n \"test:e2e:workspace\": \"npm run build && playwright test -c playwright.workspace.config.ts\",\n \"test:e2e:desktop-hosts\": \"npm run build && playwright test -c playwright.desktop-hosts.config.ts\",\n \"test:e2e:team-tools\": \"npm run build && playwright test -c playwright.team-tools.config.ts\",\n \"desktop:build\": \"npm run build && node scripts/build-desktop.mjs\",\n \"desktop:build:client\": \"node scripts/write-build-info.mjs && node scripts/build-desktop-client.mjs\",\n \"desktop:dev:client\": \"npm run desktop:build:client && cross-env YAOVI_DESKTOP_CLIENT_ONLY=1 electron .\",\n \"desktop:pack:client\": \"npm run desktop:build:client && electron-builder --config electron-builder.client.yml --mac --arm64 --publish never\",\n \"desktop:dev\": \"npm run desktop:build && cross-env YAOVI_DESKTOP_CLIENT_ONLY=0 electron .\",\n \"desktop:pack\": \"npm run desktop:build && electron-builder --mac --arm64 --publish never\",\n \"desktop:release\": \"npm run desktop:build && node scripts/desktop-release.mjs\",\n \"desktop:test\": \"cross-env YAOVI_DESKTOP_CLIENT_ONLY=0 node --test desktop/*.test.mjs\",\n \"runner\": \"node dist-server/runner/index.js\",\n \"runner:dev\": \"tsx src/runner/index.ts\",\n \"runner:build\": \"node scripts/build-runner.mjs\",\n \"desktop:build:win\": \"npm run build && node scripts/build-windows-desktop.mjs\",\n \"desktop:pack:win\": \"npm run desktop:build:win && node scripts/windows-desktop-release.mjs\",\n \"desktop:test:win\": \"node scripts/test-windows-desktop.mjs\",\n \"agent:setup\": \"node scripts/agent-setup.mjs\",\n \"agent:test\": \".venv/bin/python tests/runtime/test_kernel.py && .venv/bin/python tests/runtime/test_compression.py && .venv/bin/python tests/runtime/test_backup.py && .venv/bin/python tests/runtime/test_hermes_web.py && .venv/bin/python tests/runtime/test_native_openviking.py && .venv/bin/python tests/runtime/test_native_openviking_config.py && .venv/bin/python tests/runtime/test_vault_env.py && vitest run tests/server/embeddedAgent.test.ts tests/server/singleUserDevices.test.ts tests/server/harnessWeb.test.ts\",\n \"agent:pack\": \"npm run build && node scripts/build-agent-host.mjs\",\n \"postinstall\": \"node scripts/agent-setup.mjs --postinstall\"\n },\n \"dependencies\": {\n \"@koa/bodyparser\": \"^5.0.0\",\n \"@koa/router\": \"^15.4.0\",\n \"@openviking/sdk\": \"0.1.0\",\n \"@trycua/cua-driver\": \"0.28.2\",\n \"@types/busboy\": \"^1.5.4\",\n \"@types/koa\": \"^2.15.0\",\n \"@types/koa-send\": \"^4.1.6\",\n \"@types/koa-static\": \"^4.0.4\",\n \"@types/markdown-it\": \"^14.1.2\",\n \"@types/mime-types\": \"^2.1.4\",\n \"@types/node\": \"^24.10.0\",\n \"@types/qrcode\": \"^1.5.6\",\n \"@types/ws\": \"^8.18.1\",\n \"@vicons/ionicons5\": \"^0.13.0\",\n \"@vicons/tabler\": \"^0.13.0\",\n \"@vitejs/plugin-vue\": \"^6.0.5\",\n \"@vue/tsconfig\": \"^0.9.1\",\n \"busboy\": \"^1.6.0\",\n \"cookie\": \"^1.0.2\",\n \"docx-preview\": \"^0.3.7\",\n \"dompurify\": \"^3.2.6\",\n \"highlight.js\": \"^11.11.1\",\n \"koa\": \"^2.15.3\",\n \"koa-send\": \"^5.0.1\",\n \"koa-static\": \"^5.0.0\",\n \"libsodium-wrappers-sumo\": \"0.8.4\",\n \"markdown-it\": \"^14.1.0\",\n \"mime-types\": \"^3.0.1\",\n \"naive-ui\": \"^2.44.1\",\n \"pdfjs-dist\": \"6.2.108\",\n \"pinia\": \"^3.0.4\",\n \"pinyin-pro\": \"3.29.5\",\n \"playwright\": \"1.62.1\",\n \"qrcode\": \"^1.5.4\",\n \"read-excel-file\": \"^7.0.3\",\n \"sass\": \"^1.99.0\",\n \"ssh2\": \"1.17.0\",\n \"typescript\": \"~6.0.2\",\n \"undici\": \"7.29.1\",\n \"vite\": \"^8.0.4\",\n \"vue\": \"^3.5.32\",\n \"vue-router\": \"^4.6.4\",\n \"vue-tsc\": \"^3.2.8\",\n \"ws\": \"^8.20.0\",\n \"zod\": \"^4.1.12\"\n },\n \"devDependencies\": {\n \"@playwright/test\": \"^1.60.0\",\n \"@types/jsdom\": \"^21.1.7\",\n \"@types/ssh2\": \"1.15.5\",\n \"@types/supertest\": \"^6.0.3\",\n \"@vitest/coverage-v8\": \"^3.2.4\",\n \"@vue/test-utils\": \"^2.4.6\",\n \"cross-env\": \"^10.1.0\",\n \"electron\": \"43.4.0\",\n \"electron-builder\": \"26.15.3\",\n \"electron-updater\": \"6.8.9\",\n \"esbuild\": \"0.28.2\",\n \"js-yaml\": \"4.3.2\",\n \"jsdom\": \"^27.0.1\",\n \"supertest\": \"^7.1.4\",\n \"tsx\": \"^4.21.0\",\n \"vitest\": \"^3.2.4\"\n },\n \"files\": [\n \"bin\",\n \"dist\",\n \"dist-server\",\n \"licenses\",\n \"LICENSE\",\n \"NOTICE\",\n \"THIRD_PARTY_NOTICES.md\",\n \"release.json\",\n \"build-info.json\",\n \"runtime\",\n \"scripts/agent-backup.py\",\n \"scripts/agent-setup.mjs\",\n \"!runtime/**/__pycache__/**\",\n \"!runtime/**/*.pyc\",\n \"!runtime/**/*.pyo\",\n \"!runtime/**/.env\",\n \"!runtime/**/auth.json\"\n ],\n \"publishConfig\": {\n \"access\": \"public\"\n }\n}\n","<script setup lang=\"ts\">\nimport { version } from '../../../../package.json'\nimport BrandMark from '@/components/common/BrandMark.vue'\nimport StandaloneDialog from '@/components/common/StandaloneDialog.vue'\nconst emit = defineEmits<{ close: [] }>()\n</script>\n<template>\n <StandaloneDialog title=\"关于Yaovi\" compact @close=\"emit('close')\"><section class=\"about-content\">\n <BrandMark :size=\"56\" :label=\"false\" compact bare /><h3>Yaovi</h3><p class=\"about-version\">版本 {{ version }}</p>\n <p>通过 OpenAI 模型与 Bot 对话、协作完成任务。<br />基于 Apache-2.0 许可证开放源代码。</p>\n\n </section></StandaloneDialog>\n</template>\n<style scoped>\n.about-content{display:grid;justify-items:center;gap:12px;text-align:center}.about-content h3{font-size:21px;margin:0}.about-content p{margin:0;color:var(--text-secondary);font-size:13px;line-height:1.8}.about-content .about-version{font-size:12px}.about-content nav{display:flex;flex-wrap:wrap;justify-content:center;gap:8px;margin-top:8px}.about-content a{padding:10px 8px;color:var(--accent);font-size:13px;text-decoration:none}.about-content a:hover{text-decoration:underline}.about-content a:focus-visible{outline:2px solid var(--accent);outline-offset:2px}\n</style>\n","<script setup lang=\"ts\">\nimport { version } from '../../../../package.json'\nimport BrandMark from '@/components/common/BrandMark.vue'\nimport StandaloneDialog from '@/components/common/StandaloneDialog.vue'\nconst emit = defineEmits<{ close: [] }>()\n</script>\n<template>\n <StandaloneDialog title=\"关于Yaovi\" compact @close=\"emit('close')\"><section class=\"about-content\">\n <BrandMark :size=\"56\" :label=\"false\" compact bare /><h3>Yaovi</h3><p class=\"about-version\">版本 {{ version }}</p>\n <p>通过 OpenAI 模型与 Bot 对话、协作完成任务。<br />基于 Apache-2.0 许可证开放源代码。</p>\n\n </section></StandaloneDialog>\n</template>\n<style scoped>\n.about-content{display:grid;justify-items:center;gap:12px;text-align:center}.about-content h3{font-size:21px;margin:0}.about-content p{margin:0;color:var(--text-secondary);font-size:13px;line-height:1.8}.about-content .about-version{font-size:12px}.about-content nav{display:flex;flex-wrap:wrap;justify-content:center;gap:8px;margin-top:8px}.about-content a{padding:10px 8px;color:var(--accent);font-size:13px;text-decoration:none}.about-content a:hover{text-decoration:underline}.about-content a:focus-visible{outline:2px solid var(--accent);outline-offset:2px}\n</style>\n"],"mappings":"wVCIA,IAAM,EAAO,gBAGX,EAAA,EAAA,EAI6B,EAAA,CAJX,MAAM,UAAU,QAAA,GAAS,QAAK,AAAA,EAAA,KAAA,GAAE,EAAI,OAAA,IAAW,QAAA,MAIvD,CAJuD,EAIvD,UAJuD,EAIvD,CAHR,EAAoD,EAAA,CAAxC,KAAM,GAAK,MAAO,GAAO,QAAA,GAAQ,KAAA,KAAO,AAAA,EAAA,KAAA,EAAc,KAAA,KAAV,QAAK,EAAA,EAAK,EAA6C,IAA7C,EAAyB,MAAG,EAAG,EAAA,CAAA,CAAO,EAAA,CAAA,EACxG,AAAA,EAAA,KAAA,EAAgE,IAAA,KAAA,CAA7D,EAAA,8BAA4B,EAAA,EAAM,IAAA,EAAA,EAAA,yBAAuB"}
|