@nocobase/app-plugin-agents 0.1.0-beta.1 → 0.1.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +124 -0
- package/README.md +44 -12
- package/dist/client/api/client.d.ts +9 -2
- package/dist/client/api/client.d.ts.map +1 -1
- package/dist/client/api/client.js +15 -3
- package/dist/client/api/client.js.map +1 -1
- package/dist/client/chat/message-model.d.ts.map +1 -1
- package/dist/client/chat/message-model.js +10 -0
- package/dist/client/chat/message-model.js.map +1 -1
- package/dist/client/chat/settings-section.d.ts.map +1 -1
- package/dist/client/chat/settings-section.js +29 -19
- package/dist/client/chat/settings-section.js.map +1 -1
- package/dist/client/components/model-entries.d.ts +12 -3
- package/dist/client/components/model-entries.d.ts.map +1 -1
- package/dist/client/components/model-entries.js +41 -19
- package/dist/client/components/model-entries.js.map +1 -1
- package/dist/client/components/variables/variable-dialogs.d.ts +6 -1
- package/dist/client/components/variables/variable-dialogs.d.ts.map +1 -1
- package/dist/client/components/variables/variable-dialogs.js +27 -11
- package/dist/client/components/variables/variable-dialogs.js.map +1 -1
- package/dist/client/components/variables/variables-panel.d.ts.map +1 -1
- package/dist/client/components/variables/variables-panel.js +9 -2
- package/dist/client/components/variables/variables-panel.js.map +1 -1
- package/dist/client/lib/agents.d.ts +5 -0
- package/dist/client/lib/agents.d.ts.map +1 -1
- package/dist/client/lib/agents.js +23 -0
- package/dist/client/lib/agents.js.map +1 -1
- package/dist/client/lib/model-suggestions.d.ts +28 -0
- package/dist/client/lib/model-suggestions.d.ts.map +1 -0
- package/dist/client/lib/model-suggestions.js +44 -0
- package/dist/client/lib/model-suggestions.js.map +1 -0
- package/dist/client/locales/chat.en-US.d.ts +7 -0
- package/dist/client/locales/chat.en-US.d.ts.map +1 -1
- package/dist/client/locales/chat.en-US.js +8 -1
- package/dist/client/locales/chat.en-US.js.map +1 -1
- package/dist/client/locales/chat.zh-CN.d.ts.map +1 -1
- package/dist/client/locales/chat.zh-CN.js +8 -1
- package/dist/client/locales/chat.zh-CN.js.map +1 -1
- package/dist/client/locales/en-US.d.ts +20 -1
- package/dist/client/locales/en-US.d.ts.map +1 -1
- package/dist/client/locales/en-US.js +21 -1
- package/dist/client/locales/en-US.js.map +1 -1
- package/dist/client/locales/runWait.en-US.d.ts +31 -0
- package/dist/client/locales/runWait.en-US.d.ts.map +1 -0
- package/dist/client/locales/runWait.en-US.js +30 -0
- package/dist/client/locales/runWait.en-US.js.map +1 -0
- package/dist/client/locales/runWait.zh-CN.d.ts +5 -0
- package/dist/client/locales/runWait.zh-CN.d.ts.map +1 -0
- package/dist/client/locales/runWait.zh-CN.js +25 -0
- package/dist/client/locales/runWait.zh-CN.js.map +1 -0
- package/dist/client/locales/runtimes.en-US.d.ts +6 -0
- package/dist/client/locales/runtimes.en-US.d.ts.map +1 -1
- package/dist/client/locales/runtimes.en-US.js +7 -1
- package/dist/client/locales/runtimes.en-US.js.map +1 -1
- package/dist/client/locales/runtimes.zh-CN.d.ts.map +1 -1
- package/dist/client/locales/runtimes.zh-CN.js +7 -1
- package/dist/client/locales/runtimes.zh-CN.js.map +1 -1
- package/dist/client/locales/zh-CN.d.ts.map +1 -1
- package/dist/client/locales/zh-CN.js +21 -1
- package/dist/client/locales/zh-CN.js.map +1 -1
- package/dist/client/pages/agents/index.d.ts.map +1 -1
- package/dist/client/pages/agents/index.js +6 -6
- package/dist/client/pages/agents/index.js.map +1 -1
- package/dist/client/pages/runtimes/index.js +2 -2
- package/dist/client/pages/runtimes/index.js.map +1 -1
- package/dist/client/pages/runtimes/runner-sheet.js +11 -3
- package/dist/client/pages/runtimes/runner-sheet.js.map +1 -1
- package/dist/client/runs/format-wait.d.ts +21 -0
- package/dist/client/runs/format-wait.d.ts.map +1 -0
- package/dist/client/runs/format-wait.js +76 -0
- package/dist/client/runs/format-wait.js.map +1 -0
- package/dist/client/runs.d.ts +2 -0
- package/dist/client/runs.d.ts.map +1 -1
- package/dist/client/runs.js +2 -0
- package/dist/client/runs.js.map +1 -1
- package/dist/database/migrations/.manifest.json +20 -0
- package/dist/database/migrations/202610090001_ag_add_team_runner_variables.d.ts +9 -0
- package/dist/database/migrations/202610090001_ag_add_team_runner_variables.d.ts.map +1 -0
- package/dist/database/migrations/202610090001_ag_add_team_runner_variables.js +28 -0
- package/dist/database/migrations/202610090001_ag_add_team_runner_variables.js.map +1 -0
- package/dist/database/migrations/202610090002_ag_create_run_requests.d.ts +10 -0
- package/dist/database/migrations/202610090002_ag_create_run_requests.d.ts.map +1 -0
- package/dist/database/migrations/202610090002_ag_create_run_requests.js +105 -0
- package/dist/database/migrations/202610090002_ag_create_run_requests.js.map +1 -0
- package/dist/database/migrations/202610090003_ag_add_execution_history.d.ts +4 -0
- package/dist/database/migrations/202610090003_ag_add_execution_history.d.ts.map +1 -0
- package/dist/database/migrations/202610090003_ag_add_execution_history.js +17 -0
- package/dist/database/migrations/202610090003_ag_add_execution_history.js.map +1 -0
- package/dist/database/migrations/202610090004_ag_add_runner_workspace_usage.d.ts +9 -0
- package/dist/database/migrations/202610090004_ag_add_runner_workspace_usage.d.ts.map +1 -0
- package/dist/database/migrations/202610090004_ag_add_runner_workspace_usage.js +22 -0
- package/dist/database/migrations/202610090004_ag_add_runner_workspace_usage.js.map +1 -0
- package/dist/database/migrations/202610090005_ag_add_runner_variables.d.ts +10 -0
- package/dist/database/migrations/202610090005_ag_add_runner_variables.d.ts.map +1 -0
- package/dist/database/migrations/202610090005_ag_add_runner_variables.js +36 -0
- package/dist/database/migrations/202610090005_ag_add_runner_variables.js.map +1 -0
- package/dist/package.json +1 -1
- package/dist/server/composition.d.ts +12 -2
- package/dist/server/composition.d.ts.map +1 -1
- package/dist/server/composition.js +16 -3
- package/dist/server/composition.js.map +1 -1
- package/dist/server/core/agents/agent.service.d.ts.map +1 -1
- package/dist/server/core/agents/agent.service.js +4 -1
- package/dist/server/core/agents/agent.service.js.map +1 -1
- package/dist/server/core/consultations/consultation.service.d.ts.map +1 -1
- package/dist/server/core/consultations/consultation.service.js +5 -2
- package/dist/server/core/consultations/consultation.service.js.map +1 -1
- package/dist/server/core/conversations/availability.d.ts +10 -4
- package/dist/server/core/conversations/availability.d.ts.map +1 -1
- package/dist/server/core/conversations/availability.js +6 -20
- package/dist/server/core/conversations/availability.js.map +1 -1
- package/dist/server/core/conversations/chat-settings.d.ts +2 -2
- package/dist/server/core/conversations/chat-settings.d.ts.map +1 -1
- package/dist/server/core/conversations/chat-settings.js +19 -1
- package/dist/server/core/conversations/chat-settings.js.map +1 -1
- package/dist/server/core/conversations/conversation.service.d.ts +2 -1
- package/dist/server/core/conversations/conversation.service.d.ts.map +1 -1
- package/dist/server/core/conversations/conversation.service.js +255 -57
- package/dist/server/core/conversations/conversation.service.js.map +1 -1
- package/dist/server/core/runs/availability.d.ts +5 -2
- package/dist/server/core/runs/availability.d.ts.map +1 -1
- package/dist/server/core/runs/availability.js +17 -6
- package/dist/server/core/runs/availability.js.map +1 -1
- package/dist/server/core/runs/claim.d.ts +16 -2
- package/dist/server/core/runs/claim.d.ts.map +1 -1
- package/dist/server/core/runs/claim.js +220 -64
- package/dist/server/core/runs/claim.js.map +1 -1
- package/dist/server/core/runs/eligibility.d.ts +49 -0
- package/dist/server/core/runs/eligibility.d.ts.map +1 -0
- package/dist/server/core/runs/eligibility.js +44 -0
- package/dist/server/core/runs/eligibility.js.map +1 -0
- package/dist/server/core/runs/execution-view.d.ts +10 -0
- package/dist/server/core/runs/execution-view.d.ts.map +1 -0
- package/dist/server/core/runs/execution-view.js +25 -0
- package/dist/server/core/runs/execution-view.js.map +1 -0
- package/dist/server/core/runs/execution.d.ts +13 -0
- package/dist/server/core/runs/execution.d.ts.map +1 -0
- package/dist/server/core/runs/execution.js +85 -0
- package/dist/server/core/runs/execution.js.map +1 -0
- package/dist/server/core/runs/extensions.d.ts +2 -0
- package/dist/server/core/runs/extensions.d.ts.map +1 -1
- package/dist/server/core/runs/extensions.js.map +1 -1
- package/dist/server/core/runs/index.d.ts +6 -3
- package/dist/server/core/runs/index.d.ts.map +1 -1
- package/dist/server/core/runs/index.js +5 -2
- package/dist/server/core/runs/index.js.map +1 -1
- package/dist/server/core/runs/lifecycle.d.ts.map +1 -1
- package/dist/server/core/runs/lifecycle.js +29 -2
- package/dist/server/core/runs/lifecycle.js.map +1 -1
- package/dist/server/core/runs/ports.d.ts +20 -0
- package/dist/server/core/runs/ports.d.ts.map +1 -1
- package/dist/server/core/runs/ports.js.map +1 -1
- package/dist/server/core/runs/preview.d.ts.map +1 -1
- package/dist/server/core/runs/preview.js +2 -0
- package/dist/server/core/runs/preview.js.map +1 -1
- package/dist/server/core/runs/run-requests.d.ts +217 -0
- package/dist/server/core/runs/run-requests.d.ts.map +1 -0
- package/dist/server/core/runs/run-requests.js +471 -0
- package/dist/server/core/runs/run-requests.js.map +1 -0
- package/dist/server/core/runs/run.service.d.ts +63 -7
- package/dist/server/core/runs/run.service.d.ts.map +1 -1
- package/dist/server/core/runs/run.service.js +211 -37
- package/dist/server/core/runs/run.service.js.map +1 -1
- package/dist/server/core/runs/run.store.d.ts +14 -3
- package/dist/server/core/runs/run.store.d.ts.map +1 -1
- package/dist/server/core/runs/run.store.js +41 -2
- package/dist/server/core/runs/run.store.js.map +1 -1
- package/dist/server/core/runs/runner-view.d.ts +4 -3
- package/dist/server/core/runs/runner-view.d.ts.map +1 -1
- package/dist/server/core/runs/runner-view.js +7 -3
- package/dist/server/core/runs/runner-view.js.map +1 -1
- package/dist/server/core/runs/transitions.d.ts.map +1 -1
- package/dist/server/core/runs/transitions.js +4 -0
- package/dist/server/core/runs/transitions.js.map +1 -1
- package/dist/server/core/runs/workload.d.ts +1 -1
- package/dist/server/core/runs/workload.d.ts.map +1 -1
- package/dist/server/core/runs/workload.js +44 -13
- package/dist/server/core/runs/workload.js.map +1 -1
- package/dist/server/core/variables/index.d.ts +1 -1
- package/dist/server/core/variables/index.d.ts.map +1 -1
- package/dist/server/core/variables/index.js +1 -1
- package/dist/server/core/variables/index.js.map +1 -1
- package/dist/server/core/variables/variable.service.d.ts +38 -6
- package/dist/server/core/variables/variable.service.d.ts.map +1 -1
- package/dist/server/core/variables/variable.service.js +201 -30
- package/dist/server/core/variables/variable.service.js.map +1 -1
- package/dist/server/jobs/index.d.ts +1 -1
- package/dist/server/jobs/index.d.ts.map +1 -1
- package/dist/server/jobs/index.js +1 -1
- package/dist/server/jobs/index.js.map +1 -1
- package/dist/server/jobs/job.service.d.ts +2 -0
- package/dist/server/jobs/job.service.d.ts.map +1 -1
- package/dist/server/jobs/job.service.js +3 -1
- package/dist/server/jobs/job.service.js.map +1 -1
- package/dist/server/jobs/spec.d.ts +4 -0
- package/dist/server/jobs/spec.d.ts.map +1 -1
- package/dist/server/jobs/spec.js +6 -0
- package/dist/server/jobs/spec.js.map +1 -1
- package/dist/server/kernel/errors.d.ts +1 -1
- package/dist/server/kernel/errors.d.ts.map +1 -1
- package/dist/server/kernel/errors.js +1 -0
- package/dist/server/kernel/errors.js.map +1 -1
- package/dist/server/kernel/events.d.ts +28 -8
- package/dist/server/kernel/events.d.ts.map +1 -1
- package/dist/server/kernel/events.js.map +1 -1
- package/dist/server/routes/admin.d.ts.map +1 -1
- package/dist/server/routes/admin.js +38 -7
- package/dist/server/routes/admin.js.map +1 -1
- package/dist/server/routes/chat.d.ts.map +1 -1
- package/dist/server/routes/chat.js +9 -6
- package/dist/server/routes/chat.js.map +1 -1
- package/dist/server/routes/index.d.ts.map +1 -1
- package/dist/server/routes/index.js +11 -1
- package/dist/server/routes/index.js.map +1 -1
- package/dist/server/routes/roster.d.ts.map +1 -1
- package/dist/server/routes/roster.js +2 -1
- package/dist/server/routes/roster.js.map +1 -1
- package/dist/server/routes/run-requests.d.ts +6 -0
- package/dist/server/routes/run-requests.d.ts.map +1 -0
- package/dist/server/routes/run-requests.js +216 -0
- package/dist/server/routes/run-requests.js.map +1 -0
- package/dist/server/routes/runners/admin.d.ts +9 -1
- package/dist/server/routes/runners/admin.d.ts.map +1 -1
- package/dist/server/routes/runners/admin.js +62 -24
- package/dist/server/routes/runners/admin.js.map +1 -1
- package/dist/server/routes/runners/runner.d.ts.map +1 -1
- package/dist/server/routes/runners/runner.js +21 -3
- package/dist/server/routes/runners/runner.js.map +1 -1
- package/dist/server/routes/schemas.d.ts +31 -3
- package/dist/server/routes/schemas.d.ts.map +1 -1
- package/dist/server/routes/schemas.js +233 -3
- package/dist/server/routes/schemas.js.map +1 -1
- package/dist/server/runners/index.d.ts +1 -0
- package/dist/server/runners/index.d.ts.map +1 -1
- package/dist/server/runners/index.js +1 -0
- package/dist/server/runners/index.js.map +1 -1
- package/dist/server/runners/runner.service.d.ts +10 -5
- package/dist/server/runners/runner.service.d.ts.map +1 -1
- package/dist/server/runners/runner.service.js +35 -10
- package/dist/server/runners/runner.service.js.map +1 -1
- package/dist/server/runners/runner.store.d.ts +7 -1
- package/dist/server/runners/runner.store.d.ts.map +1 -1
- package/dist/server/runners/runner.store.js +69 -4
- package/dist/server/runners/runner.store.js.map +1 -1
- package/dist/server/runners/sweeper.d.ts +5 -0
- package/dist/server/runners/sweeper.d.ts.map +1 -1
- package/dist/server/runners/sweeper.js +4 -2
- package/dist/server/runners/sweeper.js.map +1 -1
- package/dist/server/runners/workspaces.d.ts +30 -0
- package/dist/server/runners/workspaces.d.ts.map +1 -0
- package/dist/server/runners/workspaces.js +135 -0
- package/dist/server/runners/workspaces.js.map +1 -0
- package/dist/server/tokens.d.ts +4 -2
- package/dist/server/tokens.d.ts.map +1 -1
- package/dist/server/tokens.js +2 -0
- package/dist/server/tokens.js.map +1 -1
- package/dist/shared/agents.d.ts +2 -0
- package/dist/shared/agents.d.ts.map +1 -1
- package/dist/shared/agents.js.map +1 -1
- package/dist/shared/conversations.d.ts +48 -13
- package/dist/shared/conversations.d.ts.map +1 -1
- package/dist/shared/conversations.js +2 -2
- package/dist/shared/conversations.js.map +1 -1
- package/dist/shared/runners.d.ts +56 -5
- package/dist/shared/runners.d.ts.map +1 -1
- package/dist/shared/runners.js.map +1 -1
- package/dist/shared/runs.d.ts +145 -1
- package/dist/shared/runs.d.ts.map +1 -1
- package/dist/shared/runs.js +18 -0
- package/dist/shared/runs.js.map +1 -1
- package/dist/shared/variables.d.ts +22 -0
- package/dist/shared/variables.d.ts.map +1 -1
- package/dist/shared/variables.js +9 -1
- package/dist/shared/variables.js.map +1 -1
- package/package.json +8 -8
- package/skills/nocobase-app-plugin-agents/references/extension-points.md +4 -0
- package/skills/nocobase-app-plugin-agents/references/runners.md +9 -1
- package/skills/nocobase-app-plugin-agents/references/verification-and-troubleshooting.md +1 -1
|
@@ -6,7 +6,7 @@ import { sampleValue } from '../../../shared/briefs.js';
|
|
|
6
6
|
import { forbidden, invalid, notFound, ProtocolError, } from '../../kernel/errors.js';
|
|
7
7
|
import { asJson } from '../../kernel/values.js';
|
|
8
8
|
import { findAgent } from '../agents/index.js';
|
|
9
|
-
import { eventsAfter, hasSession, lastSummary, onlineEntryOf, openRunsOnEach, } from '../runs/index.js';
|
|
9
|
+
import { eventsAfter, hasSession, lastSummary, onlineEntryOf, openRunsOnEach, queuedRun, } from '../runs/index.js';
|
|
10
10
|
import { findRunRecord, toRun } from '../runs/run.store.js';
|
|
11
11
|
import { readAttachmentIds, storedAttachments, } from './attachments.js';
|
|
12
12
|
import { availabilityOf, wakeable } from './availability.js';
|
|
@@ -76,6 +76,10 @@ function noticeText(notice, names) {
|
|
|
76
76
|
switch (notice.code) {
|
|
77
77
|
case 'switchedToDefault':
|
|
78
78
|
return `This conversation now uses ${name(notice.agentId)}, the default agent, until you switch back to ${name(notice.fromAgentId)}.`;
|
|
79
|
+
case 'switchedToOnline':
|
|
80
|
+
return `${name(notice.fromAgentId)} has no runner for you now, so ${name(notice.agentId)} answers until you switch back.`;
|
|
81
|
+
case 'onlineFallbackUnavailable':
|
|
82
|
+
return `The online fallback agent cannot answer (${notice.reason}). This conversation stays with ${name(notice.fromAgentId)} while it waits for a runner.`;
|
|
79
83
|
case 'switchedBack':
|
|
80
84
|
return `This conversation is back with ${name(notice.agentId)}.`;
|
|
81
85
|
case 'runFailed':
|
|
@@ -190,25 +194,81 @@ export function createConversationService(deps) {
|
|
|
190
194
|
const agent = await findAgent(conn, defaultAgentId);
|
|
191
195
|
return wakeable(agent, userId, deps.agents.mayInvoke) ? agent : null;
|
|
192
196
|
}
|
|
193
|
-
|
|
197
|
+
/** The configured online agent and whether its default answering model is offered now. */
|
|
198
|
+
async function onlineFallback(conn, userId, catalog, settings) {
|
|
199
|
+
const { onlineFallbackAgentId } = settings ?? (await deps.settings.settings(conn));
|
|
200
|
+
if (!onlineFallbackAgentId)
|
|
201
|
+
return null;
|
|
202
|
+
const agent = await findAgent(conn, onlineFallbackAgentId);
|
|
203
|
+
const online = agent?.type === 'online' ? agent : null;
|
|
204
|
+
return {
|
|
205
|
+
agent: online,
|
|
206
|
+
availability: availabilityOf(online, userId, deps.agents.mayInvoke, 0, catalog),
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
/** Reads the same features and variable restrictions as claim, before any write transaction. */
|
|
210
|
+
async function availabilityFor(conn, agent, userId, catalog, wanted, runners) {
|
|
211
|
+
const eligible = agent?.type === 'runner' && wakeable(agent, userId, deps.agents.mayInvoke)
|
|
212
|
+
? await deps.eligibility.runnersFor(conn, agent, {
|
|
213
|
+
actorUserId: userId,
|
|
214
|
+
...(runners ? { runners } : {}),
|
|
215
|
+
})
|
|
216
|
+
: [];
|
|
217
|
+
return availabilityOf(agent, userId, deps.agents.mayInvoke, eligible.length, catalog, wanted);
|
|
218
|
+
}
|
|
219
|
+
async function canAnswer(conn, agent, userId, catalog) {
|
|
220
|
+
if (!wakeable(agent, userId, deps.agents.mayInvoke))
|
|
221
|
+
return false;
|
|
222
|
+
return agent.type === 'runner'
|
|
223
|
+
? deps.eligibility.canClaim(conn, agent, { actorUserId: userId })
|
|
224
|
+
: availabilityOf(agent, userId, deps.agents.mayInvoke, 0, catalog).online;
|
|
225
|
+
}
|
|
226
|
+
async function automaticFallback(conn, agent, userId, catalog, settings) {
|
|
227
|
+
const current = settings ?? (await deps.settings.settings(conn));
|
|
228
|
+
if (!current.onlineFallbackAgentId ||
|
|
229
|
+
agent.type !== 'runner' ||
|
|
230
|
+
!wakeable(agent, userId, deps.agents.mayInvoke))
|
|
231
|
+
return null;
|
|
232
|
+
if (await deps.eligibility.canClaim(conn, agent, { actorUserId: userId }))
|
|
233
|
+
return null;
|
|
234
|
+
return onlineFallback(conn, userId, catalog, current);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* The agent `fallback` switches the conversation to: an available system default of the conversation's mode, else,
|
|
238
|
+
* for a runner conversation, the online fallback agent. Null while it already uses another agent in place of its own.
|
|
239
|
+
*/
|
|
240
|
+
async function fallbackTarget(conn, record, catalog) {
|
|
241
|
+
if (record.fallbackFromAgentId)
|
|
242
|
+
return null;
|
|
243
|
+
const system = await systemDefault(conn, record.userId);
|
|
244
|
+
if (system &&
|
|
245
|
+
system.id !== record.agentId &&
|
|
246
|
+
system.type === record.mode &&
|
|
247
|
+
(await canAnswer(conn, system, record.userId, catalog)))
|
|
248
|
+
return system;
|
|
249
|
+
if (record.mode !== 'runner')
|
|
250
|
+
return null;
|
|
251
|
+
const online = await onlineFallback(conn, record.userId, catalog);
|
|
252
|
+
return online?.availability.online && online.agent?.id !== record.agentId
|
|
253
|
+
? online.agent
|
|
254
|
+
: null;
|
|
255
|
+
}
|
|
256
|
+
async function detailOf(conn, record) {
|
|
194
257
|
const catalog = await deps.models.catalog();
|
|
195
258
|
const [summary] = await summaries(conn, [record], catalog.defaultModel);
|
|
196
259
|
const agent = await findAgent(conn, record.agentId);
|
|
197
260
|
const fallbackFrom = record.fallbackFromAgentId
|
|
198
261
|
? await findAgent(conn, record.fallbackFromAgentId)
|
|
199
262
|
: null;
|
|
200
|
-
const
|
|
263
|
+
const target = await fallbackTarget(conn, record, catalog);
|
|
201
264
|
return {
|
|
202
265
|
...summary,
|
|
203
|
-
availability:
|
|
266
|
+
availability: await availabilityFor(conn, agent, record.userId, catalog, chosenModel(record)),
|
|
204
267
|
models: record.mode === 'online' && agent
|
|
205
268
|
? chatModelChoices(onlineEntriesOrDefault(agent, catalog.defaultModel ?? null), catalog)
|
|
206
269
|
: [],
|
|
207
|
-
canFallback: Boolean(
|
|
208
|
-
|
|
209
|
-
system.id !== record.agentId &&
|
|
210
|
-
system.type === record.mode),
|
|
211
|
-
canRestore: wakeable(fallbackFrom, record.userId, deps.agents.mayInvoke),
|
|
270
|
+
canFallback: Boolean(target),
|
|
271
|
+
canRestore: await canAnswer(conn, fallbackFrom, record.userId, catalog),
|
|
212
272
|
};
|
|
213
273
|
}
|
|
214
274
|
async function detail(userId, id) {
|
|
@@ -232,9 +292,13 @@ export function createConversationService(deps) {
|
|
|
232
292
|
return appended;
|
|
233
293
|
}
|
|
234
294
|
async function notice(unit, record, value, runId = null) {
|
|
235
|
-
const agentIds = value.code === 'switchedToDefault' ||
|
|
295
|
+
const agentIds = value.code === 'switchedToDefault' ||
|
|
296
|
+
value.code === 'switchedToOnline' ||
|
|
297
|
+
value.code === 'switchedBack'
|
|
236
298
|
? [value.agentId, value.fromAgentId]
|
|
237
|
-
:
|
|
299
|
+
: value.code === 'onlineFallbackUnavailable'
|
|
300
|
+
? [value.fromAgentId]
|
|
301
|
+
: [];
|
|
238
302
|
const names = new Map();
|
|
239
303
|
for (const id of agentIds) {
|
|
240
304
|
const agent = await findAgent(unit.conn, id);
|
|
@@ -251,6 +315,38 @@ export function createConversationService(deps) {
|
|
|
251
315
|
async function nameOf(conn, userId) {
|
|
252
316
|
return (await deps.people.names(conn, [userId])).get(userId) ?? userId;
|
|
253
317
|
}
|
|
318
|
+
/** Remember the last fallback failure in the transcript, across requests and application restarts. */
|
|
319
|
+
async function fallbackFailureNotice(unit, record, value) {
|
|
320
|
+
const previous = await messagesRepo(unit.conn).findOne({
|
|
321
|
+
filter: (f) => f.and([
|
|
322
|
+
f.string('conversationId').eq(record.id),
|
|
323
|
+
f.string('role').eq('system'),
|
|
324
|
+
f
|
|
325
|
+
.json('metadata')
|
|
326
|
+
.path(['notice', 'code'])
|
|
327
|
+
.eq('onlineFallbackUnavailable'),
|
|
328
|
+
]),
|
|
329
|
+
sort: (sort) => sort.field('seq').desc(),
|
|
330
|
+
});
|
|
331
|
+
const metadata = previous ? metadataOf(previous) : null;
|
|
332
|
+
const last = metadata?.notice;
|
|
333
|
+
if (value &&
|
|
334
|
+
!metadata?.onlineFallbackResolved &&
|
|
335
|
+
last?.code === 'onlineFallbackUnavailable' &&
|
|
336
|
+
last.fromAgentId === value.fromAgentId &&
|
|
337
|
+
last.reason === value.reason)
|
|
338
|
+
return;
|
|
339
|
+
if (previous && !metadata?.onlineFallbackResolved)
|
|
340
|
+
await messagesRepo(unit.conn).updateMany({
|
|
341
|
+
filter: { id: previous.id },
|
|
342
|
+
values: {
|
|
343
|
+
metadata: asJson({ ...metadata, onlineFallbackResolved: true }),
|
|
344
|
+
updatedAt: now(),
|
|
345
|
+
},
|
|
346
|
+
});
|
|
347
|
+
if (value)
|
|
348
|
+
await notice(unit, record, value);
|
|
349
|
+
}
|
|
254
350
|
/** The run input a user message becomes: its text, its files, and its page context marked as data. */
|
|
255
351
|
function inputText(message) {
|
|
256
352
|
const view = toMessage(message);
|
|
@@ -272,7 +368,7 @@ export function createConversationService(deps) {
|
|
|
272
368
|
let result = null;
|
|
273
369
|
for (const message of messages) {
|
|
274
370
|
const files = storedAttachments(message.attachments);
|
|
275
|
-
const enqueued = await deps.runs.enqueue({
|
|
371
|
+
const enqueued = queuedRun(await deps.runs.enqueue({
|
|
276
372
|
agentId: agent.id,
|
|
277
373
|
subject: subjectOf(record.id),
|
|
278
374
|
threadScope: threadScopeOf(record),
|
|
@@ -293,22 +389,27 @@ export function createConversationService(deps) {
|
|
|
293
389
|
: {}),
|
|
294
390
|
},
|
|
295
391
|
},
|
|
296
|
-
}, unit);
|
|
392
|
+
}, unit));
|
|
297
393
|
await updateMessageRun(unit.conn, message, enqueued.runId, { inputId: enqueued.inputId }, now());
|
|
298
394
|
result = { id: enqueued.runId, outcome: enqueued.outcome };
|
|
299
395
|
}
|
|
300
396
|
return result;
|
|
301
397
|
}
|
|
302
398
|
/**
|
|
303
|
-
* Moves the conversation to `
|
|
304
|
-
* messages nobody answered go to the new agent.
|
|
399
|
+
* Moves the conversation to `agent`: its queued work is withdrawn, the thread moves on (a new session), and the
|
|
400
|
+
* messages nobody answered go to the new agent. The mode follows the agent's type; a model chosen for an online
|
|
401
|
+
* conversation is dropped when it changes.
|
|
305
402
|
*/
|
|
306
|
-
async function rebind(unit, record,
|
|
403
|
+
async function rebind(unit, record, agent, fallbackFromAgentId, value) {
|
|
307
404
|
const withdrawn = await deps.runs.withdrawQueued({ subject: subjectOf(record.id), byUserId: record.userId }, unit);
|
|
308
405
|
await conversationsRepo(unit.conn).updateMany({
|
|
309
406
|
filter: { id: record.id },
|
|
310
407
|
values: {
|
|
311
|
-
agentId,
|
|
408
|
+
agentId: agent.id,
|
|
409
|
+
mode: agent.type,
|
|
410
|
+
...(agent.type === record.mode
|
|
411
|
+
? {}
|
|
412
|
+
: { modelService: null, model: null }),
|
|
312
413
|
fallbackFromAgentId,
|
|
313
414
|
thread: Number(record.thread) + 1,
|
|
314
415
|
updatedAt: now(),
|
|
@@ -628,13 +729,51 @@ export function createConversationService(deps) {
|
|
|
628
729
|
throw invalid('This application takes no files in chat.', {
|
|
629
730
|
field: 'attachmentIds',
|
|
630
731
|
});
|
|
631
|
-
await owned(tx.read(), userId, id);
|
|
732
|
+
const before = await owned(tx.read(), userId, id);
|
|
733
|
+
const agent = before.mode === 'runner'
|
|
734
|
+
? await findAgent(tx.read(), before.agentId)
|
|
735
|
+
: null;
|
|
736
|
+
const replacement = agent
|
|
737
|
+
? await automaticFallback(tx.read(), agent, userId, await deps.models.catalog())
|
|
738
|
+
: null;
|
|
632
739
|
// Resolved before the transaction: resolvers read through the services of the plugins that own the kinds.
|
|
633
740
|
const context = request.context
|
|
634
741
|
? await deps.contextKinds.resolve(tx.read(), userId, request.context)
|
|
635
742
|
: null;
|
|
636
743
|
const sent = await tx.run(async (unit) => {
|
|
637
|
-
|
|
744
|
+
let record = await lockOwned(unit, userId, id);
|
|
745
|
+
let fallbackFailure = null;
|
|
746
|
+
// A concurrent switch owns its new binding; do not apply a decision made for the previous one.
|
|
747
|
+
if (replacement &&
|
|
748
|
+
record.agentId === before.agentId &&
|
|
749
|
+
record.thread === before.thread &&
|
|
750
|
+
record.mode === 'runner' &&
|
|
751
|
+
record.fallbackFromAgentId === before.fallbackFromAgentId) {
|
|
752
|
+
const current = await findAgent(unit.conn, record.agentId);
|
|
753
|
+
const target = replacement.agent
|
|
754
|
+
? await findAgent(unit.conn, replacement.agent.id)
|
|
755
|
+
: null;
|
|
756
|
+
if (current?.revision === agent?.revision &&
|
|
757
|
+
replacement.availability.online &&
|
|
758
|
+
wakeable(target, userId, deps.agents.mayInvoke) &&
|
|
759
|
+
target.revision === replacement.agent?.revision) {
|
|
760
|
+
await rebind(unit, record, target, record.fallbackFromAgentId ?? record.agentId, {
|
|
761
|
+
code: 'switchedToOnline',
|
|
762
|
+
agentId: target.id,
|
|
763
|
+
fromAgentId: record.agentId,
|
|
764
|
+
});
|
|
765
|
+
record = (await findConversation(unit.conn, id));
|
|
766
|
+
}
|
|
767
|
+
else if (current?.revision === agent?.revision &&
|
|
768
|
+
replacement.availability.reason) {
|
|
769
|
+
fallbackFailure = {
|
|
770
|
+
code: 'onlineFallbackUnavailable',
|
|
771
|
+
fromAgentId: record.agentId,
|
|
772
|
+
reason: replacement.availability.reason,
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
await fallbackFailureNotice(unit, record, fallbackFailure);
|
|
638
777
|
const at = now();
|
|
639
778
|
if (record.archivedAt || (record.titleSource === 'auto' && !record.title))
|
|
640
779
|
await conversationsRepo(unit.conn).updateMany({
|
|
@@ -742,11 +881,26 @@ export function createConversationService(deps) {
|
|
|
742
881
|
async create(userId, request) {
|
|
743
882
|
const wanted = request.model ?? null;
|
|
744
883
|
// Read before the transaction, which a read on another connection would wait for.
|
|
745
|
-
const
|
|
746
|
-
|
|
884
|
+
const catalog = await deps.models.catalog();
|
|
885
|
+
const fallback = catalog.defaultModel ?? null;
|
|
886
|
+
const settings = await deps.settings.settings();
|
|
887
|
+
const picked = await pickAgent(tx.read(), userId, request.agentId);
|
|
888
|
+
const replacement = !wanted
|
|
889
|
+
? await automaticFallback(tx.read(), picked, userId, catalog, settings)
|
|
747
890
|
: null;
|
|
748
891
|
const id = await tx.run(async (unit) => {
|
|
749
|
-
const
|
|
892
|
+
const current = await pickAgent(unit.conn, userId, request.agentId);
|
|
893
|
+
const target = replacement?.agent
|
|
894
|
+
? await findAgent(unit.conn, replacement.agent.id)
|
|
895
|
+
: null;
|
|
896
|
+
const instead = current.id === picked.id &&
|
|
897
|
+
current.revision === picked.revision &&
|
|
898
|
+
replacement?.availability.online &&
|
|
899
|
+
wakeable(target, userId, deps.agents.mayInvoke) &&
|
|
900
|
+
target.revision === replacement.agent?.revision
|
|
901
|
+
? target
|
|
902
|
+
: null;
|
|
903
|
+
const agent = instead ?? current;
|
|
750
904
|
if (wanted &&
|
|
751
905
|
(agent.type !== 'online' ||
|
|
752
906
|
!onlineEntriesOrDefault(agent, fallback).some((entry) => sameEntry(entry, wanted))))
|
|
@@ -765,7 +919,7 @@ export function createConversationService(deps) {
|
|
|
765
919
|
userId,
|
|
766
920
|
agentId: agent.id,
|
|
767
921
|
mode: agent.type,
|
|
768
|
-
fallbackFromAgentId: null,
|
|
922
|
+
fallbackFromAgentId: instead ? picked.id : null,
|
|
769
923
|
modelService: wanted?.modelService ?? null,
|
|
770
924
|
model: wanted?.model ?? null,
|
|
771
925
|
title,
|
|
@@ -784,7 +938,21 @@ export function createConversationService(deps) {
|
|
|
784
938
|
updatedAt: at,
|
|
785
939
|
},
|
|
786
940
|
});
|
|
787
|
-
|
|
941
|
+
const record = (await findConversation(unit.conn, conversationId));
|
|
942
|
+
if (instead)
|
|
943
|
+
await notice(unit, record, {
|
|
944
|
+
code: 'switchedToOnline',
|
|
945
|
+
agentId: instead.id,
|
|
946
|
+
fromAgentId: picked.id,
|
|
947
|
+
});
|
|
948
|
+
else if (replacement?.availability.reason)
|
|
949
|
+
await notice(unit, record, {
|
|
950
|
+
code: 'onlineFallbackUnavailable',
|
|
951
|
+
fromAgentId: picked.id,
|
|
952
|
+
reason: replacement.availability.reason,
|
|
953
|
+
});
|
|
954
|
+
else
|
|
955
|
+
changed(unit, record);
|
|
788
956
|
return conversationId;
|
|
789
957
|
});
|
|
790
958
|
return detail(userId, id);
|
|
@@ -916,31 +1084,47 @@ export function createConversationService(deps) {
|
|
|
916
1084
|
return detail(userId, id);
|
|
917
1085
|
},
|
|
918
1086
|
async fallback(userId, id) {
|
|
1087
|
+
const catalog = await deps.models.catalog();
|
|
1088
|
+
const before = await owned(tx.read(), userId, id);
|
|
1089
|
+
const target = await fallbackTarget(tx.read(), before, catalog);
|
|
919
1090
|
await tx.run(async (unit) => {
|
|
920
1091
|
const record = await lockOwned(unit, userId, id);
|
|
921
|
-
const
|
|
922
|
-
if (!
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
1092
|
+
const current = target ? await findAgent(unit.conn, target.id) : null;
|
|
1093
|
+
if (!target ||
|
|
1094
|
+
!wakeable(current, userId, deps.agents.mayInvoke) ||
|
|
1095
|
+
current.revision !== target.revision ||
|
|
1096
|
+
record.agentId !== before.agentId ||
|
|
1097
|
+
record.thread !== before.thread ||
|
|
1098
|
+
record.fallbackFromAgentId !== before.fallbackFromAgentId)
|
|
1099
|
+
throw conversationConflict('noFallback', 'This conversation cannot switch to another agent.');
|
|
1100
|
+
await rebind(unit, record, current, record.agentId, {
|
|
1101
|
+
code: target.type === record.mode
|
|
1102
|
+
? 'switchedToDefault'
|
|
1103
|
+
: 'switchedToOnline',
|
|
1104
|
+
agentId: target.id,
|
|
930
1105
|
fromAgentId: record.agentId,
|
|
931
1106
|
});
|
|
932
1107
|
});
|
|
933
1108
|
return detail(userId, id);
|
|
934
1109
|
},
|
|
935
1110
|
async restore(userId, id) {
|
|
1111
|
+
const before = await owned(tx.read(), userId, id);
|
|
1112
|
+
const own = before.fallbackFromAgentId
|
|
1113
|
+
? await findAgent(tx.read(), before.fallbackFromAgentId)
|
|
1114
|
+
: null;
|
|
1115
|
+
const available = await canAnswer(tx.read(), own, userId, await deps.models.catalog());
|
|
936
1116
|
await tx.run(async (unit) => {
|
|
937
1117
|
const record = await lockOwned(unit, userId, id);
|
|
938
|
-
const
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
1118
|
+
const current = own ? await findAgent(unit.conn, own.id) : null;
|
|
1119
|
+
if (!own ||
|
|
1120
|
+
!available ||
|
|
1121
|
+
!wakeable(current, userId, deps.agents.mayInvoke) ||
|
|
1122
|
+
current.revision !== own.revision ||
|
|
1123
|
+
record.agentId !== before.agentId ||
|
|
1124
|
+
record.thread !== before.thread ||
|
|
1125
|
+
record.fallbackFromAgentId !== own.id)
|
|
942
1126
|
throw conversationConflict('noRestore', 'This conversation cannot switch back to its own agent now.');
|
|
943
|
-
await rebind(unit, record,
|
|
1127
|
+
await rebind(unit, record, current, null, {
|
|
944
1128
|
code: 'switchedBack',
|
|
945
1129
|
agentId: own.id,
|
|
946
1130
|
fromAgentId: record.agentId,
|
|
@@ -1039,14 +1223,14 @@ export function createConversationService(deps) {
|
|
|
1039
1223
|
const agent = await findAgent(outer.conn, record.agentId);
|
|
1040
1224
|
if (!wakeable(agent, record.userId, deps.agents.mayInvoke))
|
|
1041
1225
|
return null;
|
|
1042
|
-
const enqueued = await deps.runs.enqueue({
|
|
1226
|
+
const enqueued = queuedRun(await deps.runs.enqueue({
|
|
1043
1227
|
agentId: agent.id,
|
|
1044
1228
|
subject: subjectOf(record.id),
|
|
1045
1229
|
threadScope: threadScopeOf(record),
|
|
1046
1230
|
actorUserId: record.userId,
|
|
1047
1231
|
ownerUserId: record.userId,
|
|
1048
1232
|
input: news.input,
|
|
1049
|
-
}, outer);
|
|
1233
|
+
}, outer));
|
|
1050
1234
|
return { id: enqueued.runId, outcome: enqueued.outcome };
|
|
1051
1235
|
},
|
|
1052
1236
|
lock: async (conn, id) => (await lockConversation(conn, id, now())) !== null,
|
|
@@ -1096,28 +1280,42 @@ export function createConversationService(deps) {
|
|
|
1096
1280
|
const conn = tx.read();
|
|
1097
1281
|
const [agents, runners, settings, preferences, catalog] = await Promise.all([
|
|
1098
1282
|
deps.agents.list(),
|
|
1099
|
-
deps.runners
|
|
1283
|
+
deps.runners
|
|
1284
|
+
.list()
|
|
1285
|
+
.then((all) => all.filter((runner) => runner.status === 'online')),
|
|
1100
1286
|
deps.settings.settings(conn),
|
|
1101
1287
|
deps.settings.preferences(userId, conn),
|
|
1102
1288
|
deps.models.catalog(),
|
|
1103
1289
|
]);
|
|
1104
|
-
|
|
1290
|
+
const replacement = await onlineFallback(conn, userId, catalog, settings);
|
|
1291
|
+
const online = replacement?.availability.online
|
|
1292
|
+
? replacement.agent
|
|
1293
|
+
: null;
|
|
1294
|
+
return Promise.all(agents
|
|
1105
1295
|
.filter((agent) => deps.agents.mayInvoke(agent, userId))
|
|
1106
|
-
.map((agent) =>
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
:
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1296
|
+
.map(async (agent) => {
|
|
1297
|
+
const availability = await availabilityFor(conn, agent, userId, catalog, undefined, runners);
|
|
1298
|
+
return {
|
|
1299
|
+
id: agent.id,
|
|
1300
|
+
name: agent.name,
|
|
1301
|
+
description: agent.description,
|
|
1302
|
+
nameText: agent.nameText,
|
|
1303
|
+
descriptionText: agent.descriptionText,
|
|
1304
|
+
avatar: agent.avatar,
|
|
1305
|
+
type: agent.type,
|
|
1306
|
+
models: agent.type === 'online'
|
|
1307
|
+
? chatModelChoices(onlineEntriesOrDefault(agent, catalog.defaultModel ?? null), catalog)
|
|
1308
|
+
: [],
|
|
1309
|
+
personal: agent.ownerUserId === userId && agent.access === 'ownerOnly',
|
|
1310
|
+
isSystemDefault: agent.id === settings.defaultAgentId,
|
|
1311
|
+
isMyDefault: agent.id === preferences.defaultAgentId,
|
|
1312
|
+
availability,
|
|
1313
|
+
fallbackAgentId: online &&
|
|
1314
|
+
agent.type === 'runner' &&
|
|
1315
|
+
availability.reason === 'noRunner'
|
|
1316
|
+
? online.id
|
|
1317
|
+
: null,
|
|
1318
|
+
};
|
|
1121
1319
|
}));
|
|
1122
1320
|
},
|
|
1123
1321
|
async copyAgent(userId, agentId, request) {
|