fix(agent): recover stale runs and improve new-user OA delivery
Fix DEV logout cookie clearing, materialize selected MindSpace OA assets before agent runs, and recover zombie runs from synced workspace pages. Add client run wait timeout, harness retry limits, page-edit asset forwarding, and logout/john2 scenario tests. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+42
-8
@@ -25,6 +25,7 @@ import { createTkmindProxy, sanitizeSessionConversationPublicHtmlLinks } from '.
|
||||
import { createSessionAccess, isSessionBrokerEnabled } from './session-broker.mjs';
|
||||
import { isSessionBrokerMetricsEnabled } from './session-broker-metrics.mjs';
|
||||
import {
|
||||
clearUserLogoutCookies,
|
||||
clearUserSessionCookie,
|
||||
createUserAuth,
|
||||
resolveCookieDomainForRequest,
|
||||
@@ -195,6 +196,7 @@ import { createPageDataService } from './page-data-service.mjs';
|
||||
import { createPageDataPublicService, isPageDataPublicPath } from './page-data-public-service.mjs';
|
||||
import { syncPageDataPolicyAccessMode } from './page-data-publish-sync.mjs';
|
||||
import { upsertPageDataPolicyIndex } from './page-data-policy-index.mjs';
|
||||
import { attachShenmeiOpinionFormRoutes } from './shenmei-opinion-form-routes.mjs';
|
||||
import { isNativeH5ApiPath } from './policies.mjs';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
@@ -424,6 +426,7 @@ async function bootstrapUserAuth() {
|
||||
maxFileBytes: mindSpaceServerRuntime.maxFileBytes,
|
||||
publicPageLimit: mindSpaceServerRuntime.publicPageLimit,
|
||||
resolveUserIdForAgentSession,
|
||||
resolveWorkspaceRoot: (userId) => userAuth.resolveWorkingDir(userId),
|
||||
resolveSessionSnapshot: (sessionId) => sessionSnapshotService?.get?.(sessionId),
|
||||
registerPublicHtmlArtifactsForConversation,
|
||||
syncWorkspaceAssetsEnabled: WORKSPACE_MAINTENANCE_ENABLED,
|
||||
@@ -793,10 +796,13 @@ function setUserLoginCookies(res, req, token) {
|
||||
}
|
||||
|
||||
function clearUserLoginCookies(res, req) {
|
||||
res.set(
|
||||
'Set-Cookie',
|
||||
clearUserSessionCookie(isSecureRequest(req), resolveCookieDomainForRequest(req)),
|
||||
);
|
||||
const secure = isSecureRequest(req);
|
||||
const domain = resolveCookieDomainForRequest(req);
|
||||
const cookies = clearUserLogoutCookies(secure, domain);
|
||||
if (legacyAuth) {
|
||||
cookies.push(clearSessionCookie(secure));
|
||||
}
|
||||
res.set('Set-Cookie', cookies);
|
||||
}
|
||||
|
||||
async function attachUserSession(req, _res, next) {
|
||||
@@ -1245,14 +1251,14 @@ app.get('/auth/me', async (req, res) => {
|
||||
|
||||
app.post('/auth/logout', async (req, res) => {
|
||||
await userAuthReady;
|
||||
const secure = isSecureRequest(req);
|
||||
if (userAuth) {
|
||||
await userAuth.revoke(userToken(req));
|
||||
clearUserLoginCookies(res, req);
|
||||
}
|
||||
if (legacyAuth) {
|
||||
legacyAuth.revoke(legacySessionToken(req));
|
||||
res.set('Set-Cookie', clearSessionCookie(secure));
|
||||
}
|
||||
if (userAuth || legacyAuth) {
|
||||
clearUserLoginCookies(res, req);
|
||||
}
|
||||
res.status(204).end();
|
||||
});
|
||||
@@ -1857,6 +1863,7 @@ app.use('/wiki-api', wikiApi);
|
||||
|
||||
const api = express.Router();
|
||||
api.use(jsonUnlessMultipart);
|
||||
attachShenmeiOpinionFormRoutes(api, { rootDir: __dirname });
|
||||
|
||||
api.use(async (req, res, next) => {
|
||||
await userAuthReady;
|
||||
@@ -1864,6 +1871,7 @@ api.use(async (req, res, next) => {
|
||||
if (req.path.startsWith('/internal/agent/')) return next();
|
||||
if (req.path === '/agent/mindspace_page_patch') return next();
|
||||
if (req.path === '/agent/mindspace_asset_delete') return next();
|
||||
if (req.path === '/agent/mindspace_asset_download') return next();
|
||||
if (req.path === '/config/blocked-words') return next();
|
||||
if (req.method === 'GET' && /^\/mindspace\/v1\/assets\/[^/]+\/download$/.test(req.path)) {
|
||||
return next();
|
||||
@@ -4036,6 +4044,32 @@ api.post('/agent/mindspace_asset_delete', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
api.post('/agent/mindspace_asset_download', async (req, res) => {
|
||||
if (!mindSpaceAssetAgent) {
|
||||
return res.status(503).json({ message: 'MindSpace 未启用' });
|
||||
}
|
||||
try {
|
||||
const result = await mindSpaceAssetAgent.applyAgentDownload(req.body ?? {});
|
||||
await mindSpaceAudit?.write({
|
||||
userId: result.userId ?? null,
|
||||
action: 'asset.download',
|
||||
objectType: 'asset',
|
||||
objectId: result.assetId,
|
||||
ip: req.ip,
|
||||
metadata: { via: 'agent', relativePath: result.relativePath ?? null },
|
||||
});
|
||||
return sendData(res, req, result);
|
||||
} catch (error) {
|
||||
if (error?.code === 'forbidden') {
|
||||
return sendError(res, req, 403, error.code, error.message);
|
||||
}
|
||||
if (error?.code === 'invalid_request' || error?.code === 'asset_not_found') {
|
||||
return sendError(res, req, 400, error.code, error.message);
|
||||
}
|
||||
return mindSpaceError(res, req, error);
|
||||
}
|
||||
});
|
||||
|
||||
api.get('/mindspace/v1/pages/:pageId/thumbnail', async (req, res) => {
|
||||
if (!mindSpacePages) return res.status(503).json({ message: 'MindSpace 未启用' });
|
||||
try {
|
||||
@@ -4684,7 +4718,7 @@ api.post('/agent/runs', async (req, res, next) => {
|
||||
[
|
||||
tkmindProxy.requireUser,
|
||||
tkmindProxy.ensureChatAllowed,
|
||||
createPostAgentRunsHandler({ userAuth, sessionAccess, agentRunGateway }),
|
||||
createPostAgentRunsHandler({ userAuth, sessionAccess, agentRunGateway, mindSpaceAssetAgent }),
|
||||
],
|
||||
req,
|
||||
res,
|
||||
|
||||
Reference in New Issue
Block a user