diff --git a/.changeset/composer-input-message-type.md b/.changeset/composer-input-message-type.md new file mode 100644 index 0000000000..aaa75e12c1 --- /dev/null +++ b/.changeset/composer-input-message-type.md @@ -0,0 +1,5 @@ +--- +"@electric-ax/agents-runtime": patch +--- + +Fix `composer_input` messages reaching the agent as raw JSON. `buildInboxMessages` dropped the `message_type` field when materializing the entity timeline from the db, so `projectInboxPayload` could no longer recognize composer input and fell back to `JSON.stringify(payload)` — the model saw `{"source":"..."}` instead of the plain text the user typed. The field is now carried through. diff --git a/packages/agents-runtime/src/entity-timeline.ts b/packages/agents-runtime/src/entity-timeline.ts index 4f55881a5e..cdc42ff39a 100644 --- a/packages/agents-runtime/src/entity-timeline.ts +++ b/packages/agents-runtime/src/entity-timeline.ts @@ -946,6 +946,7 @@ function buildInboxMessages( order: message.order, from: message.from ?? `unknown`, payload: message.payload, + message_type: message.message_type, timestamp: message.timestamp ?? new Date(0).toISOString(), mode: message.mode ?? `immediate`, status: message.status ?? `processed`, diff --git a/packages/agents-runtime/test/timeline-context.test.ts b/packages/agents-runtime/test/timeline-context.test.ts index 0370ca1b1c..e56a8c874e 100644 --- a/packages/agents-runtime/test/timeline-context.test.ts +++ b/packages/agents-runtime/test/timeline-context.test.ts @@ -524,6 +524,41 @@ describe(`timeline context`, () => { ]) }) + it(`timelineToMessages projects composer_input rows read from the db as source text`, () => { + const db = { + collections: { + runs: { toArray: [] }, + texts: { toArray: [] }, + textDeltas: { toArray: [] }, + toolCalls: { toArray: [] }, + steps: { toArray: [] }, + errors: { toArray: [] }, + inbox: { + toArray: [ + { + key: `msg-1`, + from: `user`, + message_type: `composer_input`, + payload: { source: `summarize the article` }, + timestamp: `2026-03-28T00:00:00.000Z`, + }, + ], + __electricRowOffsets: new Map([[`msg-1`, offset(1)]]), + }, + wakes: { toArray: [], __electricRowOffsets: new Map() }, + signals: { toArray: [], __electricRowOffsets: new Map() }, + contextInserted: { toArray: [], __electricRowOffsets: new Map() }, + contextRemoved: { toArray: [], __electricRowOffsets: new Map() }, + manifests: { toArray: [], __electricRowOffsets: new Map() }, + childStatus: { toArray: [], __electricRowOffsets: new Map() }, + }, + } as unknown as EntityStreamDB + + expect(timelineToMessages(db)).toEqual([ + { role: `user`, content: `summarize the article` }, + ]) + }) + it(`timelineToMessages handles an empty entity timeline`, () => { const db = { collections: {