u
This commit is contained in:
8
API.md
8
API.md
@@ -298,11 +298,11 @@ The store is exposed as a `LibDashStore` instance (see `src/index.js`). Each met
|
|||||||
- `listByCategoryId(googleAuthId: number, categoryId: number, tx?: object): Promise<Array<{id: number, categoryId: number, propertyId: number, name: string, exclusive: boolean, value: string, sort: number}>>`
|
- `listByCategoryId(googleAuthId: number, categoryId: number, tx?: object): Promise<Array<{id: number, categoryId: number, propertyId: number, name: string, exclusive: boolean, value: string, sort: number}>>`
|
||||||
- `setByCategoryId(googleAuthId: number, categoryId: number, properties: Array<{propertyId: number, value: string, sort?: number}>, tx?: object): Promise<Array<{id: number, categoryId: number, propertyId: number, name: string, exclusive: boolean, value: string, sort: number}>>`
|
- `setByCategoryId(googleAuthId: number, categoryId: number, properties: Array<{propertyId: number, value: string, sort?: number}>, tx?: object): Promise<Array<{id: number, categoryId: number, propertyId: number, name: string, exclusive: boolean, value: string, sort: number}>>`
|
||||||
- [modelconversation](API.modelconversation.md)
|
- [modelconversation](API.modelconversation.md)
|
||||||
- `create(googleAuthId: number, input: {provider: string, model: string}, tx?: object): Promise<{id: number, companyuser_id: number, provider: string, model: string, created_at: string, updated_at: string, state: string}>`
|
- `create(googleAuthId: number, input: {provider: string, model: string, started_by?: number|null}, tx?: object): Promise<{id: number, companyuser_id: number, provider: string, model: string, created_at: string, updated_at: string, state: string, started_by: number|null}>`
|
||||||
- `delete(googleAuthId: number, conversationId: number, tx?: object): Promise<void>`
|
- `delete(googleAuthId: number, conversationId: number, tx?: object): Promise<void>`
|
||||||
- `get(googleAuthId: number, conversationId: number, tx?: object): Promise<{id: number, companyuser_id: number, provider: string, model: string, created_at: string, updated_at: string, state: string}>`
|
- `get(googleAuthId: number, conversationId: number, tx?: object): Promise<{id: number, companyuser_id: number, provider: string, model: string, created_at: string, updated_at: string, state: string, started_by: number|null}>`
|
||||||
- `list(googleAuthId: number, tx?: object): Promise<Array<{id: number, companyuser_id: number, provider: string, model: string, created_at: string, updated_at: string, state: string}>>`
|
- `list(googleAuthId: number, tx?: object): Promise<Array<{id: number, companyuser_id: number, provider: string, model: string, created_at: string, updated_at: string, state: string, started_by: number|null, first_user_message: string|null}>>`
|
||||||
- `setState(googleAuthId: number, conversationId: number, state: string, tx?: object): Promise<{id: number, companyuser_id: number, provider: string, model: string, created_at: string, updated_at: string, state: string}>`
|
- `setState(googleAuthId: number, conversationId: number, state: string, tx?: object): Promise<{id: number, companyuser_id: number, provider: string, model: string, created_at: string, updated_at: string, state: string, started_by: number|null}>`
|
||||||
- [modelmessage](API.modelmessage.md)
|
- [modelmessage](API.modelmessage.md)
|
||||||
- `add(googleAuthId: number, conversationId: number, input: {message: string, role?: 'user'|'assistant'|'system'|'tool'|'reasoning'|'usage'}, tx?: object): Promise<{id: number, conversation_id: number, created_at: string, role: string, message: string}>`
|
- `add(googleAuthId: number, conversationId: number, input: {message: string, role?: 'user'|'assistant'|'system'|'tool'|'reasoning'|'usage'}, tx?: object): Promise<{id: number, conversation_id: number, created_at: string, role: string, message: string}>`
|
||||||
- `list(googleAuthId: number, conversationId: number, tx?: object): Promise<Array<{id: number, conversation_id: number, created_at: string, role: string, message: string}>>`
|
- `list(googleAuthId: number, conversationId: number, tx?: object): Promise<Array<{id: number, conversation_id: number, created_at: string, role: string, message: string}>>`
|
||||||
|
|||||||
@@ -16,15 +16,16 @@ Creates a new (idle) model conversation for the user in the selected company.
|
|||||||
| `input` | `object` | Conversation input. |
|
| `input` | `object` | Conversation input. |
|
||||||
| `input.provider` | `string` | LLM provider name. |
|
| `input.provider` | `string` | LLM provider name. |
|
||||||
| `input.model` | `string` | Model identifier. |
|
| `input.model` | `string` | Model identifier. |
|
||||||
|
| `[input.started_by]` | `number|null` | Id of the conversation that starts this one as a subagent (unenforced reference; omit/null for a top-level conversation). |
|
||||||
| `[tx]` | `object` | Optional transaction to run within. |
|
| `[tx]` | `object` | Optional transaction to run within. |
|
||||||
|
|
||||||
**Returns**
|
**Returns**
|
||||||
|
|
||||||
`Promise<{id: number, companyuser_id: number, provider: string, model: string, created_at: string, updated_at: string, state: string}>`
|
`Promise<{id: number, companyuser_id: number, provider: string, model: string, created_at: string, updated_at: string, state: string, started_by: number|null}>`
|
||||||
|
|
||||||
**Throws**
|
**Throws**
|
||||||
|
|
||||||
`Error` — 'Provider required' or 'Model required'.
|
`Error` — 'Provider required', 'Model required' or 'Invalid started_by'.
|
||||||
|
|
||||||
**Example**
|
**Example**
|
||||||
|
|
||||||
@@ -36,7 +37,7 @@ const c = await store.modelconversation.create(42, { provider: 'openrouter', mod
|
|||||||
|
|
||||||
*Source: `src/store/modelconversation/delete.js`*
|
*Source: `src/store/modelconversation/delete.js`*
|
||||||
|
|
||||||
Deletes a model conversation by ID for the user in the selected company.
|
Deletes a model conversation by ID for the user in the selected company, together with every subagent conversation below it (`started_by` chain, recursively) and their messages.
|
||||||
|
|
||||||
**Parameters**
|
**Parameters**
|
||||||
|
|
||||||
@@ -70,7 +71,7 @@ Gets a single model conversation by ID for the user in the selected company.
|
|||||||
|
|
||||||
**Returns**
|
**Returns**
|
||||||
|
|
||||||
`Promise<{id: number, companyuser_id: number, provider: string, model: string, created_at: string, updated_at: string, state: string}>`
|
`Promise<{id: number, companyuser_id: number, provider: string, model: string, created_at: string, updated_at: string, state: string, started_by: number|null}>`
|
||||||
|
|
||||||
**Throws**
|
**Throws**
|
||||||
|
|
||||||
@@ -91,7 +92,7 @@ Lists the user's own model conversations in the selected company, newest activit
|
|||||||
|
|
||||||
**Returns**
|
**Returns**
|
||||||
|
|
||||||
`Promise<Array<{id: number, companyuser_id: number, provider: string, model: string, created_at: string, updated_at: string, state: string}>>`
|
`Promise<Array<{id: number, companyuser_id: number, provider: string, model: string, created_at: string, updated_at: string, state: string, started_by: number|null, first_user_message: string|null}>>` — `started_by` is the originating conversation id for subagent conversations (null otherwise), so clients can build a tree. `first_user_message` is the stored `message` payload of the conversation's first `user` message (null if none yet).
|
||||||
|
|
||||||
**Example**
|
**Example**
|
||||||
|
|
||||||
@@ -116,7 +117,7 @@ Sets the state of a model conversation ('idle' | 'busy').
|
|||||||
|
|
||||||
**Returns**
|
**Returns**
|
||||||
|
|
||||||
`Promise<{id: number, companyuser_id: number, provider: string, model: string, created_at: string, updated_at: string, state: string}>`
|
`Promise<{id: number, companyuser_id: number, provider: string, model: string, created_at: string, updated_at: string, state: string, started_by: number|null}>`
|
||||||
|
|
||||||
**Throws**
|
**Throws**
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ async function run() {
|
|||||||
|
|
||||||
const list = await store.modelconversation.list(userA);
|
const list = await store.modelconversation.list(userA);
|
||||||
assert.equal(list.length, 1);
|
assert.equal(list.length, 1);
|
||||||
|
assert.equal(list[0].first_user_message, null);
|
||||||
|
|
||||||
assert.deepEqual(await store.modelmessage.list(userA, conv.id), []);
|
assert.deepEqual(await store.modelmessage.list(userA, conv.id), []);
|
||||||
|
|
||||||
@@ -65,6 +66,7 @@ async function run() {
|
|||||||
const msgs = await store.modelmessage.list(userA, conv.id);
|
const msgs = await store.modelmessage.list(userA, conv.id);
|
||||||
assert.deepEqual(msgs.map((m) => m.message), ['hello', 'echo: hello', 'second']);
|
assert.deepEqual(msgs.map((m) => m.message), ['hello', 'echo: hello', 'second']);
|
||||||
assert.deepEqual(msgs.map((m) => m.role), ['user', 'assistant', 'user']);
|
assert.deepEqual(msgs.map((m) => m.role), ['user', 'assistant', 'user']);
|
||||||
|
assert.equal((await store.modelconversation.list(userA)).find((c) => c.id === conv.id).first_user_message, 'hello');
|
||||||
|
|
||||||
// Other user cannot see or write
|
// Other user cannot see or write
|
||||||
const userB = await store.google_auth.add('mc-b@example.com');
|
const userB = await store.google_auth.add('mc-b@example.com');
|
||||||
@@ -74,6 +76,33 @@ async function run() {
|
|||||||
await assert.rejects(store.modelmessage.list(userB, conv.id), /Conversation not found/);
|
await assert.rejects(store.modelmessage.list(userB, conv.id), /Conversation not found/);
|
||||||
await assert.rejects(store.modelmessage.add(userB, conv.id, { message: 'x' }), /Conversation not found/);
|
await assert.rejects(store.modelmessage.add(userB, conv.id, { message: 'x' }), /Conversation not found/);
|
||||||
|
|
||||||
|
// started_by: top-level is null, subagents link to their originator, list exposes it
|
||||||
|
assert.equal(conv.started_by, null);
|
||||||
|
const sub1 = await store.modelconversation.create(userA, { provider: 'openrouter', model: 'm', started_by: conv.id });
|
||||||
|
assert.equal(sub1.started_by, conv.id);
|
||||||
|
const sub2 = await store.modelconversation.create(userA, { provider: 'openrouter', model: 'm', started_by: sub1.id });
|
||||||
|
const sibling = await store.modelconversation.create(userA, { provider: 'openrouter', model: 'm' });
|
||||||
|
await assert.rejects(
|
||||||
|
store.modelconversation.create(userA, { provider: 'openrouter', model: 'm', started_by: 0 }),
|
||||||
|
/Invalid started_by/,
|
||||||
|
);
|
||||||
|
const listed = await store.modelconversation.list(userA);
|
||||||
|
assert.equal(listed.find((c) => c.id === sub2.id).started_by, sub1.id);
|
||||||
|
assert.equal((await store.modelconversation.get(userA, sub1.id)).started_by, conv.id);
|
||||||
|
await store.modelmessage.add(userA, sub2.id, { message: 'deep' });
|
||||||
|
|
||||||
|
// Deleting a child removes only its own chain; deleting the root removes the whole chain
|
||||||
|
await store.modelconversation.delete(userA, sub2.id);
|
||||||
|
await assert.rejects(store.modelconversation.get(userA, sub2.id), /Conversation not found/);
|
||||||
|
await store.modelconversation.get(userA, sub1.id);
|
||||||
|
const sub3 = await store.modelconversation.create(userA, { provider: 'openrouter', model: 'm', started_by: sub1.id });
|
||||||
|
await store.modelconversation.delete(userA, conv.id);
|
||||||
|
for (const gone of [conv.id, sub1.id, sub3.id]) {
|
||||||
|
await assert.rejects(store.modelconversation.get(userA, gone), /Conversation not found/);
|
||||||
|
}
|
||||||
|
await store.modelconversation.get(userA, sibling.id);
|
||||||
|
await assert.rejects(store.modelconversation.delete(userA, conv.id), /Conversation not found/);
|
||||||
|
|
||||||
console.log('PASS');
|
console.log('PASS');
|
||||||
} finally {
|
} finally {
|
||||||
await provider.close?.();
|
await provider.close?.();
|
||||||
|
|||||||
@@ -5,15 +5,25 @@ export const MODELCONVERSATION_COLUMNS = `
|
|||||||
model,
|
model,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at,
|
updated_at,
|
||||||
state
|
state,
|
||||||
|
started_by
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const LIST_BY_COMPANYUSER = `
|
export const LIST_BY_COMPANYUSER = `
|
||||||
SELECT
|
SELECT
|
||||||
${MODELCONVERSATION_COLUMNS}
|
${MODELCONVERSATION_COLUMNS},
|
||||||
FROM modelconversation
|
(
|
||||||
WHERE companyuser_id = @companyuserId
|
SELECT m.message
|
||||||
ORDER BY updated_at DESC, id DESC
|
FROM modelmessage m
|
||||||
|
WHERE m.id = (
|
||||||
|
SELECT MIN(f.id)
|
||||||
|
FROM modelmessage f
|
||||||
|
WHERE f.conversation_id = c.id AND f.role = 'user'
|
||||||
|
)
|
||||||
|
) AS first_user_message
|
||||||
|
FROM modelconversation c
|
||||||
|
WHERE c.companyuser_id = @companyuserId
|
||||||
|
ORDER BY c.updated_at DESC, c.id DESC
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const GET_BY_COMPANYUSER_AND_ID = `
|
export const GET_BY_COMPANYUSER_AND_ID = `
|
||||||
@@ -27,7 +37,8 @@ export const CREATE = `
|
|||||||
INSERT INTO modelconversation (
|
INSERT INTO modelconversation (
|
||||||
companyuser_id,
|
companyuser_id,
|
||||||
provider,
|
provider,
|
||||||
model
|
model,
|
||||||
|
started_by
|
||||||
)
|
)
|
||||||
OUTPUT
|
OUTPUT
|
||||||
INSERTED.id,
|
INSERTED.id,
|
||||||
@@ -36,11 +47,13 @@ OUTPUT
|
|||||||
INSERTED.model,
|
INSERTED.model,
|
||||||
INSERTED.created_at,
|
INSERTED.created_at,
|
||||||
INSERTED.updated_at,
|
INSERTED.updated_at,
|
||||||
INSERTED.state
|
INSERTED.state,
|
||||||
|
INSERTED.started_by
|
||||||
VALUES (
|
VALUES (
|
||||||
@companyuserId,
|
@companyuserId,
|
||||||
@provider,
|
@provider,
|
||||||
@model
|
@model,
|
||||||
|
@startedBy
|
||||||
)
|
)
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -69,11 +82,30 @@ OUTPUT
|
|||||||
INSERTED.model,
|
INSERTED.model,
|
||||||
INSERTED.created_at,
|
INSERTED.created_at,
|
||||||
INSERTED.updated_at,
|
INSERTED.updated_at,
|
||||||
INSERTED.state
|
INSERTED.state,
|
||||||
|
INSERTED.started_by
|
||||||
WHERE id = @id AND companyuser_id = @companyuserId
|
WHERE id = @id AND companyuser_id = @companyuserId
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a conversation and, because started_by is not a foreign key, the whole chain of
|
||||||
|
* subagent conversations below it (started_by -> id, recursively) in a single statement.
|
||||||
|
* Messages go with their conversations via ON DELETE CASCADE. rowsAffected is 0 only when
|
||||||
|
* the root conversation does not exist for this user (a root always yields >= 1 row).
|
||||||
|
*/
|
||||||
export const DELETE_BY_COMPANYUSER_AND_ID = `
|
export const DELETE_BY_COMPANYUSER_AND_ID = `
|
||||||
|
WITH subtree AS (
|
||||||
|
SELECT id
|
||||||
|
FROM modelconversation
|
||||||
|
WHERE id = @id AND companyuser_id = @companyuserId
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
SELECT c.id
|
||||||
|
FROM modelconversation c
|
||||||
|
INNER JOIN subtree s ON c.started_by = s.id
|
||||||
|
WHERE c.companyuser_id = @companyuserId
|
||||||
|
)
|
||||||
DELETE FROM modelconversation
|
DELETE FROM modelconversation
|
||||||
WHERE id = @id AND companyuser_id = @companyuserId
|
WHERE id IN (SELECT id FROM subtree)
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -819,10 +819,12 @@ BEGIN
|
|||||||
created_at DATETIME2 NOT NULL CONSTRAINT DF_modelconversation_created_at DEFAULT SYSUTCDATETIME(),
|
created_at DATETIME2 NOT NULL CONSTRAINT DF_modelconversation_created_at DEFAULT SYSUTCDATETIME(),
|
||||||
updated_at DATETIME2 NOT NULL CONSTRAINT DF_modelconversation_updated_at DEFAULT SYSUTCDATETIME(),
|
updated_at DATETIME2 NOT NULL CONSTRAINT DF_modelconversation_updated_at DEFAULT SYSUTCDATETIME(),
|
||||||
state NVARCHAR(20) NOT NULL CONSTRAINT DF_modelconversation_state DEFAULT 'idle',
|
state NVARCHAR(20) NOT NULL CONSTRAINT DF_modelconversation_state DEFAULT 'idle',
|
||||||
|
started_by INT NULL, -- id of the conversation that started this one as a subagent; deliberately NOT a foreign key
|
||||||
CONSTRAINT FK_modelconversation_companyuser FOREIGN KEY (companyuser_id) REFERENCES companyuser(id) ON DELETE CASCADE,
|
CONSTRAINT FK_modelconversation_companyuser FOREIGN KEY (companyuser_id) REFERENCES companyuser(id) ON DELETE CASCADE,
|
||||||
CONSTRAINT CK_modelconversation_state CHECK (state IN ('idle', 'busy'))
|
CONSTRAINT CK_modelconversation_state CHECK (state IN ('idle', 'busy'))
|
||||||
);
|
);
|
||||||
CREATE INDEX IX_modelconversation_companyuser ON modelconversation (companyuser_id, updated_at);
|
CREATE INDEX IX_modelconversation_companyuser ON modelconversation (companyuser_id, updated_at);
|
||||||
|
CREATE INDEX IX_modelconversation_started_by ON modelconversation (started_by);
|
||||||
END
|
END
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -511,9 +511,11 @@ CREATE TABLE IF NOT EXISTS modelconversation (
|
|||||||
created_at TEXT NOT NULL DEFAULT (${UTC_NOW}),
|
created_at TEXT NOT NULL DEFAULT (${UTC_NOW}),
|
||||||
updated_at TEXT NOT NULL DEFAULT (${UTC_NOW}),
|
updated_at TEXT NOT NULL DEFAULT (${UTC_NOW}),
|
||||||
state TEXT NOT NULL DEFAULT 'idle' CHECK (state IN ('idle', 'busy')),
|
state TEXT NOT NULL DEFAULT 'idle' CHECK (state IN ('idle', 'busy')),
|
||||||
|
started_by INTEGER, -- id of the conversation that started this one as a subagent; deliberately NOT a foreign key
|
||||||
FOREIGN KEY (companyuser_id) REFERENCES companyuser(id) ON DELETE CASCADE
|
FOREIGN KEY (companyuser_id) REFERENCES companyuser(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
CREATE INDEX IF NOT EXISTS IX_modelconversation_companyuser ON modelconversation (companyuser_id, updated_at);
|
CREATE INDEX IF NOT EXISTS IX_modelconversation_companyuser ON modelconversation (companyuser_id, updated_at);
|
||||||
|
CREATE INDEX IF NOT EXISTS IX_modelconversation_started_by ON modelconversation (started_by);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -15,9 +15,10 @@ export default function create(provider) {
|
|||||||
* @param {object} input - Conversation input.
|
* @param {object} input - Conversation input.
|
||||||
* @param {string} input.provider - LLM provider name.
|
* @param {string} input.provider - LLM provider name.
|
||||||
* @param {string} input.model - Model identifier.
|
* @param {string} input.model - Model identifier.
|
||||||
|
* @param {number|null} [input.started_by] - Id of the conversation that starts this one as a subagent (unenforced reference; omit/null for a top-level conversation).
|
||||||
* @param {object} [tx] - Optional transaction to run within.
|
* @param {object} [tx] - Optional transaction to run within.
|
||||||
* @returns {Promise<{id: number, companyuser_id: number, provider: string, model: string, created_at: string, updated_at: string, state: string}>}
|
* @returns {Promise<{id: number, companyuser_id: number, provider: string, model: string, created_at: string, updated_at: string, state: string, started_by: number|null}>}
|
||||||
* @throws {Error} 'Provider required' or 'Model required'.
|
* @throws {Error} 'Provider required', 'Model required' or 'Invalid started_by'.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const c = await store.modelconversation.create(42, { provider: 'openrouter', model: 'openai/gpt-5' });
|
* const c = await store.modelconversation.create(42, { provider: 'openrouter', model: 'openai/gpt-5' });
|
||||||
@@ -28,6 +29,10 @@ export default function create(provider) {
|
|||||||
if (!providerName) throw new Error('Provider required');
|
if (!providerName) throw new Error('Provider required');
|
||||||
const model = typeof input.model === 'string' ? input.model.trim() : '';
|
const model = typeof input.model === 'string' ? input.model.trim() : '';
|
||||||
if (!model) throw new Error('Model required');
|
if (!model) throw new Error('Model required');
|
||||||
|
const startedBy = input.started_by ?? null;
|
||||||
|
if (startedBy !== null && (!Number.isInteger(startedBy) || startedBy <= 0)) {
|
||||||
|
throw new Error('Invalid started_by');
|
||||||
|
}
|
||||||
|
|
||||||
const runner = makeRunner(provider, tx);
|
const runner = makeRunner(provider, tx);
|
||||||
const { companyuserId } = await access(googleAuthId, runner);
|
const { companyuserId } = await access(googleAuthId, runner);
|
||||||
@@ -36,6 +41,7 @@ export default function create(provider) {
|
|||||||
companyuserId,
|
companyuserId,
|
||||||
provider: providerName,
|
provider: providerName,
|
||||||
model,
|
model,
|
||||||
|
startedBy,
|
||||||
});
|
});
|
||||||
return shape(result.rows[0]);
|
return shape(result.rows[0]);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ export default function del(provider) {
|
|||||||
provider?.dialect === 'sqlite3' ? SQLITE_DELETE : MSSQL_DELETE;
|
provider?.dialect === 'sqlite3' ? SQLITE_DELETE : MSSQL_DELETE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a model conversation by ID for the user in the selected company.
|
* Deletes a model conversation by ID for the user in the selected company, together with
|
||||||
|
* every subagent conversation below it (`started_by` chain, recursively) and their messages.
|
||||||
*
|
*
|
||||||
* @param {number} googleAuthId - The id of the Google auth user.
|
* @param {number} googleAuthId - The id of the Google auth user.
|
||||||
* @param {number} conversationId - The conversation id.
|
* @param {number} conversationId - The conversation id.
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export default function get(provider) {
|
|||||||
* @param {number} googleAuthId - The id of the Google auth user.
|
* @param {number} googleAuthId - The id of the Google auth user.
|
||||||
* @param {number} conversationId - The conversation id.
|
* @param {number} conversationId - The conversation id.
|
||||||
* @param {object} [tx] - Optional transaction to run within.
|
* @param {object} [tx] - Optional transaction to run within.
|
||||||
* @returns {Promise<{id: number, companyuser_id: number, provider: string, model: string, created_at: string, updated_at: string, state: string}>}
|
* @returns {Promise<{id: number, companyuser_id: number, provider: string, model: string, created_at: string, updated_at: string, state: string, started_by: number|null}>}
|
||||||
* @throws {Error} 'Conversation not found'.
|
* @throws {Error} 'Conversation not found'.
|
||||||
*/
|
*/
|
||||||
return async (googleAuthId, conversationId, tx) => {
|
return async (googleAuthId, conversationId, tx) => {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export default function list(provider) {
|
|||||||
*
|
*
|
||||||
* @param {number} googleAuthId - The id of the Google auth user.
|
* @param {number} googleAuthId - The id of the Google auth user.
|
||||||
* @param {object} [tx] - Optional transaction to run within.
|
* @param {object} [tx] - Optional transaction to run within.
|
||||||
* @returns {Promise<Array<{id: number, companyuser_id: number, provider: string, model: string, created_at: string, updated_at: string, state: string}>>}
|
* @returns {Promise<Array<{id: number, companyuser_id: number, provider: string, model: string, created_at: string, updated_at: string, state: string, started_by: number|null, first_user_message: string|null}>>} `started_by` is the originating conversation id for subagent conversations (null otherwise), so clients can build a tree. `first_user_message` is the stored `message` payload of the conversation's first `user` message (null if none yet).
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const conversations = await store.modelconversation.list(42);
|
* const conversations = await store.modelconversation.list(42);
|
||||||
@@ -22,6 +22,9 @@ export default function list(provider) {
|
|||||||
const runner = makeRunner(provider, tx);
|
const runner = makeRunner(provider, tx);
|
||||||
const { companyuserId } = await access(googleAuthId, runner);
|
const { companyuserId } = await access(googleAuthId, runner);
|
||||||
const result = await runner.query(sql, { companyuserId });
|
const result = await runner.query(sql, { companyuserId });
|
||||||
return result.rows.map(shape);
|
return result.rows.map((row) => ({
|
||||||
|
...shape(row),
|
||||||
|
first_user_message: row.first_user_message ?? null,
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export default function setState(provider) {
|
|||||||
* @param {number} conversationId - The conversation id.
|
* @param {number} conversationId - The conversation id.
|
||||||
* @param {string} state - The new state ('idle' | 'busy').
|
* @param {string} state - The new state ('idle' | 'busy').
|
||||||
* @param {object} [tx] - Optional transaction to run within.
|
* @param {object} [tx] - Optional transaction to run within.
|
||||||
* @returns {Promise<{id: number, companyuser_id: number, provider: string, model: string, created_at: string, updated_at: string, state: string}>}
|
* @returns {Promise<{id: number, companyuser_id: number, provider: string, model: string, created_at: string, updated_at: string, state: string, started_by: number|null}>}
|
||||||
* @throws {Error} 'Invalid state' or 'Conversation not found'.
|
* @throws {Error} 'Invalid state' or 'Conversation not found'.
|
||||||
*/
|
*/
|
||||||
return async (googleAuthId, conversationId, state, tx) => {
|
return async (googleAuthId, conversationId, state, tx) => {
|
||||||
|
|||||||
@@ -7,5 +7,6 @@ export default function shape(row) {
|
|||||||
created_at: row.created_at ?? row.createdAt,
|
created_at: row.created_at ?? row.createdAt,
|
||||||
updated_at: row.updated_at ?? row.updatedAt,
|
updated_at: row.updated_at ?? row.updatedAt,
|
||||||
state: row.state,
|
state: row.state,
|
||||||
|
started_by: row.started_by ?? row.startedBy ?? null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user