@capybara-hq/kernel 0.1.0-rc.24 → 0.1.0-rc.25
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/dist/admin/admin.module.js +2 -0
- package/dist/admin/health.service.d.ts +2 -2
- package/dist/admin/health.service.js +6 -2
- package/dist/agents/agents.module.js +3 -0
- package/dist/agents/agents.service.d.ts +6 -1
- package/dist/agents/agents.service.js +11 -2
- package/dist/catalog/catalog-import.module.js +0 -4
- package/dist/catalog/catalog-scope.service.d.ts +63 -26
- package/dist/catalog/catalog-scope.service.js +146 -71
- package/dist/catalog/catalog.module.js +4 -3
- package/dist/catalog/catalog.service.d.ts +11 -4
- package/dist/catalog/catalog.service.js +21 -5
- package/dist/catalog/cli.d.ts +4 -3
- package/dist/catalog/cli.js +8 -13
- package/dist/catalog/job-ids.d.ts +8 -20
- package/dist/catalog/job-ids.js +11 -16
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/catalog/org-plugin-uploads.d.ts +11 -4
- package/dist/catalog/org-plugin-uploads.js +21 -6
- package/dist/catalog/owned-items.d.ts +6 -13
- package/dist/catalog/owned-items.js +11 -29
- package/dist/catalog/owned-plugin-cleanup.d.ts +8 -17
- package/dist/catalog/owned-plugin-cleanup.js +12 -66
- package/dist/catalog/platform-components.d.ts +56 -34
- package/dist/catalog/platform-components.js +68 -78
- package/dist/catalog/plugin-import.d.ts +28 -25
- package/dist/catalog/plugin-import.js +90 -76
- package/dist/catalog/plugin-upgrade.d.ts +4 -4
- package/dist/catalog/plugin-upgrade.js +7 -19
- package/dist/catalog/scope-jobs.d.ts +59 -0
- package/dist/catalog/scope-jobs.js +65 -0
- package/dist/catalog/scope-sync-workers.d.ts +22 -0
- package/dist/catalog/scope-sync-workers.js +58 -2
- package/dist/catalog/scope-sync.d.ts +40 -65
- package/dist/catalog/scope-sync.js +114 -122
- package/dist/channels/inbound.js +2 -2
- package/dist/channels/threads.d.ts +4 -3
- package/dist/channels/threads.js +4 -3
- package/dist/conversations/conversations.controller.js +4 -2
- package/dist/conversations/conversations.service.d.ts +17 -5
- package/dist/conversations/conversations.service.js +17 -5
- package/dist/conversations/events.d.ts +21 -0
- package/dist/conversations/events.js +24 -1
- package/dist/conversations/source.d.ts +10 -0
- package/dist/conversations/source.js +17 -0
- package/dist/conversations/tree.d.ts +10 -0
- package/dist/conversations/tree.js +35 -0
- package/dist/db/kernel-schema.d.ts +9 -14
- package/dist/devices/devices.service.d.ts +11 -8
- package/dist/devices/devices.service.js +16 -23
- package/dist/evals/run-replay.d.ts +13 -2
- package/dist/evals/run-replay.js +28 -1
- package/dist/evals/snapshot.d.ts +7 -1
- package/dist/evals/snapshot.js +2 -1
- package/dist/extensions/hooks.d.ts +22 -3
- package/dist/extensions/hooks.js +15 -1
- package/dist/extensions/lifecycle-hooks.d.ts +99 -0
- package/dist/extensions/lifecycle-hooks.js +273 -0
- package/dist/extensions/org-config.d.ts +13 -1
- package/dist/extensions/org-config.js +19 -2
- package/dist/extensions/permission-view.d.ts +9 -5
- package/dist/extensions/permission-view.js +9 -4
- package/dist/extensions/point-defaults.d.ts +28 -1
- package/dist/extensions/point-defaults.js +44 -13
- package/dist/inbox/inbox.service.d.ts +6 -20
- package/dist/inbox/inbox.service.js +0 -3
- package/dist/inbox/org-notices.d.ts +1 -1
- package/dist/inbox/platform-inbox.service.d.ts +3 -32
- package/dist/inbox/platform-inbox.service.js +5 -54
- package/dist/infra/outbox/registry.d.ts +124 -0
- package/dist/infra/outbox/registry.js +126 -0
- package/dist/infra/queue/queue-names.d.ts +1 -1
- package/dist/infra/queue/queue-names.js +1 -0
- package/dist/orgs/directory-sync.js +1 -1
- package/dist/orgs/members.service.js +1 -1
- package/dist/orgs/offboarding.d.ts +50 -57
- package/dist/orgs/offboarding.js +117 -162
- package/dist/orgs/orgs.module.js +3 -0
- package/dist/orgs/orgs.service.d.ts +11 -5
- package/dist/orgs/orgs.service.js +15 -6
- package/dist/overrides.d.ts +3 -0
- package/dist/overrides.js +2 -0
- package/dist/platform/ops/inbox.ops.js +4 -20
- package/dist/platform/ops/platform.ops.d.ts +1 -0
- package/dist/platform/ops/platform.ops.js +6 -3
- package/dist/roles/worker-role.d.ts +10 -5
- package/dist/roles/worker-role.js +19 -9
- package/dist/roles/worker.module.js +2 -4
- package/dist/runtime/context/build.d.ts +3 -0
- package/dist/runtime/context/build.js +15 -0
- package/dist/runtime/context/plugin-context.d.ts +20 -0
- package/dist/runtime/context/plugin-context.js +30 -0
- package/dist/runtime/langgraph-loop.js +4 -0
- package/dist/runtime/loop.d.ts +8 -0
- package/dist/runtime/maintenance.d.ts +5 -3
- package/dist/runtime/maintenance.js +36 -11
- package/dist/runtime/model-call.d.ts +6 -1
- package/dist/runtime/model-call.js +1 -0
- package/dist/runtime/plugin-context.service.d.ts +69 -0
- package/dist/runtime/plugin-context.service.js +208 -0
- package/dist/runtime/run-control.d.ts +6 -1
- package/dist/runtime/run-control.js +13 -0
- package/dist/runtime/run-queue.d.ts +13 -0
- package/dist/runtime/run-queue.js +63 -0
- package/dist/runtime/run-worker.d.ts +22 -1
- package/dist/runtime/run-worker.js +107 -10
- package/dist/runtime/runtime.module.js +2 -0
- package/dist/runtime/subagents/dispatch.js +1 -0
- package/dist/runtime/subagents/skill-fork.js +3 -0
- package/dist/sandbox/jobs.d.ts +20 -10
- package/dist/sandbox/jobs.js +23 -17
- package/dist/schedules/fire.d.ts +25 -4
- package/dist/schedules/fire.js +87 -13
- package/dist/schedules/job-ids.d.ts +66 -17
- package/dist/schedules/job-ids.js +81 -13
- package/dist/schedules/run-tracker.js +2 -1
- package/dist/schedules/scan.job.d.ts +13 -32
- package/dist/schedules/scan.job.js +35 -144
- package/dist/schedules/scheduled-tasks.service.d.ts +6 -6
- package/dist/schedules/scheduled-tasks.service.js +17 -14
- package/dist/schedules/schedules.module.js +0 -4
- package/dist/skills/changes.controller.d.ts +1 -1
- package/dist/skills/changes.controller.js +1 -1
- package/dist/tools/approvals.service.d.ts +15 -4
- package/dist/tools/approvals.service.js +26 -10
- package/dist/tools/pipeline.d.ts +11 -2
- package/dist/tools/pipeline.js +5 -0
- package/dist/tools/questions.service.d.ts +15 -4
- package/dist/tools/questions.service.js +21 -19
- package/dist/tools/registry.d.ts +8 -1
- package/dist/tools/registry.js +9 -0
- package/package.json +3 -3
- package/src/migrations/0121_drop_platform_migration_runs.sql +12 -0
- package/src/migrations/0122_drop_scheduled_task_runs_rechecked_at.sql +5 -0
- package/src/migrations/0123_pgvector.sql +27 -0
- package/src/migrations/0124_conversation_source.sql +36 -0
- package/dist/catalog/platform-migration.d.ts +0 -115
- package/dist/catalog/platform-migration.js +0 -583
- package/dist/infra/queue/add-job-once.d.ts +0 -14
- package/dist/infra/queue/add-job-once.js +0 -16
|
@@ -177,9 +177,10 @@ let ConversationsService = class ConversationsService {
|
|
|
177
177
|
* Creates an empty conversation bound to its device (`pick`, see
|
|
178
178
|
* {@link ConversationDevices.forNew}); an agent the account cannot use →
|
|
179
179
|
* `agent.not_found`, a device it may not use → `device.not_found` /
|
|
180
|
-
* `device.unavailable`, and nothing is created.
|
|
180
|
+
* `device.unavailable`, and nothing is created. `opts.source` records where the conversation
|
|
181
|
+
* comes from.
|
|
181
182
|
*/
|
|
182
|
-
async create(orgId, accountId, agentId, pick = {}) {
|
|
183
|
+
async create(orgId, accountId, agentId, pick = {}, opts) {
|
|
183
184
|
await this.agents.get(orgId, agentId, accountId);
|
|
184
185
|
const deviceId = await this.devices.forNew(orgId, accountId, pick);
|
|
185
186
|
const row = await this.db
|
|
@@ -190,6 +191,7 @@ let ConversationsService = class ConversationsService {
|
|
|
190
191
|
agent_id: agentId,
|
|
191
192
|
account_id: accountId,
|
|
192
193
|
device_id: deviceId,
|
|
194
|
+
source: opts.source,
|
|
193
195
|
})
|
|
194
196
|
.returning(COLUMNS)
|
|
195
197
|
.executeTakeFirstOrThrow();
|
|
@@ -203,10 +205,10 @@ let ConversationsService = class ConversationsService {
|
|
|
203
205
|
* `agent.not_found` and nothing is written. Queues a run afterwards, like `postUserMessage`.
|
|
204
206
|
* `events` are every event the create wrote, in seq order from seq 1 (a `model_switched` before
|
|
205
207
|
* the message when the send switched), so a client can seed the new conversation's log whole.
|
|
206
|
-
* `internal
|
|
207
|
-
* `conversations.scheduled_task_id`, and its zone override.
|
|
208
|
+
* `internal`: the conversation's source and, for kernel callers only, a scheduled task's marker,
|
|
209
|
+
* which also sets `conversations.scheduled_task_id`, and its zone override.
|
|
208
210
|
*/
|
|
209
|
-
async createWithMessage(orgId, accountId, agentId, input, pick = {}, internal
|
|
211
|
+
async createWithMessage(orgId, accountId, agentId, input, pick = {}, internal) {
|
|
210
212
|
const agent = await this.agents.get(orgId, agentId, accountId);
|
|
211
213
|
const prior = await this.findCreated(orgId, accountId, agentId, input.clientMessageId);
|
|
212
214
|
if (prior)
|
|
@@ -242,6 +244,7 @@ let ConversationsService = class ConversationsService {
|
|
|
242
244
|
device_id: conv.deviceId,
|
|
243
245
|
create_request_id: input.clientMessageId,
|
|
244
246
|
scheduled_task_id: internal.schedule?.taskId ?? null,
|
|
247
|
+
source: internal.source,
|
|
245
248
|
})
|
|
246
249
|
.onConflict((oc) => oc
|
|
247
250
|
.columns(["org_id", "account_id", "create_request_id"])
|
|
@@ -1014,6 +1017,15 @@ let ConversationsService = class ConversationsService {
|
|
|
1014
1017
|
await runs.ensureRunAfterCommit(conversationId);
|
|
1015
1018
|
return run.id;
|
|
1016
1019
|
}
|
|
1020
|
+
// a queued run no worker has started yet is ended in PG (ruling R27), never left to the stop
|
|
1021
|
+
// marker's TTL: the marker first, so a worker that takes it meanwhile stops too
|
|
1022
|
+
if (run.status === "queued") {
|
|
1023
|
+
await runs.requestAbort(run.id, accountId);
|
|
1024
|
+
if (await runs.abortQueued(conversationId, run.id, accountId)) {
|
|
1025
|
+
await runs.ensureRunAfterCommit(conversationId);
|
|
1026
|
+
}
|
|
1027
|
+
return run.id;
|
|
1028
|
+
}
|
|
1017
1029
|
// 孤儿运行没有 worker 在听中止广播:直接写成 aborted,之后再看有没有等待中的消息要起新一轮。
|
|
1018
1030
|
if (await runs.abortIfOrphaned(conversationId, run.id, accountId)) {
|
|
1019
1031
|
await runs.ensureRunAfterCommit(conversationId);
|
|
@@ -200,6 +200,20 @@ export declare const RunContextSchema: z.ZodObject<{
|
|
|
200
200
|
person: z.ZodString;
|
|
201
201
|
}, z.core.$strip>>;
|
|
202
202
|
}, z.core.$strip>;
|
|
203
|
+
/**
|
|
204
|
+
* The most UTF-8 bytes a `plugin_context` event's text may hold (memory spec §6.1): the providers'
|
|
205
|
+
* limit, so the log takes every text a provider may return.
|
|
206
|
+
*/
|
|
207
|
+
export declare const PLUGIN_CONTEXT_MAX_BYTES: number;
|
|
208
|
+
/**
|
|
209
|
+
* `plugin_context` (memory spec §6.1): the text a `context@1` provider added at a run start. Empty
|
|
210
|
+
* text means the provider has nothing (any more); `hash` is the sha256 (hex) of `text`.
|
|
211
|
+
*/
|
|
212
|
+
export declare const PluginContextSchema: z.ZodObject<{
|
|
213
|
+
plugin: z.ZodString;
|
|
214
|
+
text: z.ZodString;
|
|
215
|
+
hash: z.ZodString;
|
|
216
|
+
}, z.core.$strip>;
|
|
203
217
|
/**
|
|
204
218
|
* 会话事件(`type` + `payload`)的判别联合。事件日志只追加,写入前用它校验 payload;
|
|
205
219
|
* 读出时按写入时的形状原样返回。
|
|
@@ -547,6 +561,13 @@ export declare const ConversationEventSchema: z.ZodDiscriminatedUnion<[z.ZodObje
|
|
|
547
561
|
person: z.ZodString;
|
|
548
562
|
}, z.core.$strip>>;
|
|
549
563
|
}, z.core.$strip>;
|
|
564
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
565
|
+
type: z.ZodLiteral<"plugin_context">;
|
|
566
|
+
payload: z.ZodObject<{
|
|
567
|
+
plugin: z.ZodString;
|
|
568
|
+
text: z.ZodString;
|
|
569
|
+
hash: z.ZodString;
|
|
570
|
+
}, z.core.$strip>;
|
|
550
571
|
}, z.core.$strip>, z.ZodObject<{
|
|
551
572
|
type: z.ZodLiteral<"tools_changed">;
|
|
552
573
|
payload: z.ZodObject<{
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DOCUMENT_MEDIA_TYPES, hasControlChars, IMAGE_MEDIA_TYPES, isAttachmentId, isSkillName, MEDIA_NAME_MAX_CHARS, } from "@capybara-hq/plugin-sdk";
|
|
1
|
+
import { CONTEXT_LIMITS, DOCUMENT_MEDIA_TYPES, hasControlChars, IMAGE_MEDIA_TYPES, isAttachmentId, isSkillName, MEDIA_NAME_MAX_CHARS, } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { AgentDefinitionSchema, DisabledSkillRef, DisabledToolRef, ModelRef, } from "../agents/definition.js";
|
|
4
4
|
import { ORG_ROLES } from "../orgs/roles.js";
|
|
@@ -228,6 +228,22 @@ export const RunContextSchema = z.object({
|
|
|
228
228
|
.optional(),
|
|
229
229
|
});
|
|
230
230
|
const ev = (type, payload) => z.object({ type: z.literal(type), payload });
|
|
231
|
+
/**
|
|
232
|
+
* The most UTF-8 bytes a `plugin_context` event's text may hold (memory spec §6.1): the providers'
|
|
233
|
+
* limit, so the log takes every text a provider may return.
|
|
234
|
+
*/
|
|
235
|
+
export const PLUGIN_CONTEXT_MAX_BYTES = CONTEXT_LIMITS.maxBytes;
|
|
236
|
+
/**
|
|
237
|
+
* `plugin_context` (memory spec §6.1): the text a `context@1` provider added at a run start. Empty
|
|
238
|
+
* text means the provider has nothing (any more); `hash` is the sha256 (hex) of `text`.
|
|
239
|
+
*/
|
|
240
|
+
export const PluginContextSchema = z.object({
|
|
241
|
+
plugin: z.string().min(1).max(64),
|
|
242
|
+
text: z.string().refine((t) => Buffer.byteLength(t, "utf8") <= PLUGIN_CONTEXT_MAX_BYTES, {
|
|
243
|
+
message: `at most ${PLUGIN_CONTEXT_MAX_BYTES} UTF-8 bytes`,
|
|
244
|
+
}),
|
|
245
|
+
hash: z.string().regex(/^[0-9a-f]{64}$/),
|
|
246
|
+
});
|
|
231
247
|
/**
|
|
232
248
|
* An agent definition frozen into a log (`subagent_started`): the current schema, with the
|
|
233
249
|
* selected and pinned skills of older logs still accepted (skills default-on spec §4: history
|
|
@@ -485,6 +501,13 @@ export const ConversationEventSchema = z.discriminatedUnion("type", [
|
|
|
485
501
|
* system message (`runContextText`); a summary covering it restores the current values.
|
|
486
502
|
*/
|
|
487
503
|
ev("run_context", RunContextSchema),
|
|
504
|
+
/**
|
|
505
|
+
* Text a `context@1` provider added at a run start (memory spec §6.1), written only when its
|
|
506
|
+
* hash differs from the plugin's previous one in the conversation. **Model-visible**: rendered
|
|
507
|
+
* at its position as a system message (`pluginContextText`, nothing for empty text); a summary
|
|
508
|
+
* covering it is followed by the latest non-empty one per plugin again.
|
|
509
|
+
*/
|
|
510
|
+
ev("plugin_context", PluginContextSchema),
|
|
488
511
|
/**
|
|
489
512
|
* The step's resolved tool set differs from the one the conversation's previous model request
|
|
490
513
|
* offered (`toolSetChange`): tool names as the model sees them, sorted. Written before that
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ConversationSource } from "@capybara-hq/plugin-sdk";
|
|
2
|
+
import type { KernelSchemaDb } from "../db/kernel-schema.js";
|
|
3
|
+
/**
|
|
4
|
+
* Where a conversation comes from (memory plugin spec §6.2), stored on its row at creation
|
|
5
|
+
* (`conversations.source`, migration 0124) and never changed. A missing conversation throws
|
|
6
|
+
* rather than guessing a source (a guess could offer a group chat what only a DM may see).
|
|
7
|
+
*/
|
|
8
|
+
export declare function conversationSourceOf(db: Pick<KernelSchemaDb, "selectFrom">, conversationId: string): Promise<ConversationSource>;
|
|
9
|
+
/** The source of a conversation created for a channel chat of `chatType`. */
|
|
10
|
+
export declare const channelSource: (chatType: "dm" | "group") => ConversationSource;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a conversation comes from (memory plugin spec §6.2), stored on its row at creation
|
|
3
|
+
* (`conversations.source`, migration 0124) and never changed. A missing conversation throws
|
|
4
|
+
* rather than guessing a source (a guess could offer a group chat what only a DM may see).
|
|
5
|
+
*/
|
|
6
|
+
export async function conversationSourceOf(db, conversationId) {
|
|
7
|
+
const row = await db
|
|
8
|
+
.selectFrom("conversations")
|
|
9
|
+
.select("source")
|
|
10
|
+
.where("id", "=", conversationId)
|
|
11
|
+
.executeTakeFirst();
|
|
12
|
+
if (row === undefined)
|
|
13
|
+
throw new Error(`conversation ${conversationId} not found`);
|
|
14
|
+
return row.source;
|
|
15
|
+
}
|
|
16
|
+
/** The source of a conversation created for a channel chat of `chatType`. */
|
|
17
|
+
export const channelSource = (chatType) => chatType === "dm" ? "channel_dm" : "channel_group";
|
|
@@ -15,3 +15,13 @@ export declare function conversationTree(db: KernelSchemaDb, conversationId: str
|
|
|
15
15
|
* `conversations.parent_conversation_id`. Empty for a root or an unknown id.
|
|
16
16
|
*/
|
|
17
17
|
export declare function conversationAncestors(db: KernelSchemaDb, conversationId: string): Promise<string[]>;
|
|
18
|
+
/**
|
|
19
|
+
* Locks the conversations `ids` and every ancestor of theirs (`FOR NO KEY UPDATE`, in the caller's
|
|
20
|
+
* transaction) tree by tree, root first: the trees in root id order, within a tree by level below
|
|
21
|
+
* the root, then id. A reply locks its root conversation and only then the sub-agents' below it
|
|
22
|
+
* (`ConversationsService.skipWaitsIn`), and a dispatch locks a parent before its child; a locker
|
|
23
|
+
* of conversations in plain id order could take a child before its root and wait for the root
|
|
24
|
+
* while such a path holds the root and waits for the child (40P01). Returns the ids locked (the
|
|
25
|
+
* requested ones and their ancestors); an unknown id is skipped.
|
|
26
|
+
*/
|
|
27
|
+
export declare function lockConversationTrees(db: KernelSchemaDb, ids: readonly string[]): Promise<string[]>;
|
|
@@ -40,3 +40,38 @@ export async function conversationAncestors(db, conversationId) {
|
|
|
40
40
|
SELECT id FROM up WHERE level > 0 ORDER BY level`.execute(db);
|
|
41
41
|
return r.rows.map((row) => row.id);
|
|
42
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Locks the conversations `ids` and every ancestor of theirs (`FOR NO KEY UPDATE`, in the caller's
|
|
45
|
+
* transaction) tree by tree, root first: the trees in root id order, within a tree by level below
|
|
46
|
+
* the root, then id. A reply locks its root conversation and only then the sub-agents' below it
|
|
47
|
+
* (`ConversationsService.skipWaitsIn`), and a dispatch locks a parent before its child; a locker
|
|
48
|
+
* of conversations in plain id order could take a child before its root and wait for the root
|
|
49
|
+
* while such a path holds the root and waits for the child (40P01). Returns the ids locked (the
|
|
50
|
+
* requested ones and their ancestors); an unknown id is skipped.
|
|
51
|
+
*/
|
|
52
|
+
export async function lockConversationTrees(db, ids) {
|
|
53
|
+
const wanted = [...new Set(ids)];
|
|
54
|
+
if (wanted.length === 0)
|
|
55
|
+
return [];
|
|
56
|
+
const r = await sql `
|
|
57
|
+
WITH RECURSIVE up(start, id, parent, level) AS (
|
|
58
|
+
SELECT c.id, c.id, c.parent_conversation_id, 0 FROM capybara.conversations c
|
|
59
|
+
WHERE c.id = ANY(${wanted}::text[])
|
|
60
|
+
UNION ALL
|
|
61
|
+
SELECT up.start, c.id, c.parent_conversation_id, up.level + 1
|
|
62
|
+
FROM capybara.conversations c JOIN up ON c.id = up.parent
|
|
63
|
+
WHERE up.level < ${TREE_MAX_LEVELS}
|
|
64
|
+
),
|
|
65
|
+
tops AS (
|
|
66
|
+
SELECT DISTINCT ON (start) start, id AS root, level AS height
|
|
67
|
+
FROM up ORDER BY start, level DESC
|
|
68
|
+
),
|
|
69
|
+
nodes AS (
|
|
70
|
+
SELECT DISTINCT up.id, tops.root, tops.height - up.level AS depth
|
|
71
|
+
FROM up JOIN tops ON tops.start = up.start
|
|
72
|
+
)
|
|
73
|
+
SELECT c.id FROM capybara.conversations c JOIN nodes n ON n.id = c.id
|
|
74
|
+
ORDER BY n.root, n.depth, n.id
|
|
75
|
+
FOR NO KEY UPDATE OF c`.execute(db);
|
|
76
|
+
return r.rows.map((row) => row.id);
|
|
77
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FileSystemHandle, ParsedTheme, SandboxTier, SkillInvocation } from "@capybara-hq/plugin-sdk";
|
|
1
|
+
import type { ConversationSource, FileSystemHandle, ParsedTheme, SandboxTier, SkillInvocation } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import type { ColumnType, Generated, Kysely } from "kysely";
|
|
3
3
|
import type { ModelRef } from "../agents/definition.js";
|
|
4
4
|
import type { ScheduleChat, ScheduleMarker, ScheduleMode, SchedulePauseReason, ScheduleStatus, ScheduleTrigger, TaskRunSkipReason, TaskRunStatus } from "../schedules/types.js";
|
|
@@ -263,6 +263,11 @@ export interface ConversationsTable {
|
|
|
263
263
|
device_id: ColumnType<string | null, string | null | undefined, string | null>;
|
|
264
264
|
/** 0113: the scheduled task that created it (`new` mode); null otherwise. */
|
|
265
265
|
scheduled_task_id: ColumnType<string | null, string | null | undefined, string | null>;
|
|
266
|
+
/**
|
|
267
|
+
* 0124: where the conversation comes from, set at creation and never changed. Required on insert
|
|
268
|
+
* (the column's `web` default is not relied on: a forgotten source must not read as web).
|
|
269
|
+
*/
|
|
270
|
+
source: ColumnType<ConversationSource, ConversationSource, never>;
|
|
266
271
|
}
|
|
267
272
|
/** 0060: an org's order of file-preview@1 previewers (UI refresh B Task 11). */
|
|
268
273
|
export interface FilePreviewersTable {
|
|
@@ -1224,8 +1229,6 @@ export interface ScheduledTaskRunsTable {
|
|
|
1224
1229
|
skip_reason: ColumnType<TaskRunSkipReason | null, TaskRunSkipReason | null | undefined, TaskRunSkipReason | null>;
|
|
1225
1230
|
error: NullableText;
|
|
1226
1231
|
missing_grants: ColumnType<string[], string | undefined, string>;
|
|
1227
|
-
/** When the scanner's repair last found this started run still in flight. */
|
|
1228
|
-
rechecked_at: NullableTs;
|
|
1229
1232
|
}
|
|
1230
1233
|
/** 0113: a scheduled task's remembered grants (same shape as `conversation_tool_grants`). */
|
|
1231
1234
|
export interface ScheduledTaskGrantsTable {
|
|
@@ -1333,7 +1336,6 @@ export interface KernelDatabase {
|
|
|
1333
1336
|
plugin_install_components: PluginInstallComponentsTable;
|
|
1334
1337
|
extension_catalog_sources: ExtensionCatalogSourcesTable;
|
|
1335
1338
|
bootstrap_runs: BootstrapRunsTable;
|
|
1336
|
-
platform_migration_runs: PlatformMigrationRunsTable;
|
|
1337
1339
|
org_notices: OrgNoticesTable;
|
|
1338
1340
|
platform_notices: PlatformNoticesTable;
|
|
1339
1341
|
plugin_scope_sync_failures: PluginScopeSyncFailuresTable;
|
|
@@ -1397,21 +1399,14 @@ export interface ExtensionClientReportsTable {
|
|
|
1397
1399
|
last_at: Ts;
|
|
1398
1400
|
last_message: string;
|
|
1399
1401
|
}
|
|
1400
|
-
/**
|
|
1401
|
-
export
|
|
1402
|
-
id: string;
|
|
1403
|
-
started_at: Ts;
|
|
1404
|
-
finished_at: TsNullable;
|
|
1405
|
-
stats: Json<Record<string, unknown>>;
|
|
1406
|
-
}
|
|
1407
|
-
/** 0082: the kinds of {@link OrgNoticesTable}. */
|
|
1408
|
-
export type OrgNoticeKind = "skill_renamed" | "item_shared" | "shared_item_updated" | "upload_confirmed" | "upload_rejected";
|
|
1402
|
+
/** 0084 (0121 dropped `skill_renamed`): the kinds of {@link OrgNoticesTable}. */
|
|
1403
|
+
export type OrgNoticeKind = "item_shared" | "shared_item_updated" | "upload_confirmed" | "upload_rejected";
|
|
1409
1404
|
/** 0082: a notice for an org's admins (an inbox item), until one of them dismisses it. */
|
|
1410
1405
|
export interface OrgNoticesTable {
|
|
1411
1406
|
id: string;
|
|
1412
1407
|
org_id: string;
|
|
1413
1408
|
kind: OrgNoticeKind;
|
|
1414
|
-
/**
|
|
1409
|
+
/** The kind's shape (`OrgNoticeDetails` in `inbox/org-notices.ts`). */
|
|
1415
1410
|
detail: Json<Record<string, unknown>>;
|
|
1416
1411
|
created_at: Ts;
|
|
1417
1412
|
dismissed_at: TsNullable;
|
|
@@ -4,9 +4,9 @@ import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
|
4
4
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
5
5
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
6
6
|
import { LockService } from "../infra/lock/lock.service.js";
|
|
7
|
+
import { OutboxService } from "../infra/outbox/outbox.service.js";
|
|
7
8
|
import { DevicePolicyService } from "../local-devices/device-policy.service.js";
|
|
8
9
|
import { MachinesService } from "../local-devices/machines.js";
|
|
9
|
-
import type { KernelLogger } from "../log.js";
|
|
10
10
|
import type { DeviceSandboxLevel } from "../runtime/context/device-paths.js";
|
|
11
11
|
import { SandboxJobs } from "../sandbox/jobs.js";
|
|
12
12
|
import { SandboxProviders } from "../sandbox/providers.js";
|
|
@@ -181,8 +181,8 @@ export interface DeviceList {
|
|
|
181
181
|
* Personal devices (spec 2026-11-07-environments-design §4.2 items 7, 12, 13; §6; §9):
|
|
182
182
|
* cloud ones on the deployment's sandbox provider and `device` ones on a connected machine; a
|
|
183
183
|
* person's devices in an org, visible and usable only by their owner (anyone else gets
|
|
184
|
-
* `device.not_found`). Deleting one soft-deletes it and
|
|
185
|
-
* `sandbox-destroy-for` job
|
|
184
|
+
* `device.not_found`). Deleting one soft-deletes it and owes the destruction of its instance and
|
|
185
|
+
* files (a `sandbox-destroy-for` job) in the same transaction.
|
|
186
186
|
*/
|
|
187
187
|
export declare class DevicesService {
|
|
188
188
|
private readonly db;
|
|
@@ -191,14 +191,17 @@ export declare class DevicesService {
|
|
|
191
191
|
private readonly providers;
|
|
192
192
|
private readonly sandbox;
|
|
193
193
|
private readonly sandboxJobs;
|
|
194
|
+
/** Owes a deleted device's destruction in the delete's transaction. */
|
|
195
|
+
private readonly outbox;
|
|
194
196
|
private readonly access;
|
|
195
197
|
private readonly displayNames;
|
|
196
|
-
private readonly log;
|
|
197
198
|
/** Reads the device relay's lease (`online`). */
|
|
198
199
|
private readonly locks;
|
|
199
200
|
private readonly machines;
|
|
200
201
|
private readonly policies;
|
|
201
|
-
constructor(db: KernelDb, txHost: TransactionHost, config: Pick<KernelConfigService, "get">, providers: Pick<SandboxProviders, "cloud" | "forName">, sandbox: Pick<SandboxService, "deviceLimits" | "limitsState">, sandboxJobs: Pick<SandboxJobs, "
|
|
202
|
+
constructor(db: KernelDb, txHost: TransactionHost, config: Pick<KernelConfigService, "get">, providers: Pick<SandboxProviders, "cloud" | "forName">, sandbox: Pick<SandboxService, "deviceLimits" | "limitsState">, sandboxJobs: Pick<SandboxJobs, "resetFiles">,
|
|
203
|
+
/** Owes a deleted device's destruction in the delete's transaction. */
|
|
204
|
+
outbox: Pick<OutboxService, "add">, access: Pick<DeviceAccess, "usable">, displayNames: Pick<ExtensionDisplayNames, "many">,
|
|
202
205
|
/** Reads the device relay's lease (`online`). */
|
|
203
206
|
locks: Pick<LockService, "holderOf">, machines: Pick<MachinesService, "revoke" | "revokeActive">, policies: Pick<DevicePolicyService, "apply" | "summary">);
|
|
204
207
|
/**
|
|
@@ -284,9 +287,9 @@ export declare class DevicesService {
|
|
|
284
287
|
/** Renames the device locked by {@link lockOwn} (`before`); see {@link rename}. */
|
|
285
288
|
private renameLocked;
|
|
286
289
|
/**
|
|
287
|
-
* Deletes one of the person's live devices: soft delete, its IM default row cleared, and
|
|
288
|
-
*
|
|
289
|
-
* destroys the instance and the files). Conversations bound to it keep pointing at it; their
|
|
290
|
+
* Deletes one of the person's live devices: soft delete, its IM default row cleared, and a
|
|
291
|
+
* `sandbox-destroy-for` job owed in the same transaction (`device-destroy-<deviceId>`; the
|
|
292
|
+
* worker waits for running commands, then destroys the instance and the files). Conversations bound to it keep pointing at it; their
|
|
290
293
|
* sandbox tools then fail with `device.not_found`. A local device's machine is
|
|
291
294
|
* revoked in the same transaction; after the commit it is told `stop { reason: "deleted" }`
|
|
292
295
|
* (while connected) and its connection is closed.
|
|
@@ -15,18 +15,17 @@ import { ApiError } from "../errors.js";
|
|
|
15
15
|
import { ExtensionDisplayNames } from "../extensions/display-name.js";
|
|
16
16
|
import { audit } from "../identity/audit.js";
|
|
17
17
|
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
18
|
-
import { afterCommit } from "../infra/database/after-commit.js";
|
|
19
18
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
20
19
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
21
20
|
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
22
21
|
import { LockService } from "../infra/lock/lock.service.js";
|
|
23
|
-
import {
|
|
22
|
+
import { OutboxService } from "../infra/outbox/outbox.service.js";
|
|
24
23
|
import { DevicePolicyService } from "../local-devices/device-policy.service.js";
|
|
25
24
|
import { deviceLeaseKey } from "../local-devices/keys.js";
|
|
26
25
|
import { MachinesService } from "../local-devices/machines.js";
|
|
27
26
|
import { deviceFeaturesOf, localPolicyOf } from "../local-devices/policy-summary.js";
|
|
28
27
|
import { readOrgSettings } from "../orgs/settings.service.js";
|
|
29
|
-
import { SandboxJobs } from "../sandbox/jobs.js";
|
|
28
|
+
import { oweDeviceDestroy, SandboxJobs } from "../sandbox/jobs.js";
|
|
30
29
|
import { isInternalProvider, LOCAL_PROVIDER, SandboxProviders } from "../sandbox/providers.js";
|
|
31
30
|
import { deviceNotFound, SandboxService } from "../sandbox/sandbox.service.js";
|
|
32
31
|
import { effectiveSandboxPolicy, readDeploymentSandboxPolicy, } from "../sandbox/tiers.js";
|
|
@@ -43,8 +42,8 @@ const nameTaken = () => new ApiError("device.name_taken", "You already have a de
|
|
|
43
42
|
* Personal devices (spec 2026-11-07-environments-design §4.2 items 7, 12, 13; §6; §9):
|
|
44
43
|
* cloud ones on the deployment's sandbox provider and `device` ones on a connected machine; a
|
|
45
44
|
* person's devices in an org, visible and usable only by their owner (anyone else gets
|
|
46
|
-
* `device.not_found`). Deleting one soft-deletes it and
|
|
47
|
-
* `sandbox-destroy-for` job
|
|
45
|
+
* `device.not_found`). Deleting one soft-deletes it and owes the destruction of its instance and
|
|
46
|
+
* files (a `sandbox-destroy-for` job) in the same transaction.
|
|
48
47
|
*/
|
|
49
48
|
let DevicesService = class DevicesService {
|
|
50
49
|
db;
|
|
@@ -53,13 +52,15 @@ let DevicesService = class DevicesService {
|
|
|
53
52
|
providers;
|
|
54
53
|
sandbox;
|
|
55
54
|
sandboxJobs;
|
|
55
|
+
outbox;
|
|
56
56
|
access;
|
|
57
57
|
displayNames;
|
|
58
|
-
log;
|
|
59
58
|
locks;
|
|
60
59
|
machines;
|
|
61
60
|
policies;
|
|
62
|
-
constructor(db, txHost, config, providers, sandbox, sandboxJobs,
|
|
61
|
+
constructor(db, txHost, config, providers, sandbox, sandboxJobs,
|
|
62
|
+
/** Owes a deleted device's destruction in the delete's transaction. */
|
|
63
|
+
outbox, access, displayNames,
|
|
63
64
|
/** Reads the device relay's lease (`online`). */
|
|
64
65
|
locks, machines, policies) {
|
|
65
66
|
this.db = db;
|
|
@@ -68,9 +69,9 @@ let DevicesService = class DevicesService {
|
|
|
68
69
|
this.providers = providers;
|
|
69
70
|
this.sandbox = sandbox;
|
|
70
71
|
this.sandboxJobs = sandboxJobs;
|
|
72
|
+
this.outbox = outbox;
|
|
71
73
|
this.access = access;
|
|
72
74
|
this.displayNames = displayNames;
|
|
73
|
-
this.log = log;
|
|
74
75
|
this.locks = locks;
|
|
75
76
|
this.machines = machines;
|
|
76
77
|
this.policies = policies;
|
|
@@ -388,9 +389,9 @@ let DevicesService = class DevicesService {
|
|
|
388
389
|
});
|
|
389
390
|
}
|
|
390
391
|
/**
|
|
391
|
-
* Deletes one of the person's live devices: soft delete, its IM default row cleared, and
|
|
392
|
-
*
|
|
393
|
-
* destroys the instance and the files). Conversations bound to it keep pointing at it; their
|
|
392
|
+
* Deletes one of the person's live devices: soft delete, its IM default row cleared, and a
|
|
393
|
+
* `sandbox-destroy-for` job owed in the same transaction (`device-destroy-<deviceId>`; the
|
|
394
|
+
* worker waits for running commands, then destroys the instance and the files). Conversations bound to it keep pointing at it; their
|
|
394
395
|
* sandbox tools then fail with `device.not_found`. A local device's machine is
|
|
395
396
|
* revoked in the same transaction; after the commit it is told `stop { reason: "deleted" }`
|
|
396
397
|
* (while connected) and its connection is closed.
|
|
@@ -417,15 +418,7 @@ let DevicesService = class DevicesService {
|
|
|
417
418
|
target: id,
|
|
418
419
|
detail: { name: before.name, isDefault: before.is_default, ...auditDetail },
|
|
419
420
|
});
|
|
420
|
-
|
|
421
|
-
try {
|
|
422
|
-
await this.sandboxJobs.destroy([id]);
|
|
423
|
-
}
|
|
424
|
-
catch (err) {
|
|
425
|
-
// the hourly destruction sweep destroys deleted devices too
|
|
426
|
-
this.log.warn("cannot schedule device destruction", { deviceId: id, err });
|
|
427
|
-
}
|
|
428
|
-
});
|
|
421
|
+
await oweDeviceDestroy(this.outbox, id, orgId);
|
|
429
422
|
}
|
|
430
423
|
/**
|
|
431
424
|
* Disconnects the machine of one of the person's own local devices (it must sign in
|
|
@@ -1021,9 +1014,9 @@ DevicesService = __decorate([
|
|
|
1021
1014
|
__param(3, Inject(SandboxProviders)),
|
|
1022
1015
|
__param(4, Inject(SandboxService)),
|
|
1023
1016
|
__param(5, Inject(SandboxJobs)),
|
|
1024
|
-
__param(6, Inject(
|
|
1025
|
-
__param(7, Inject(
|
|
1026
|
-
__param(8, Inject(
|
|
1017
|
+
__param(6, Inject(OutboxService)),
|
|
1018
|
+
__param(7, Inject(DeviceAccess)),
|
|
1019
|
+
__param(8, Inject(ExtensionDisplayNames)),
|
|
1027
1020
|
__param(9, Inject(LockService)),
|
|
1028
1021
|
__param(10, Inject(MachinesService)),
|
|
1029
1022
|
__param(11, Inject(DevicePolicyService))
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ContentBlock, type Logger, type ModelUsage, type StopReason } from "@capybara-hq/plugin-sdk";
|
|
1
|
+
import { type ContentBlock, type ConversationSource, type Logger, type ModelUsage, type StopReason } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import { type ModelRef } from "../agents/definition.js";
|
|
3
3
|
import { EventLog } from "../conversations/event-log.js";
|
|
4
4
|
import type { KernelSchemaDb } from "../db/kernel-schema.js";
|
|
@@ -98,6 +98,8 @@ export interface ReplaySeed {
|
|
|
98
98
|
evalRunId: string;
|
|
99
99
|
orgId: string;
|
|
100
100
|
agentId: string;
|
|
101
|
+
/** The original conversation's source: the replay offers the tools of this source. */
|
|
102
|
+
source: ConversationSource;
|
|
101
103
|
accountId: string;
|
|
102
104
|
conversationId: string;
|
|
103
105
|
runId: string;
|
|
@@ -121,11 +123,20 @@ export interface ReplaySeed {
|
|
|
121
123
|
runContext: {
|
|
122
124
|
payload: unknown;
|
|
123
125
|
} | undefined;
|
|
126
|
+
/**
|
|
127
|
+
* The original run's `plugin_context` events (memory spec §6.1), in log order, copied as is:
|
|
128
|
+
* replays never call context providers.
|
|
129
|
+
*/
|
|
130
|
+
pluginContexts: {
|
|
131
|
+
payload: {
|
|
132
|
+
plugin: string;
|
|
133
|
+
};
|
|
134
|
+
}[];
|
|
124
135
|
}
|
|
125
136
|
/**
|
|
126
137
|
* The seed of a full-run replay (C6): the `kind = 'eval'` conversation with the events before the
|
|
127
138
|
* original run at their original seqs, the run row and its `run_started` (and the original
|
|
128
|
-
* `skills_catalog`), in one transaction of its own.
|
|
139
|
+
* `skills_catalog`, `run_context` and `plugin_context` events), in one transaction of its own.
|
|
129
140
|
*/
|
|
130
141
|
export declare class EvalReplaySeeder {
|
|
131
142
|
private readonly db;
|
package/dist/evals/run-replay.js
CHANGED
|
@@ -18,6 +18,7 @@ import { ApiError } from "../errors.js";
|
|
|
18
18
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
19
19
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
20
20
|
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
21
|
+
import { pluginContextIdemKey } from "../runtime/context/plugin-context.js";
|
|
21
22
|
import { estimateTokens } from "../runtime/context/tokens.js";
|
|
22
23
|
import { runDeadline, tightestLimit } from "../runtime/limits.js";
|
|
23
24
|
import { RunError } from "../runtime/model-call.js";
|
|
@@ -36,7 +37,7 @@ export class ReplayClaimLost extends Error {
|
|
|
36
37
|
/**
|
|
37
38
|
* The seed of a full-run replay (C6): the `kind = 'eval'` conversation with the events before the
|
|
38
39
|
* original run at their original seqs, the run row and its `run_started` (and the original
|
|
39
|
-
* `skills_catalog`), in one transaction of its own.
|
|
40
|
+
* `skills_catalog`, `run_context` and `plugin_context` events), in one transaction of its own.
|
|
40
41
|
*/
|
|
41
42
|
let EvalReplaySeeder = class EvalReplaySeeder {
|
|
42
43
|
db;
|
|
@@ -74,6 +75,7 @@ let EvalReplaySeeder = class EvalReplaySeeder {
|
|
|
74
75
|
consumed_seq: p.consumed,
|
|
75
76
|
kind: "eval",
|
|
76
77
|
eval_run_id: evalRunId,
|
|
78
|
+
source: p.source,
|
|
77
79
|
})
|
|
78
80
|
.execute();
|
|
79
81
|
for (let i = 0; i < p.seeds.length; i += SEED_BATCH) {
|
|
@@ -133,6 +135,12 @@ let EvalReplaySeeder = class EvalReplaySeeder {
|
|
|
133
135
|
},
|
|
134
136
|
]
|
|
135
137
|
: []),
|
|
138
|
+
...p.pluginContexts.map((c) => ({
|
|
139
|
+
type: "plugin_context",
|
|
140
|
+
runId,
|
|
141
|
+
idemKey: pluginContextIdemKey(runId, c.payload.plugin),
|
|
142
|
+
payload: c.payload,
|
|
143
|
+
})),
|
|
136
144
|
]);
|
|
137
145
|
if (onSeeded && !(await onSeeded(conversationId)))
|
|
138
146
|
throw new ReplayClaimLost();
|
|
@@ -333,6 +341,9 @@ export async function replayRun(d, s, candidate, evalRunId, signal) {
|
|
|
333
341
|
const trigger = replayTrigger(start.payload, seeds, consumed);
|
|
334
342
|
const catalog = s.events.find((e) => e.runId === s.runId && e.type === "skills_catalog" && e.seq > R);
|
|
335
343
|
const runContext = s.events.find((e) => e.runId === s.runId && e.type === "run_context" && e.seq > R);
|
|
344
|
+
const pluginContexts = s.events
|
|
345
|
+
.filter((e) => e.runId === s.runId && e.type === "plugin_context" && e.seq > R)
|
|
346
|
+
.map((e) => ({ payload: e.payload }));
|
|
336
347
|
// a sub-agent's case (spec §15.3) runs the effective definition frozen in its subagent_started
|
|
337
348
|
// (the request renders that event's preamble and instructions; its tools come from it too), with
|
|
338
349
|
// only the candidate's model and params, and the logged step limit
|
|
@@ -348,12 +359,16 @@ export async function replayRun(d, s, candidate, evalRunId, signal) {
|
|
|
348
359
|
const forkRun = triggerEvent?.type === "user_message" &&
|
|
349
360
|
triggerEvent.payload.skill !== undefined &&
|
|
350
361
|
triggerEvent.payload.dispatch !== undefined;
|
|
362
|
+
// the original conversation's source (recorded in the case); a case taken before it was
|
|
363
|
+
// recorded replays as its run did by default: a web conversation, or a sub-agent's for a child
|
|
364
|
+
const source = s.source ?? (subagent === undefined ? "web" : "subagent");
|
|
351
365
|
const conversationId = randomUUID();
|
|
352
366
|
const runId = randomUUID();
|
|
353
367
|
await d.seeder.seed({
|
|
354
368
|
evalRunId,
|
|
355
369
|
orgId: s.orgId,
|
|
356
370
|
agentId: s.agentId,
|
|
371
|
+
source,
|
|
357
372
|
accountId,
|
|
358
373
|
conversationId,
|
|
359
374
|
runId,
|
|
@@ -365,6 +380,7 @@ export async function replayRun(d, s, candidate, evalRunId, signal) {
|
|
|
365
380
|
model: agent.model,
|
|
366
381
|
catalog,
|
|
367
382
|
runContext,
|
|
383
|
+
pluginContexts,
|
|
368
384
|
}, d.onSeeded);
|
|
369
385
|
await d.hooks?.afterRunSeeded?.(evalRunId, conversationId);
|
|
370
386
|
// 停止:取消 / 越限 / 熔断 / 失去认领 / 截止时间 → 中止 loop(进行中的请求立即停下,loop 以 aborted 返回)
|
|
@@ -542,6 +558,13 @@ export async function replayRun(d, s, candidate, evalRunId, signal) {
|
|
|
542
558
|
// a fork invocation's report is the recorded one: a replay never dispatches a sub-agent (A5)
|
|
543
559
|
const skillForks = recordedSkillForks(eventLog, s.events.filter((e) => e.runId === s.runId));
|
|
544
560
|
const loop = d.loop({ toolPipeline: pipeline, registry, skillForks, ...(media && { media }) });
|
|
561
|
+
// a replay never executes a tool, so the role only completes the context
|
|
562
|
+
const membership = await db
|
|
563
|
+
.selectFrom("memberships")
|
|
564
|
+
.select("role")
|
|
565
|
+
.where("org_id", "=", s.orgId)
|
|
566
|
+
.where("account_id", "=", accountId)
|
|
567
|
+
.executeTakeFirst();
|
|
545
568
|
let outcome;
|
|
546
569
|
try {
|
|
547
570
|
outcome = await loop.run({
|
|
@@ -550,6 +573,10 @@ export async function replayRun(d, s, candidate, evalRunId, signal) {
|
|
|
550
573
|
runId,
|
|
551
574
|
agentId: s.agentId,
|
|
552
575
|
triggerAccountId: accountId,
|
|
576
|
+
// the seeded conversation's source (the original's): its tools are the original's
|
|
577
|
+
source,
|
|
578
|
+
ownerAccountId: accountId,
|
|
579
|
+
role: membership?.role ?? "guest",
|
|
553
580
|
agent,
|
|
554
581
|
agentVersion: candidate.agentVersion,
|
|
555
582
|
signal: loopSignal,
|
package/dist/evals/snapshot.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ToolSpec } from "@capybara-hq/plugin-sdk";
|
|
1
|
+
import type { ConversationSource, ToolSpec } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import { type AgentDefinition } from "../agents/definition.js";
|
|
3
3
|
import type { ConversationEvent, ConversationEventType } from "../conversations/events.js";
|
|
4
4
|
import type { KernelSchemaDb } from "../db/kernel-schema.js";
|
|
@@ -21,6 +21,12 @@ export interface CaseSnapshot {
|
|
|
21
21
|
runId: string;
|
|
22
22
|
agentId: string;
|
|
23
23
|
agentVersion: number;
|
|
24
|
+
/**
|
|
25
|
+
* The original conversation's source (memory plugin spec §6.2): a full-run replay offers the
|
|
26
|
+
* tools of this source. Cases taken before it was recorded have none (replayed as `web`, or as
|
|
27
|
+
* `subagent` for a child case).
|
|
28
|
+
*/
|
|
29
|
+
source?: ConversationSource;
|
|
24
30
|
/** seq ≤ 该运行终态事件的 seq,按 seq 升序。 */
|
|
25
31
|
events: SnapshotEvent[];
|
|
26
32
|
/** 事件里各次运行(`run_started`)与本运行引用的 agent 版本 → 定义。 */
|
package/dist/evals/snapshot.js
CHANGED
|
@@ -17,7 +17,7 @@ const PAGE = 1000;
|
|
|
17
17
|
export async function takeSnapshot(db, orgId, conversationId, runId, maxBytes) {
|
|
18
18
|
const conv = await db
|
|
19
19
|
.selectFrom("conversations")
|
|
20
|
-
.select(["agent_id", "kind"])
|
|
20
|
+
.select(["agent_id", "kind", "source"])
|
|
21
21
|
.where("id", "=", conversationId)
|
|
22
22
|
.where("org_id", "=", orgId)
|
|
23
23
|
.executeTakeFirst();
|
|
@@ -126,6 +126,7 @@ export async function takeSnapshot(db, orgId, conversationId, runId, maxBytes) {
|
|
|
126
126
|
runId,
|
|
127
127
|
agentId: conv.agent_id,
|
|
128
128
|
agentVersion: run.agent_version,
|
|
129
|
+
source: conv.source,
|
|
129
130
|
events,
|
|
130
131
|
definitions,
|
|
131
132
|
toolSpecs,
|