Skip to content

Commit b9b4e01

Browse files
author
Val
committed
daemon: align run-source validation with the attachments contract (#7040)
attachments is a list of file-path strings; commentAttachments is a list of objects. Accept well-formed non-empty lists of either kind for empty-message turns, and add specs covering the attachments-only case + malformed lists.
1 parent a8b9da5 commit b9b4e01

2 files changed

Lines changed: 35 additions & 3 deletions

File tree

apps/daemon/src/routes/runs.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1246,9 +1246,17 @@ export function registerRunRoutes(app: Express, ctx: RegisterRunRoutesDeps) {
12461246
const hasPrompt =
12471247
(typeof requestBody.message === 'string' && requestBody.message.trim().length > 0)
12481248
|| (typeof requestBody.currentPrompt === 'string' && requestBody.currentPrompt.trim().length > 0);
1249-
const hasAttachments = Array.isArray(requestBody.attachments)
1250-
&& requestBody.attachments.length > 0
1251-
&& requestBody.attachments.every((entry) => entry !== null && typeof entry === 'object');
1249+
// Attachments mirror the seed predicate: attachments is a list of
1250+
// non-empty file-path strings and commentAttachments is a list of
1251+
// objects. Either list, when well-formed and non-empty, legitimizes an
1252+
// empty-message turn (attachments-only sends seed the user message).
1253+
const hasAttachments =
1254+
(Array.isArray(requestBody.attachments)
1255+
&& requestBody.attachments.length > 0
1256+
&& requestBody.attachments.every((entry) => typeof entry === 'string' && entry.trim().length > 0))
1257+
|| (Array.isArray(requestBody.commentAttachments)
1258+
&& requestBody.commentAttachments.length > 0
1259+
&& requestBody.commentAttachments.every((entry) => entry !== null && typeof entry === 'object'));
12521260
const hasPluginBriefSource =
12531261
(typeof requestBody.pluginId === 'string'
12541262
&& requestBody.pluginId.length > 0

apps/daemon/tests/run-create-validation.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ describe('run creation input validation (#7040)', () => {
4343
{ message: {} },
4444
{ message: 42 },
4545
{ attachments: [null, 42] },
46+
{ commentAttachments: [42] },
4647
{ pluginId: 'definitely-not-installed' },
4748
];
4849
for (const body of cases) {
@@ -55,6 +56,29 @@ describe('run creation input validation (#7040)', () => {
5556
}
5657
});
5758

59+
it('accepts attachments-only turns with an empty message', async () => {
60+
const id = 'att-only-' + Date.now();
61+
const create = await fetch(baseUrl + '/api/projects', {
62+
method: 'POST',
63+
headers: { 'Content-Type': 'application/json' },
64+
body: JSON.stringify({ id, name: 'Attachments Only' }),
65+
});
66+
expect(create.status).toBe(200);
67+
const convId = ((await create.json()) as { conversationId: string }).conversationId;
68+
const resp = await fetch(baseUrl + '/api/runs', {
69+
method: 'POST',
70+
headers: { 'Content-Type': 'application/json' },
71+
body: JSON.stringify({
72+
agentId: 'missing-agent-' + Date.now(),
73+
projectId: id,
74+
conversationId: convId,
75+
message: '',
76+
attachments: ['assets/no-prompt.png'],
77+
}),
78+
});
79+
expect(resp.status).toBe(202);
80+
});
81+
5882
it('still accepts a normal prompt', async () => {
5983
const resp = await fetch(baseUrl + '/api/runs', {
6084
method: 'POST',

0 commit comments

Comments
 (0)