Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions backend/__tests__/unit/config/nativeAgents.scoutLinearCard.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Scout posts Linear list results as a kind:'ui' card (TASK-573, D98).
*
* The prompt is the only thing that tells Scout the card exists, so these tests
* pin three things: the catalogue tuple it quotes is the live revision file (not
* a copy that can drift), the example source it teaches passes the same
* validator a real write goes through, and the rule is present at all.
*/
const { scoutApp } = require('../../../config/native-agents/scout');
const revision = require('../../../config/uiCardCatalogueRevision.json');
const { validateAgentCardPayload } = require('../../../services/uiCardValidation');

const prompt = scoutApp.systemPrompt;

// The example copied from the prompt, so a changed example fails here too.
const EXAMPLE_SOURCE = 'root = Card("Linear issues", [Table([Col("Issue"), Col("Status")], '
+ '[["LIN-12 Fix login", "In progress"]])])';

describe('Scout Linear list results as a card', () => {
it('teaches the rule with the live catalogue tuple', () => {
expect(prompt).toContain('LINEAR LIST RESULTS');
expect(prompt).toContain(`"catalogId":"${revision.catalogId}"`);
expect(prompt).toContain(`"catalogRev":${revision.catalogRev}`);
expect(prompt).toContain(`"catalogHash":"${revision.catalogHash}"`);
});

it('teaches an example the card validator accepts', () => {
expect(prompt).toContain(EXAMPLE_SOURCE);
const payload = {
...revision, kind: 'ui', v: 1, lang: 'openui@1', source: EXAMPLE_SOURCE,
};
expect(() => validateAgentCardPayload('card', payload, 'LIN-12 Fix login (In progress)')).not.toThrow();
});

it('names the card as the path for list results, with a text fallback', () => {
expect(prompt).toContain('messageType "card"');
expect(prompt).toContain('fallback every reader sees');
});
});
16 changes: 15 additions & 1 deletion backend/config/native-agents/scout.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { NativeAgentDefinition } from './types';
import { composePrompt } from './housePreamble';
import UI_CARD_REVISION from '../uiCardCatalogueRevision.json';

/**
* Scout — the per-user onboarding admin agent (retention plan D4; named
Expand Down Expand Up @@ -294,7 +295,20 @@ export const scoutApp = {
+ '- They want Commonly to run an agent for them, or would rather not '
+ 'install anything → commonly_offer_hosted_agent.\n'
+ '- commonly_read_context before answering questions about what happened '
+ 'here.',
+ 'here.\n'
+ '\n'
+ 'LINEAR LIST RESULTS: when a linear.* list tool (for example linear.list_issues) '
+ 'returns issues, answer with one card, not bullet prose. Call commonly_post_message '
+ 'with messageType "card". Its content is a short plain-text list of the same '
+ 'issues, which is the fallback every reader sees. Its payload is '
+ `{"kind":"ui","v":1,"lang":"openui@1","catalogId":"${UI_CARD_REVISION.catalogId}",`
+ `"catalogRev":${UI_CARD_REVISION.catalogRev},"catalogHash":"${UI_CARD_REVISION.catalogHash}",`
+ '"source":"<OpenUI source>"}. The source uses only the catalogue components '
+ 'Card, Table, Col and Text, in one statement set, for example: '
+ 'root = Card("Linear issues", [Table([Col("Issue"), Col("Status")], '
+ '[["LIN-12 Fix login", "In progress"]])]). Every row needs one cell per Col. '
+ 'Show at most 10 rows and say how many more there are in a Text line. Keep each '
+ 'cell under 80 characters. Copy the catalogue fields exactly.',
'Never claim the user\'s own agent is connected or working — the '
+ 'connect page verifies that, not you. Point them there instead. And '
+ 'remember you are the product\'s first impression: an invented product '
Expand Down
Loading