snapshot of https://github.com/toeverything/AFFiNE.git (canary, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Build & Test / Analyze (javascript, affine) (push) Failing after 4s
Build & Test / Analyze (typescript, affine) (push) Failing after 3s
Build & Test / Rust test filter (push) Failing after 4s
Build & Test / Check yarn binary (push) Failing after 3s
Build & Test / E2E BlockSuite Test (3) (push) Failing after 3s
Build & Test / Analyze (javascript, blocksuite) (push) Failing after 3s
Build & Test / Analyze (typescript, blocksuite) (push) Failing after 3s
Build & Test / Mobile native build filter (push) Failing after 3s
Build & Test / E2E BlockSuite Test (1) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (2) (push) Failing after 4s
Build & Test / E2E Mobile Test (1) (push) Failing after 3s
Build & Test / Unit Test (1) (push) Has been skipped
Build & Test / Unit Test (2) (push) Has been skipped
Build & Test / Unit Test (3) (push) Has been skipped
Build & Test / Unit Test (4) (push) Has been skipped
Build & Test / Unit Test (5) (push) Has been skipped
Build & Test / Native Unit Test (push) Has been skipped
Build & Test / E2E BlockSuite Test (4) (push) Failing after 3s
Build & Test / E2E BlockSuite Test (5) (push) Failing after 3s
Build & Test / E2E BlockSuite Cross Browser Test (push) Failing after 3s
Build & Test / E2E Mobile Test (2) (push) Failing after 3s
Build & Test / Build AFFiNE native (x86_64-unknown-linux-gnu) (push) Failing after 3s
Build & Test / Build Server native (push) Failing after 3s
Build & Test / Check Git Status (push) Has been skipped
Build & Test / Server Test (0, 4) (push) Has been skipped
Build & Test / Server Test (1, 4) (push) Has been skipped
Build & Test / Server Test (2, 4) (push) Has been skipped
Build & Test / Server Test (3, 4) (push) Has been skipped
Build & Test / Server Test with Elasticsearch (push) Has been skipped
Build & Test / Build @affine/electron renderer (push) Failing after 3s
Build & Test / Copilot test filter (push) Failing after 4s
Build & Test / Lint Rust (push) Has been skipped
Build & Test / Server Test with Embedded Search (push) Has been skipped
Build & Test / Server Test with Manticore Search (push) Has been skipped
Build & Test / Server E2E Test (push) Has been skipped
Build & Test / Cloud Desktop E2E Test (push) Has been skipped
Build & Test / Cloud E2E Test 1/2 (push) Has been skipped
Build & Test / Cloud E2E Test 2/2 (push) Has been skipped
Build & Test / Build Android app (push) Has been skipped
Build & Test / Test Android app (push) Has been skipped
Build & Test / Run native tests (push) Has been skipped
Build & Test / Server Copilot Api Test (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (1, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (2, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (3, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (4, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (5, 5) (push) Has been skipped
Release / Canary Gate (push) Has been skipped
Build & Test / Build AFFiNE native (aarch64-apple-darwin) (push) Has been cancelled
Build & Test / Lint (push) Has been cancelled
Build & Test / Typecheck (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-apple-darwin) (push) Has been cancelled
Build & Test / Desktop Test (ubuntu-latest, linux, x64, x86_64-unknown-linux-gnu, true) (push) Has been cancelled
Build & Test / Desktop Test (windows-latest, windows, x64, x86_64-pc-windows-msvc, true) (push) Has been cancelled
Build & Test / E2E Test (1) (push) Has been cancelled
Build & Test / E2E Test (2) (push) Has been cancelled
Build & Test / E2E Test (3) (push) Has been cancelled
Build & Test / E2E Test (4) (push) Has been cancelled
Build & Test / E2E Test (5) (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, arm64, aarch64-apple-darwin, true) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, x64, x86_64-apple-darwin, false) (push) Has been cancelled
Build & Test / 3, 2, 1 Launch (push) Has been cancelled
Build & Test / Build iOS app (push) Has been cancelled
Copilot Test Automatically / Setup Test (push) Failing after 1s
Release / Prepare (push) Failing after 2s
Release / Release Cloud (push) Has been skipped
Release / Release Desktop (push) Has been skipped
Release / Release Docker Image (push) Has been skipped
Release / Release Mobile (push) Has been skipped
Some checks failed
Build & Test / Analyze (javascript, affine) (push) Failing after 4s
Build & Test / Analyze (typescript, affine) (push) Failing after 3s
Build & Test / Rust test filter (push) Failing after 4s
Build & Test / Check yarn binary (push) Failing after 3s
Build & Test / E2E BlockSuite Test (3) (push) Failing after 3s
Build & Test / Analyze (javascript, blocksuite) (push) Failing after 3s
Build & Test / Analyze (typescript, blocksuite) (push) Failing after 3s
Build & Test / Mobile native build filter (push) Failing after 3s
Build & Test / E2E BlockSuite Test (1) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (2) (push) Failing after 4s
Build & Test / E2E Mobile Test (1) (push) Failing after 3s
Build & Test / Unit Test (1) (push) Has been skipped
Build & Test / Unit Test (2) (push) Has been skipped
Build & Test / Unit Test (3) (push) Has been skipped
Build & Test / Unit Test (4) (push) Has been skipped
Build & Test / Unit Test (5) (push) Has been skipped
Build & Test / Native Unit Test (push) Has been skipped
Build & Test / E2E BlockSuite Test (4) (push) Failing after 3s
Build & Test / E2E BlockSuite Test (5) (push) Failing after 3s
Build & Test / E2E BlockSuite Cross Browser Test (push) Failing after 3s
Build & Test / E2E Mobile Test (2) (push) Failing after 3s
Build & Test / Build AFFiNE native (x86_64-unknown-linux-gnu) (push) Failing after 3s
Build & Test / Build Server native (push) Failing after 3s
Build & Test / Check Git Status (push) Has been skipped
Build & Test / Server Test (0, 4) (push) Has been skipped
Build & Test / Server Test (1, 4) (push) Has been skipped
Build & Test / Server Test (2, 4) (push) Has been skipped
Build & Test / Server Test (3, 4) (push) Has been skipped
Build & Test / Server Test with Elasticsearch (push) Has been skipped
Build & Test / Build @affine/electron renderer (push) Failing after 3s
Build & Test / Copilot test filter (push) Failing after 4s
Build & Test / Lint Rust (push) Has been skipped
Build & Test / Server Test with Embedded Search (push) Has been skipped
Build & Test / Server Test with Manticore Search (push) Has been skipped
Build & Test / Server E2E Test (push) Has been skipped
Build & Test / Cloud Desktop E2E Test (push) Has been skipped
Build & Test / Cloud E2E Test 1/2 (push) Has been skipped
Build & Test / Cloud E2E Test 2/2 (push) Has been skipped
Build & Test / Build Android app (push) Has been skipped
Build & Test / Test Android app (push) Has been skipped
Build & Test / Run native tests (push) Has been skipped
Build & Test / Server Copilot Api Test (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (1, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (2, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (3, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (4, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (5, 5) (push) Has been skipped
Release / Canary Gate (push) Has been skipped
Build & Test / Build AFFiNE native (aarch64-apple-darwin) (push) Has been cancelled
Build & Test / Lint (push) Has been cancelled
Build & Test / Typecheck (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-apple-darwin) (push) Has been cancelled
Build & Test / Desktop Test (ubuntu-latest, linux, x64, x86_64-unknown-linux-gnu, true) (push) Has been cancelled
Build & Test / Desktop Test (windows-latest, windows, x64, x86_64-pc-windows-msvc, true) (push) Has been cancelled
Build & Test / E2E Test (1) (push) Has been cancelled
Build & Test / E2E Test (2) (push) Has been cancelled
Build & Test / E2E Test (3) (push) Has been cancelled
Build & Test / E2E Test (4) (push) Has been cancelled
Build & Test / E2E Test (5) (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, arm64, aarch64-apple-darwin, true) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, x64, x86_64-apple-darwin, false) (push) Has been cancelled
Build & Test / 3, 2, 1 Launch (push) Has been cancelled
Build & Test / Build iOS app (push) Has been cancelled
Copilot Test Automatically / Setup Test (push) Failing after 1s
Release / Prepare (push) Failing after 2s
Release / Release Cloud (push) Has been skipped
Release / Release Desktop (push) Has been skipped
Release / Release Docker Image (push) Has been skipped
Release / Release Mobile (push) Has been skipped
This commit is contained in:
@@ -0,0 +1,424 @@
|
||||
import '../../plugins/copilot/runtime/capability-runtime';
|
||||
|
||||
import ava from 'ava';
|
||||
|
||||
import {
|
||||
CopilotMessageNotFound,
|
||||
CopilotSessionNotFound,
|
||||
type Mutex,
|
||||
} from '../../base';
|
||||
import type { CompatSubmissionStore } from '../../plugins/copilot/compat/submission-store';
|
||||
import type { ConversationPolicy } from '../../plugins/copilot/conversation/policy';
|
||||
import type { Turn } from '../../plugins/copilot/core';
|
||||
import type { AdmittedAttachmentSource } from '../../plugins/copilot/runtime/hosts/attachment-admission';
|
||||
import { ConversationHost } from '../../plugins/copilot/runtime/hosts/conversation-host';
|
||||
import {
|
||||
ChatSession,
|
||||
type ChatSessionService,
|
||||
} from '../../plugins/copilot/session';
|
||||
|
||||
const test = ava;
|
||||
|
||||
function fixture(
|
||||
options: {
|
||||
failFirstAcceptedWrite?: boolean;
|
||||
failFirstAppend?: boolean;
|
||||
mode?: 'canonical' | 'personal';
|
||||
admittedAttachments?: AdmittedAttachmentSource[];
|
||||
revokeDuringAdmission?: boolean;
|
||||
promptAction?: string;
|
||||
} = {}
|
||||
) {
|
||||
const sessionId = 'session-1';
|
||||
const token = 'submission-1';
|
||||
const durable = new Map<string, Turn>();
|
||||
const accepted = new Map<
|
||||
string,
|
||||
{ userId: string; sessionId: string; turnId: string }
|
||||
>();
|
||||
const submissions = new Map([
|
||||
[
|
||||
token,
|
||||
{
|
||||
id: token,
|
||||
userId: 'user-1',
|
||||
workspaceId: 'workspace-1',
|
||||
sessionId,
|
||||
content: 'hello',
|
||||
attachments: options.admittedAttachments?.length
|
||||
? ['data:text/plain;base64,aGVsbG8=']
|
||||
: [],
|
||||
params: {
|
||||
tone: 'brief',
|
||||
scopeSelectors: [{ kind: 'document', id: 'doc-2' }],
|
||||
},
|
||||
createdAt: new Date('2026-01-01T00:00:00.000Z'),
|
||||
},
|
||||
],
|
||||
]);
|
||||
let appendCount = 0;
|
||||
let quota = true;
|
||||
let accessAllowed = true;
|
||||
let acceptedWriteCount = 0;
|
||||
let artifactWrites = 0;
|
||||
const titleCalls: unknown[] = [];
|
||||
const chatSession = new ChatSession(
|
||||
{
|
||||
sessionId,
|
||||
userId: 'user-1',
|
||||
workspaceId: 'workspace-1',
|
||||
docId: 'doc-1',
|
||||
focus: { selectors: [] },
|
||||
prompt: {
|
||||
name: 'Chat With AFFiNE AI',
|
||||
action: options.promptAction,
|
||||
config: {},
|
||||
paramKeys: [],
|
||||
params: {},
|
||||
},
|
||||
turns: [],
|
||||
},
|
||||
() => []
|
||||
);
|
||||
const sessions = {
|
||||
getOwnedScope: async (id: string, userId: string) =>
|
||||
id === sessionId && userId === 'user-1'
|
||||
? { workspaceId: 'workspace-1', docId: 'doc-1' }
|
||||
: undefined,
|
||||
getInScope: async (input: { sessionId: string; userId: string }) =>
|
||||
input.sessionId === sessionId && input.userId === 'user-1'
|
||||
? chatSession
|
||||
: undefined,
|
||||
findTurnByCompatSubmissionId: async (
|
||||
_sessionId: string,
|
||||
_userId: string,
|
||||
_workspaceId: string,
|
||||
id: string
|
||||
) => durable.get(id),
|
||||
appendTurn: async (input: { compatSubmissionId: string; turn: Turn }) => {
|
||||
appendCount += 1;
|
||||
if (options.failFirstAppend && appendCount === 1) {
|
||||
throw new Error('durable append failed');
|
||||
}
|
||||
const stored = { ...input.turn, id: `turn-${appendCount}` };
|
||||
durable.set(input.compatSubmissionId, stored);
|
||||
return stored;
|
||||
},
|
||||
getMessage: async (
|
||||
_sessionId: string,
|
||||
_userId: string,
|
||||
_workspaceId: string,
|
||||
turnId: string
|
||||
) => [...durable.values()].find(turn => turn.id === turnId),
|
||||
revertLatestMessage: async () => {},
|
||||
generateSessionTitle: async (input: unknown) => {
|
||||
titleCalls.push(input);
|
||||
},
|
||||
} as unknown as ChatSessionService;
|
||||
const submissionStore = {
|
||||
get: async (id: string, userId: string) => {
|
||||
const value = submissions.get(id);
|
||||
return value?.userId === userId ? value : undefined;
|
||||
},
|
||||
getAccepted: async (id: string, userId: string) => {
|
||||
const value = accepted.get(id);
|
||||
return value?.userId === userId
|
||||
? { ...value, acceptedAt: new Date('2026-01-01T00:00:00.000Z') }
|
||||
: undefined;
|
||||
},
|
||||
markAccepted: async (
|
||||
id: string,
|
||||
userId: string,
|
||||
value: { sessionId: string; turnId: string }
|
||||
) => {
|
||||
acceptedWriteCount += 1;
|
||||
if (options.failFirstAcceptedWrite && acceptedWriteCount === 1) {
|
||||
throw new Error('accepted cache write failed');
|
||||
}
|
||||
accepted.set(id, { ...value, userId });
|
||||
submissions.delete(id);
|
||||
},
|
||||
} as unknown as CompatSubmissionStore;
|
||||
const mutex = {
|
||||
acquire: async () => ({ async [Symbol.asyncDispose]() {} }),
|
||||
} as unknown as Mutex;
|
||||
const policy = {
|
||||
hasQuota: async () => quota,
|
||||
shouldScheduleTitle: (prompt: { action?: string }) => !prompt.action,
|
||||
} as unknown as ConversationPolicy;
|
||||
const runtime = {
|
||||
putWorkspaceArtifact: async () => {
|
||||
artifactWrites++;
|
||||
return {
|
||||
id: 'artifact-1',
|
||||
canonicalMediaType: 'text/plain',
|
||||
};
|
||||
},
|
||||
compileTurnScope: async (input: {
|
||||
selectors: unknown[];
|
||||
preferredSourceIds?: string[];
|
||||
}) => ({
|
||||
version: 1,
|
||||
resolvedAt: '2026-01-01T00:00:00.000Z',
|
||||
selectors: input.selectors,
|
||||
requiredDocIds: [],
|
||||
requiredArtifactIds: [],
|
||||
preferredSourceIds: input.preferredSourceIds ?? [],
|
||||
retrieval: {
|
||||
mode: input.selectors.length ? 'required' : 'workspace',
|
||||
requiredDocIds: [],
|
||||
requiredArtifactIds: [],
|
||||
preferredSourceIds: input.preferredSourceIds ?? [],
|
||||
},
|
||||
}),
|
||||
};
|
||||
const attachmentAdmission = {
|
||||
admitPromptAttachments: async () => {
|
||||
if (options.revokeDuringAdmission) accessAllowed = false;
|
||||
return options.admittedAttachments ?? [];
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
host: new ConversationHost(
|
||||
sessions,
|
||||
submissionStore,
|
||||
mutex,
|
||||
policy,
|
||||
runtime as never,
|
||||
attachmentAdmission as never,
|
||||
{
|
||||
sessionResource: async () => {
|
||||
if (!accessAllowed) throw new Error('permission denied');
|
||||
return options.mode ?? 'canonical';
|
||||
},
|
||||
} as never
|
||||
),
|
||||
sessionId,
|
||||
token,
|
||||
durable,
|
||||
accepted,
|
||||
submissions,
|
||||
session: chatSession,
|
||||
appendCount: () => appendCount,
|
||||
artifactWrites: () => artifactWrites,
|
||||
titleCalls,
|
||||
setQuota: (value: boolean) => {
|
||||
quota = value;
|
||||
},
|
||||
revokeAccess: () => {
|
||||
accessAllowed = false;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
test('compat submission becomes one durable user turn and replays idempotently', async t => {
|
||||
const state = fixture();
|
||||
|
||||
const first = await state.host.prepareTurn('user-1', state.sessionId, {
|
||||
messageId: state.token,
|
||||
});
|
||||
t.is(first.latestTurn?.content, 'hello');
|
||||
t.deepEqual(first.latestTurn?.metadata, { tone: 'brief' });
|
||||
t.deepEqual(first.latestTurn?.scopeSnapshot?.selectors, [
|
||||
{ kind: 'document', id: 'doc-2', source: 'draft' },
|
||||
]);
|
||||
t.is(state.appendCount(), 1);
|
||||
t.false(state.submissions.has(state.token));
|
||||
t.truthy(state.accepted.get(state.token));
|
||||
|
||||
state.setQuota(false);
|
||||
const replay = await state.host.prepareTurn('user-1', state.sessionId, {
|
||||
messageId: state.token,
|
||||
});
|
||||
t.is(replay.latestTurn?.id, first.latestTurn?.id);
|
||||
t.true(replay.quotaBackedRoutesAllowed);
|
||||
t.is(state.appendCount(), 1);
|
||||
});
|
||||
|
||||
test('durable compat turn recovers after accepted-cache write failure', async t => {
|
||||
const state = fixture({ failFirstAcceptedWrite: true });
|
||||
|
||||
await t.throwsAsync(
|
||||
state.host.prepareTurn('user-1', state.sessionId, {
|
||||
messageId: state.token,
|
||||
}),
|
||||
{ message: 'accepted cache write failed' }
|
||||
);
|
||||
t.is(state.appendCount(), 1);
|
||||
t.truthy(state.durable.get(state.token));
|
||||
|
||||
const recovered = await state.host.prepareTurn('user-1', state.sessionId, {
|
||||
messageId: state.token,
|
||||
});
|
||||
t.is(recovered.latestTurn?.id, state.durable.get(state.token)?.id);
|
||||
t.is(state.appendCount(), 1);
|
||||
t.truthy(state.accepted.get(state.token));
|
||||
});
|
||||
|
||||
test('compat submission remains retryable when durable append fails', async t => {
|
||||
const state = fixture({ failFirstAppend: true });
|
||||
|
||||
await t.throwsAsync(
|
||||
state.host.prepareTurn('user-1', state.sessionId, {
|
||||
messageId: state.token,
|
||||
}),
|
||||
{ message: 'durable append failed' }
|
||||
);
|
||||
t.true(state.submissions.has(state.token));
|
||||
t.false(state.accepted.has(state.token));
|
||||
|
||||
const recovered = await state.host.prepareTurn('user-1', state.sessionId, {
|
||||
messageId: state.token,
|
||||
});
|
||||
t.is(recovered.latestTurn?.content, 'hello');
|
||||
t.is(state.durable.size, 1);
|
||||
});
|
||||
|
||||
test('compat submission cannot be consumed by another session', async t => {
|
||||
const state = fixture();
|
||||
const other = fixture();
|
||||
other.submissions.set(state.token, {
|
||||
id: state.token,
|
||||
userId: 'user-1',
|
||||
workspaceId: 'workspace-1',
|
||||
sessionId: 'session-other',
|
||||
content: 'secret',
|
||||
attachments: [],
|
||||
params: { tone: 'brief', scopeSelectors: [] },
|
||||
createdAt: new Date(),
|
||||
});
|
||||
|
||||
await t.throwsAsync(
|
||||
other.host.prepareTurn('user-1', other.sessionId, {
|
||||
messageId: state.token,
|
||||
}),
|
||||
{ instanceOf: CopilotMessageNotFound }
|
||||
);
|
||||
await t.throwsAsync(
|
||||
state.host.prepareTurn('user-2', state.sessionId, {
|
||||
messageId: state.token,
|
||||
}),
|
||||
{ instanceOf: CopilotSessionNotFound }
|
||||
);
|
||||
t.is(other.appendCount(), 0);
|
||||
});
|
||||
|
||||
test('direct conversation rejects revoked canonical access before durable append', async t => {
|
||||
const state = fixture();
|
||||
state.revokeAccess();
|
||||
|
||||
await t.throwsAsync(
|
||||
state.host.prepareTurn('user-1', state.sessionId, {
|
||||
messageId: state.token,
|
||||
}),
|
||||
{ message: 'permission denied' }
|
||||
);
|
||||
t.is(state.appendCount(), 0);
|
||||
t.true(state.submissions.has(state.token));
|
||||
});
|
||||
|
||||
test('workspace context fails closed for personal scope and revoked canonical access', async t => {
|
||||
const personal = fixture({ mode: 'personal' });
|
||||
const personalError = await t.throwsAsync(
|
||||
personal.host.prepareTurn('user-1', personal.sessionId, {
|
||||
messageId: personal.token,
|
||||
})
|
||||
);
|
||||
|
||||
const canonical = fixture({
|
||||
admittedAttachments: [
|
||||
{
|
||||
id: 'attachment-1',
|
||||
kind: 'bytes',
|
||||
mimeType: 'text/plain',
|
||||
size: 5,
|
||||
fileName: 'note.txt',
|
||||
hash: 'hash-1',
|
||||
data: 'aGVsbG8=',
|
||||
encoding: 'base64',
|
||||
},
|
||||
],
|
||||
revokeDuringAdmission: true,
|
||||
});
|
||||
const canonicalError = await t.throwsAsync(
|
||||
canonical.host.prepareTurn('user-1', canonical.sessionId, {
|
||||
messageId: canonical.token,
|
||||
})
|
||||
);
|
||||
|
||||
t.snapshot({
|
||||
personal: {
|
||||
error: personalError?.message,
|
||||
artifactWrites: personal.artifactWrites(),
|
||||
durableAppends: personal.appendCount(),
|
||||
},
|
||||
canonical: {
|
||||
error: canonicalError?.message,
|
||||
artifactWrites: canonical.artifactWrites(),
|
||||
durableAppends: canonical.appendCount(),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('assistant persistence rechecks access and schedules eligible titles', async t => {
|
||||
const state = fixture();
|
||||
state.revokeAccess();
|
||||
|
||||
await t.throwsAsync(
|
||||
state.host.persistAssistantTurn(
|
||||
state.session,
|
||||
{
|
||||
conversationId: state.session.config.sessionId,
|
||||
role: 'assistant',
|
||||
content: 'must not persist',
|
||||
attachments: [],
|
||||
metadata: {},
|
||||
renderTrace: [],
|
||||
toolEvents: [],
|
||||
createdAt: new Date(),
|
||||
},
|
||||
false
|
||||
),
|
||||
{ message: 'permission denied' }
|
||||
);
|
||||
t.is(state.appendCount(), 0);
|
||||
|
||||
const eligible = fixture();
|
||||
const assistantTurn = {
|
||||
conversationId: eligible.sessionId,
|
||||
role: 'assistant' as const,
|
||||
content: 'answer',
|
||||
attachments: [],
|
||||
metadata: {},
|
||||
renderTrace: [],
|
||||
toolEvents: [],
|
||||
createdAt: new Date(),
|
||||
};
|
||||
await eligible.host.persistAssistantTurn(
|
||||
eligible.session,
|
||||
assistantTurn,
|
||||
false
|
||||
);
|
||||
await eligible.host.persistAssistantTurn(
|
||||
eligible.session,
|
||||
{ ...assistantTurn, content: 'aborted' },
|
||||
true
|
||||
);
|
||||
|
||||
const action = fixture({ promptAction: 'edit' });
|
||||
await action.host.persistAssistantTurn(
|
||||
action.session,
|
||||
{ ...assistantTurn, conversationId: action.sessionId },
|
||||
false
|
||||
);
|
||||
|
||||
t.deepEqual(eligible.titleCalls, [
|
||||
{
|
||||
sessionId: 'session-1',
|
||||
userId: 'user-1',
|
||||
workspaceId: 'workspace-1',
|
||||
},
|
||||
]);
|
||||
t.deepEqual(action.titleCalls, []);
|
||||
});
|
||||
Reference in New Issue
Block a user