@capybara-hq/kernel 0.1.0-rc.3 → 0.1.0-rc.4
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.controller.d.ts +9 -4
- package/dist/admin/admin.controller.js +13 -4
- package/dist/admin/extensions.controller.d.ts +8 -1
- package/dist/admin/extensions.controller.js +22 -2
- package/dist/agents/agent-lookup.d.ts +3 -0
- package/dist/agents/agent-lookup.js +10 -0
- package/dist/agents/agent-profile.service.d.ts +29 -0
- package/dist/agents/agent-profile.service.js +117 -0
- package/dist/agents/agents.controller.d.ts +93 -3
- package/dist/agents/agents.controller.js +114 -4
- package/dist/agents/agents.module.js +6 -2
- package/dist/agents/agents.service.d.ts +13 -5
- package/dist/agents/agents.service.js +30 -7
- package/dist/agents/definition.d.ts +4 -0
- package/dist/agents/definition.js +10 -0
- package/dist/agents/instruction-files.d.ts +60 -0
- package/dist/agents/instruction-files.js +111 -0
- package/dist/agents/models.controller.d.ts +9 -3
- package/dist/agents/models.controller.js +19 -4
- package/dist/agents/profile.d.ts +96 -0
- package/dist/agents/profile.js +51 -0
- package/dist/agents/readiness.controller.d.ts +26 -0
- package/dist/agents/readiness.controller.js +56 -0
- package/dist/agents/templates.js +1 -1
- package/dist/background/background.controller.d.ts +38 -0
- package/dist/background/background.controller.js +87 -0
- package/dist/background/background.module.d.ts +10 -0
- package/dist/background/background.module.js +32 -0
- package/dist/background/finish.d.ts +43 -0
- package/dist/background/finish.js +94 -0
- package/dist/background/launch-lock.d.ts +14 -0
- package/dist/background/launch-lock.js +39 -0
- package/dist/background/launch.d.ts +36 -0
- package/dist/background/launch.js +70 -0
- package/dist/background/poll.job.d.ts +53 -0
- package/dist/background/poll.job.js +182 -0
- package/dist/background/render.d.ts +23 -0
- package/dist/background/render.js +48 -0
- package/dist/background/session.d.ts +37 -0
- package/dist/background/session.js +66 -0
- package/dist/background/status.d.ts +35 -0
- package/dist/background/status.js +57 -0
- package/dist/background/stop.d.ts +62 -0
- package/dist/background/stop.js +225 -0
- package/dist/background/store.d.ts +150 -0
- package/dist/background/store.js +351 -0
- package/dist/background/store.module.d.ts +6 -0
- package/dist/background/store.module.js +22 -0
- package/dist/background/tasks.service.d.ts +67 -0
- package/dist/background/tasks.service.js +186 -0
- package/dist/background/wake-rules.d.ts +57 -0
- package/dist/background/wake-rules.js +83 -0
- package/dist/background/wake.d.ts +28 -0
- package/dist/background/wake.js +57 -0
- package/dist/catalog/claude/agents.js +2 -0
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/catalog/plugin-import.js +1 -1
- package/dist/catalog/plugin-upgrade.js +1 -1
- package/dist/channels/cards.js +1 -1
- package/dist/channels/connections.service.d.ts +16 -1
- package/dist/channels/connections.service.js +38 -4
- package/dist/channels/threads.d.ts +5 -0
- package/dist/channels/threads.js +8 -0
- package/dist/config/schema.d.ts +18 -0
- package/dist/config/schema.js +102 -0
- package/dist/conversations/access.js +2 -0
- package/dist/conversations/attachments.service.d.ts +30 -0
- package/dist/conversations/attachments.service.js +130 -5
- package/dist/conversations/conversations.controller.d.ts +36 -5
- package/dist/conversations/conversations.controller.js +62 -12
- package/dist/conversations/conversations.module.js +3 -0
- package/dist/conversations/conversations.service.d.ts +44 -2
- package/dist/conversations/conversations.service.js +140 -12
- package/dist/conversations/event-log.d.ts +1 -1
- package/dist/conversations/event-log.js +10 -1
- package/dist/conversations/events.d.ts +58 -0
- package/dist/conversations/events.js +57 -2
- package/dist/conversations/queue.d.ts +3 -1
- package/dist/conversations/queue.js +13 -2
- package/dist/conversations/queue.service.js +3 -1
- package/dist/conversations/titles.d.ts +7 -0
- package/dist/conversations/titles.js +15 -0
- package/dist/db/kernel-schema.d.ts +58 -2
- package/dist/db/pool.d.ts +5 -0
- package/dist/db/pool.js +3 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +8 -0
- package/dist/evals/run-replay.d.ts +5 -2
- package/dist/evals/run-replay.js +30 -7
- package/dist/evals/step-replay.js +12 -6
- package/dist/evals/trajectory.js +1 -1
- package/dist/extensions/admin-ops.js +2 -0
- package/dist/extensions/admin-view.d.ts +3 -1
- package/dist/extensions/admin-view.js +10 -2
- package/dist/extensions/blobs.js +1 -0
- package/dist/extensions/config-issues.d.ts +13 -1
- package/dist/extensions/config-issues.js +22 -5
- package/dist/extensions/config-json-schema.d.ts +43 -7
- package/dist/extensions/config-json-schema.js +124 -58
- package/dist/extensions/deployment-config-store.d.ts +6 -0
- package/dist/extensions/deployment-config.d.ts +50 -3
- package/dist/extensions/deployment-config.js +125 -28
- package/dist/extensions/display-name.d.ts +22 -0
- package/dist/extensions/display-name.js +77 -0
- package/dist/extensions/extensions.module.js +6 -0
- package/dist/extensions/host.d.ts +26 -1
- package/dist/extensions/host.js +82 -9
- package/dist/extensions/org-config.controller.d.ts +11 -3
- package/dist/extensions/org-config.controller.js +14 -6
- package/dist/extensions/org-config.d.ts +6 -0
- package/dist/extensions/org-config.js +12 -13
- package/dist/extensions/package.d.ts +14 -1
- package/dist/extensions/package.js +51 -2
- package/dist/extensions/permission-view.d.ts +3 -0
- package/dist/extensions/point-defaults.controller.d.ts +34 -0
- package/dist/extensions/point-defaults.controller.js +71 -0
- package/dist/extensions/point-defaults.d.ts +64 -0
- package/dist/extensions/point-defaults.js +206 -0
- package/dist/extensions/registry.d.ts +1 -1
- package/dist/extensions/registry.js +2 -0
- package/dist/http/api.module.js +8 -0
- package/dist/http/nest-logger.d.ts +6 -5
- package/dist/http/nest-logger.js +24 -10
- package/dist/identity/auth.controller.js +9 -5
- package/dist/identity/cookies.d.ts +20 -5
- package/dist/identity/cookies.js +44 -6
- package/dist/identity/me.controller.d.ts +5 -1
- package/dist/identity/me.controller.js +12 -3
- package/dist/identity/sessions.d.ts +12 -0
- package/dist/identity/sessions.js +45 -0
- package/dist/index.d.ts +1 -1
- package/dist/infra/config/kernel-config.service.js +6 -0
- package/dist/infra/database/database.module.js +1 -0
- package/dist/infra/events/kernel-events.d.ts +8 -0
- package/dist/infra/lifecycle/process-guards.js +4 -0
- package/dist/infra/logging/logging.module.d.ts +4 -2
- package/dist/infra/logging/logging.module.js +24 -6
- package/dist/infra/queue/redis-connection.d.ts +6 -0
- package/dist/infra/queue/redis-connection.js +7 -0
- package/dist/infra/redis/redis.module.js +6 -2
- package/dist/infra/tokens.d.ts +6 -1
- package/dist/infra/tokens.js +6 -1
- package/dist/log.d.ts +13 -1
- package/dist/log.js +93 -17
- package/dist/models/purposes.d.ts +23 -0
- package/dist/models/purposes.js +31 -10
- package/dist/orgs/offboarding.d.ts +3 -1
- package/dist/orgs/offboarding.js +26 -16
- package/dist/orgs/settings.service.d.ts +5 -0
- package/dist/orgs/settings.service.js +13 -2
- package/dist/roles/worker.module.js +6 -0
- package/dist/runtime/context/build.d.ts +9 -7
- package/dist/runtime/context/build.js +27 -19
- package/dist/runtime/context/claude-tools.d.ts +2 -1
- package/dist/runtime/context/claude-tools.js +5 -4
- package/dist/runtime/context/compaction.js +4 -0
- package/dist/runtime/langgraph-loop.js +3 -2
- package/dist/runtime/loop.d.ts +4 -3
- package/dist/runtime/maintenance.d.ts +4 -2
- package/dist/runtime/maintenance.js +17 -5
- package/dist/runtime/model-call.d.ts +17 -1
- package/dist/runtime/model-call.js +3 -2
- package/dist/runtime/providers.d.ts +6 -0
- package/dist/runtime/providers.js +8 -0
- package/dist/runtime/run-queue.d.ts +25 -2
- package/dist/runtime/run-queue.js +129 -33
- package/dist/runtime/run-worker.js +16 -3
- package/dist/runtime/runtime.module.js +8 -3
- package/dist/runtime/step-log.d.ts +6 -0
- package/dist/runtime/step-log.js +16 -2
- package/dist/runtime/subagents/definition.js +3 -1
- package/dist/runtime/usage-report.d.ts +2 -1
- package/dist/runtime/usage-report.js +2 -1
- package/dist/runtime/waits.d.ts +8 -2
- package/dist/runtime/waits.js +37 -11
- package/dist/sandbox/sandbox.module.js +6 -1
- package/dist/sandbox/sandbox.service.d.ts +61 -3
- package/dist/sandbox/sandbox.service.js +141 -7
- package/dist/tools/approvals.service.js +2 -0
- package/dist/tools/builtin-names.d.ts +20 -2
- package/dist/tools/builtin-names.js +29 -2
- package/dist/tools/builtins/builtin-tools.d.ts +15 -1
- package/dist/tools/builtins/builtin-tools.js +34 -2
- package/dist/tools/builtins/sandbox/bash.d.ts +1 -1
- package/dist/tools/builtins/sandbox/bash.js +230 -3
- package/dist/tools/builtins/sandbox/common.d.ts +27 -4
- package/dist/tools/builtins/sandbox/common.js +7 -3
- package/dist/tools/builtins/sandbox/deliver.js +1 -1
- package/dist/tools/builtins/sandbox/index.d.ts +2 -0
- package/dist/tools/builtins/sandbox/index.js +6 -0
- package/dist/tools/builtins/sandbox/task-output.d.ts +63 -0
- package/dist/tools/builtins/sandbox/task-output.js +304 -0
- package/dist/tools/builtins/sandbox/task-stop.d.ts +10 -0
- package/dist/tools/builtins/sandbox/task-stop.js +112 -0
- package/dist/tools/builtins/web/errors.d.ts +8 -0
- package/dist/tools/builtins/web/errors.js +53 -0
- package/dist/tools/builtins/web/extract.d.ts +62 -0
- package/dist/tools/builtins/web/extract.js +267 -0
- package/dist/tools/builtins/web/fetch.d.ts +55 -0
- package/dist/tools/builtins/web/fetch.js +174 -0
- package/dist/tools/builtins/web/index.d.ts +13 -0
- package/dist/tools/builtins/web/index.js +12 -0
- package/dist/tools/builtins/web/page-cache.d.ts +25 -0
- package/dist/tools/builtins/web/page-cache.js +77 -0
- package/dist/tools/builtins/web/search.d.ts +25 -0
- package/dist/tools/builtins/web/search.js +209 -0
- package/dist/tools/builtins/web/url.d.ts +17 -0
- package/dist/tools/builtins/web/url.js +43 -0
- package/dist/tools/oauth/flow-paths.d.ts +1 -1
- package/dist/tools/oauth/flow-paths.js +1 -1
- package/dist/tools/pipeline.js +15 -0
- package/dist/tools/registry.d.ts +22 -3
- package/dist/tools/registry.js +26 -11
- package/dist/tools/tools.module.js +12 -0
- package/dist/tools/validate.js +1 -1
- package/package.json +3 -3
- package/src/migrations/0064_account_last_org.sql +7 -0
- package/src/migrations/0065_conversation_archive.sql +8 -0
- package/src/migrations/0066_extension_display_name.sql +4 -0
- package/src/migrations/0067_agent_profiles.sql +20 -0
- package/src/migrations/0068_agent_avatar_uploads.sql +7 -0
- package/src/migrations/0069_attachments_agent_idx.sql +3 -0
- package/src/migrations/0070_background_tasks.sql +37 -0
- package/src/migrations/0071_background_tasks_reserve.sql +6 -0
- package/src/migrations/0072_background_tasks_stopping.sql +5 -0
- package/src/migrations/0073_org_extension_point_defaults.sql +11 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { KernelSchemaDb } from "../db/kernel-schema.js";
|
|
2
|
+
/**
|
|
3
|
+
* The wake rules of a finished background task (spec 2026-11-05-background-bash-design §3), read
|
|
4
|
+
* by `RunQueue.ensureRun` in its transaction: every path that may start a run (a finish, the end of
|
|
5
|
+
* a run, the delayed wake job, a page opening) applies the same rules. `BackgroundWake` is what
|
|
6
|
+
* calls `ensureRun` when a task finishes.
|
|
7
|
+
*/
|
|
8
|
+
/** No two wakes of one conversation start within this window. */
|
|
9
|
+
export declare const WAKE_WINDOW_MS = 60000;
|
|
10
|
+
/** The delayed job that wakes a conversation at the end of its window (`cap-kernel-maintenance`). */
|
|
11
|
+
export declare const BG_WAKE_JOB = "bg-wake";
|
|
12
|
+
/**
|
|
13
|
+
* Its job id: one job per conversation and window end (no `:`). `retry` > 0 is a job that fired
|
|
14
|
+
* before the window ended on the database's clock and scheduled itself again: a distinct id, since
|
|
15
|
+
* BullMQ ignores an add under the id of the job still running.
|
|
16
|
+
*/
|
|
17
|
+
export declare const bgWakeJobId: (conversationId: string, windowEnd: number, retry?: number) => string;
|
|
18
|
+
export interface BgWakeJobData {
|
|
19
|
+
conversationId: string;
|
|
20
|
+
/** How many times this window's wake fired early (absent: none). */
|
|
21
|
+
retry?: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* What a conversation's unconsumed finishes decide:
|
|
25
|
+
* - `wake`: start a run whose boundary is the finish at `seq`, as `triggerAccountId`;
|
|
26
|
+
* - `later`: a wake started less than {@link WAKE_WINDOW_MS} ago; the window ends at `windowEnd`
|
|
27
|
+
* (epoch ms, the database's clock), `delayMs` from now;
|
|
28
|
+
* - null: nothing wakes the conversation.
|
|
29
|
+
*/
|
|
30
|
+
export type WakeDecision = {
|
|
31
|
+
kind: "wake";
|
|
32
|
+
seq: number;
|
|
33
|
+
triggerAccountId: string;
|
|
34
|
+
} | {
|
|
35
|
+
kind: "later";
|
|
36
|
+
windowEnd: number;
|
|
37
|
+
delayMs: number;
|
|
38
|
+
} | null;
|
|
39
|
+
/**
|
|
40
|
+
* The wake decision for a chat conversation that is idle, whose queue is not paused and has no
|
|
41
|
+
* unconsumed message (the caller checked these under the conversation's row lock). A finish
|
|
42
|
+
* wakes it when:
|
|
43
|
+
* - it is after `consumed`, not `reported` (a tool call already told the model) and has no
|
|
44
|
+
* `stoppedBy` (`"person"`: that person is looking at the conversation; `"deletion"`: the
|
|
45
|
+
* conversation is being deleted);
|
|
46
|
+
* - it belongs to a task of this conversation (the join on `conversation_id` reads the task's
|
|
47
|
+
* trigger account). A sub-agent's task is excluded by the conversation's kind, not here: its
|
|
48
|
+
* finish is in the child's log, and `ensureRun` never wakes a non-chat conversation;
|
|
49
|
+
* - the wake setting is on (the org's override, else `deploymentOn`);
|
|
50
|
+
* - the task's trigger account can still send to the conversation (`canSend`);
|
|
51
|
+
* - no other wake started in the last {@link WAKE_WINDOW_MS} (else `later`).
|
|
52
|
+
* The boundary is the newest finish whose trigger account can send.
|
|
53
|
+
*/
|
|
54
|
+
export declare function wakeDecisionIn(t: KernelSchemaDb, conv: {
|
|
55
|
+
id: string;
|
|
56
|
+
orgId: string;
|
|
57
|
+
}, consumed: number, deploymentOn: boolean, canSend: (accountId: string) => Promise<boolean>): Promise<WakeDecision>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { sql } from "kysely";
|
|
2
|
+
import { effectiveWakeOnBackgroundTask, readOrgSettings } from "../orgs/settings.service.js";
|
|
3
|
+
/**
|
|
4
|
+
* The wake rules of a finished background task (spec 2026-11-05-background-bash-design §3), read
|
|
5
|
+
* by `RunQueue.ensureRun` in its transaction: every path that may start a run (a finish, the end of
|
|
6
|
+
* a run, the delayed wake job, a page opening) applies the same rules. `BackgroundWake` is what
|
|
7
|
+
* calls `ensureRun` when a task finishes.
|
|
8
|
+
*/
|
|
9
|
+
/** No two wakes of one conversation start within this window. */
|
|
10
|
+
export const WAKE_WINDOW_MS = 60_000;
|
|
11
|
+
/** The delayed job that wakes a conversation at the end of its window (`cap-kernel-maintenance`). */
|
|
12
|
+
export const BG_WAKE_JOB = "bg-wake";
|
|
13
|
+
/**
|
|
14
|
+
* Its job id: one job per conversation and window end (no `:`). `retry` > 0 is a job that fired
|
|
15
|
+
* before the window ended on the database's clock and scheduled itself again: a distinct id, since
|
|
16
|
+
* BullMQ ignores an add under the id of the job still running.
|
|
17
|
+
*/
|
|
18
|
+
export const bgWakeJobId = (conversationId, windowEnd, retry = 0) => `bg-wake-${conversationId}-${windowEnd}${retry > 0 ? `-r${retry}` : ""}`;
|
|
19
|
+
/** How many unconsumed finishes are tried for a trigger account that can still send. */
|
|
20
|
+
const CANDIDATES = 20;
|
|
21
|
+
/**
|
|
22
|
+
* The wake decision for a chat conversation that is idle, whose queue is not paused and has no
|
|
23
|
+
* unconsumed message (the caller checked these under the conversation's row lock). A finish
|
|
24
|
+
* wakes it when:
|
|
25
|
+
* - it is after `consumed`, not `reported` (a tool call already told the model) and has no
|
|
26
|
+
* `stoppedBy` (`"person"`: that person is looking at the conversation; `"deletion"`: the
|
|
27
|
+
* conversation is being deleted);
|
|
28
|
+
* - it belongs to a task of this conversation (the join on `conversation_id` reads the task's
|
|
29
|
+
* trigger account). A sub-agent's task is excluded by the conversation's kind, not here: its
|
|
30
|
+
* finish is in the child's log, and `ensureRun` never wakes a non-chat conversation;
|
|
31
|
+
* - the wake setting is on (the org's override, else `deploymentOn`);
|
|
32
|
+
* - the task's trigger account can still send to the conversation (`canSend`);
|
|
33
|
+
* - no other wake started in the last {@link WAKE_WINDOW_MS} (else `later`).
|
|
34
|
+
* The boundary is the newest finish whose trigger account can send.
|
|
35
|
+
*/
|
|
36
|
+
export async function wakeDecisionIn(t, conv, consumed, deploymentOn, canSend) {
|
|
37
|
+
const candidates = await t
|
|
38
|
+
.selectFrom("conversation_events as e")
|
|
39
|
+
.innerJoin("background_tasks as b", (j) => j
|
|
40
|
+
.on(sql `b.id = e.payload ->> 'taskId'`)
|
|
41
|
+
.onRef("b.conversation_id", "=", "e.conversation_id"))
|
|
42
|
+
.select(["e.seq", "b.trigger_account_id"])
|
|
43
|
+
.where("e.conversation_id", "=", conv.id)
|
|
44
|
+
.where("e.type", "=", "background_task_finished")
|
|
45
|
+
.where("e.seq", ">", String(consumed))
|
|
46
|
+
.where(sql `(e.payload ->> 'reported')::boolean = false`)
|
|
47
|
+
.where(sql `e.payload ->> 'stoppedBy' IS NULL`)
|
|
48
|
+
.orderBy("e.seq", "desc")
|
|
49
|
+
.limit(CANDIDATES)
|
|
50
|
+
.execute();
|
|
51
|
+
if (candidates.length === 0)
|
|
52
|
+
return null;
|
|
53
|
+
const org = (await readOrgSettings(t, conv.orgId)).wakeOnBackgroundTask;
|
|
54
|
+
if (!effectiveWakeOnBackgroundTask(deploymentOn, org))
|
|
55
|
+
return null;
|
|
56
|
+
let boundary;
|
|
57
|
+
for (const c of candidates) {
|
|
58
|
+
if (await canSend(c.trigger_account_id)) {
|
|
59
|
+
boundary = { seq: Number(c.seq), triggerAccountId: c.trigger_account_id };
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (boundary === undefined)
|
|
64
|
+
return null;
|
|
65
|
+
// the window starts when the last wake run was created (PG, so every replica reads the same)
|
|
66
|
+
const last = await t
|
|
67
|
+
.selectFrom("runs as r")
|
|
68
|
+
.innerJoin("conversation_events as w", (j) => j.onRef("w.conversation_id", "=", "r.conversation_id").onRef("w.seq", "=", "r.trigger_seq"))
|
|
69
|
+
.select([
|
|
70
|
+
sql `floor(extract(epoch from max(r.created_at)) * 1000)::bigint`.as("started"),
|
|
71
|
+
sql `floor(extract(epoch from now()) * 1000)::bigint`.as("now"),
|
|
72
|
+
])
|
|
73
|
+
.where("r.conversation_id", "=", conv.id)
|
|
74
|
+
.where("w.type", "=", "background_task_finished")
|
|
75
|
+
.executeTakeFirst();
|
|
76
|
+
if (last?.started != null) {
|
|
77
|
+
const windowEnd = Number(last.started) + WAKE_WINDOW_MS;
|
|
78
|
+
const now = Number(last.now);
|
|
79
|
+
if (windowEnd > now)
|
|
80
|
+
return { kind: "later", windowEnd, delayMs: windowEnd - now };
|
|
81
|
+
}
|
|
82
|
+
return { kind: "wake", ...boundary };
|
|
83
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Logger } from "@capybara-hq/plugin-sdk";
|
|
2
|
+
import { type OnApplicationShutdown } from "@nestjs/common";
|
|
3
|
+
import { KernelEvents } from "../infra/events/kernel-events.js";
|
|
4
|
+
import { RunQueue } from "../runtime/run-queue.js";
|
|
5
|
+
/**
|
|
6
|
+
* Wakes an idle conversation when one of its background tasks finished (spec
|
|
7
|
+
* 2026-11-05-background-bash-design §3). {@link BackgroundTaskFinisher} announces the committed
|
|
8
|
+
* finish (`background_task.finished`, an in-process domain event: the finisher's module sits
|
|
9
|
+
* below the runtime); {@link onFinished} asks the run queue for a run. The wake rules themselves
|
|
10
|
+
* (own task, not reported, the wake setting, the queue not paused, the trigger account can still
|
|
11
|
+
* send, the 60 s window and its delayed `bg-wake` job) are applied by `RunQueue.ensureRun` in its
|
|
12
|
+
* transaction (`wakeDecisionIn`, ./wake-rules.ts), so a run's end, the delayed job and a page
|
|
13
|
+
* opening apply the same rules. Provided by `RuntimeModule`.
|
|
14
|
+
*/
|
|
15
|
+
export declare class BackgroundWake implements OnApplicationShutdown {
|
|
16
|
+
private readonly runs;
|
|
17
|
+
private readonly log;
|
|
18
|
+
/** Drops the subscription (the events may outlive this instance). */
|
|
19
|
+
private readonly unsubscribe;
|
|
20
|
+
constructor(runs: Pick<RunQueue, "ensureRun">, events: KernelEvents, log: Pick<Logger, "warn">);
|
|
21
|
+
/**
|
|
22
|
+
* A task of `conversationId` finished (after the commit): starts a wake run when the rules
|
|
23
|
+
* allow it, or schedules one at the end of the wake window. A run in progress absorbs the finish
|
|
24
|
+
* at its next step instead, or its end starts the wake. Never throws.
|
|
25
|
+
*/
|
|
26
|
+
onFinished(conversationId: string): Promise<void>;
|
|
27
|
+
onApplicationShutdown(): void;
|
|
28
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
|
+
};
|
|
10
|
+
import { Inject, Injectable } from "@nestjs/common";
|
|
11
|
+
import { KernelEvents } from "../infra/events/kernel-events.js";
|
|
12
|
+
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
13
|
+
import { RunQueue } from "../runtime/run-queue.js";
|
|
14
|
+
/**
|
|
15
|
+
* Wakes an idle conversation when one of its background tasks finished (spec
|
|
16
|
+
* 2026-11-05-background-bash-design §3). {@link BackgroundTaskFinisher} announces the committed
|
|
17
|
+
* finish (`background_task.finished`, an in-process domain event: the finisher's module sits
|
|
18
|
+
* below the runtime); {@link onFinished} asks the run queue for a run. The wake rules themselves
|
|
19
|
+
* (own task, not reported, the wake setting, the queue not paused, the trigger account can still
|
|
20
|
+
* send, the 60 s window and its delayed `bg-wake` job) are applied by `RunQueue.ensureRun` in its
|
|
21
|
+
* transaction (`wakeDecisionIn`, ./wake-rules.ts), so a run's end, the delayed job and a page
|
|
22
|
+
* opening apply the same rules. Provided by `RuntimeModule`.
|
|
23
|
+
*/
|
|
24
|
+
let BackgroundWake = class BackgroundWake {
|
|
25
|
+
runs;
|
|
26
|
+
log;
|
|
27
|
+
/** Drops the subscription (the events may outlive this instance). */
|
|
28
|
+
unsubscribe;
|
|
29
|
+
constructor(runs, events, log) {
|
|
30
|
+
this.runs = runs;
|
|
31
|
+
this.log = log;
|
|
32
|
+
this.unsubscribe = events.on("background_task.finished", ({ conversationId }) => this.onFinished(conversationId));
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A task of `conversationId` finished (after the commit): starts a wake run when the rules
|
|
36
|
+
* allow it, or schedules one at the end of the wake window. A run in progress absorbs the finish
|
|
37
|
+
* at its next step instead, or its end starts the wake. Never throws.
|
|
38
|
+
*/
|
|
39
|
+
async onFinished(conversationId) {
|
|
40
|
+
try {
|
|
41
|
+
await this.runs.ensureRun(conversationId);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
this.log.warn("background task wake failed", { conversationId, err });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
onApplicationShutdown() {
|
|
48
|
+
this.unsubscribe();
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
BackgroundWake = __decorate([
|
|
52
|
+
Injectable(),
|
|
53
|
+
__param(0, Inject(RunQueue)),
|
|
54
|
+
__param(1, Inject(KernelEvents)),
|
|
55
|
+
__param(2, Inject(KERNEL_LOGGER))
|
|
56
|
+
], BackgroundWake);
|
|
57
|
+
export { BackgroundWake };
|
|
@@ -26,6 +26,8 @@ const TOOL_MAP = {
|
|
|
26
26
|
Agent: "Agent",
|
|
27
27
|
Task: "Agent",
|
|
28
28
|
TodoWrite: "plugin__todo__write",
|
|
29
|
+
WebFetch: "WebFetch",
|
|
30
|
+
WebSearch: "WebSearch",
|
|
29
31
|
};
|
|
30
32
|
/** Claude ignores these for plugin agents: `not_applicable`. */
|
|
31
33
|
const NOT_APPLICABLE = new Set(["permissionMode", "hooks", "mcpServers"]);
|
|
@@ -164,7 +164,7 @@ export declare function headsToResolve(entries: readonly MarketplaceEntry[], imp
|
|
|
164
164
|
/** The host versions (an entry's `minKernel` is judged against them). */
|
|
165
165
|
export declare const HOST_VERSIONS: {
|
|
166
166
|
readonly kernel: string;
|
|
167
|
-
readonly sdk: "0.1.0-rc.
|
|
167
|
+
readonly sdk: "0.1.0-rc.2";
|
|
168
168
|
};
|
|
169
169
|
/** Whether this deployment satisfies the entry's `minKernel`. */
|
|
170
170
|
export declare const entryCompatible: (e: Pick<MarketplaceEntry, "minKernel">) => boolean;
|
|
@@ -33,8 +33,8 @@ import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
|
33
33
|
import { packPackageTgz } from "../packages/tgz.js";
|
|
34
34
|
import { checkSkillFiles, SkillArchiveError } from "../skills/package.js";
|
|
35
35
|
import { StorageService } from "../storage/storage.service.js";
|
|
36
|
+
import { canonicalJson } from "../tools/canonical-json.js";
|
|
36
37
|
import { endpointCredentialIssues } from "../tools/endpoint.js";
|
|
37
|
-
import { canonicalJson } from "../tools/registry.js";
|
|
38
38
|
import { findConnectorIn } from "./catalog-content.js";
|
|
39
39
|
import { assertPluginNameOwner, catalogObjectPrefix, itemView, permissionsMismatch, precheckCatalogImport, putCatalogFiles, templateProblems, writeCatalogVersion, } from "./catalog-items.js";
|
|
40
40
|
import { CLAUDE_PLUGIN_FILE, claudeContentDigest, } from "./claude/manifest.js";
|
|
@@ -24,8 +24,8 @@ import { OrgLock } from "../orgs/org-lock.js";
|
|
|
24
24
|
import { packagePath } from "../packages/tgz.js";
|
|
25
25
|
import { SkillsService, skillConflictError } from "../skills/skills.service.js";
|
|
26
26
|
import { StorageService } from "../storage/storage.service.js";
|
|
27
|
+
import { canonicalJson } from "../tools/canonical-json.js";
|
|
27
28
|
import { ConnectorsService } from "../tools/connectors.service.js";
|
|
28
|
-
import { canonicalJson } from "../tools/registry.js";
|
|
29
29
|
import { getPath, lockOrgForActions, orgTemplateView, readCatalogAgentTemplates, readCatalogFiles, readCatalogTemplates, requiredActions, setPath, skillOf, templatePluginProblem, } from "./catalog-content.js";
|
|
30
30
|
import { catalogSkillNames, InstallFailure, journalEntries, missingTemplateInput, PluginInstaller, planActions, planSkillConflicts, resolveDependencies, under, } from "./plugin-install.js";
|
|
31
31
|
import { assertItemInstallable, CatalogRequestsService } from "./requests.service.js";
|
package/dist/channels/cards.js
CHANGED
|
@@ -83,7 +83,7 @@ export function webBase(publicUrl) {
|
|
|
83
83
|
/** 会话的网页链接(与审批详情的 `conversationPath` 一致:`/chat/<id>`,不含语言前缀)。 */
|
|
84
84
|
export const conversationUrl = (base, conversationId) => `${base}/chat/${conversationId}`;
|
|
85
85
|
/** 个人连接页里某个连接器的位置(与网页 ConnectCard 的链接一致)。 */
|
|
86
|
-
export const connectUrl = (base, connectorId) => `${base}/
|
|
86
|
+
export const connectUrl = (base, connectorId) => `${base}/account/connections#connector-${encodeURIComponent(connectorId)}`;
|
|
87
87
|
/**
|
|
88
88
|
* The tool as the card names it (tool naming spec §7): a builtin by its name; a connector or
|
|
89
89
|
* extension tool as `<display name> [<tool>]` (the connector's name, the plugin's display name in
|
|
@@ -106,6 +106,8 @@ export declare class ChannelConnections implements OnApplicationShutdown {
|
|
|
106
106
|
private readonly log;
|
|
107
107
|
readonly txHost: TransactionHost;
|
|
108
108
|
private readonly unsubscribe;
|
|
109
|
+
/** `<plugin>@<version>` whose invalid connection hints were warned about (a log dedupe only). */
|
|
110
|
+
private readonly hintsWarned;
|
|
109
111
|
constructor(db: KernelDb,
|
|
110
112
|
/** The org row lock (orgs/org-lock.ts): joins the transaction of the write. */
|
|
111
113
|
orgLock: Pick<OrgLock, "lockAndAuthorize">, keyring: Keyring, registry: Pick<ExtensionRegistry, "list">, bus: ExtensionContext["bus"], redis: Pick<Redis, "del">,
|
|
@@ -122,8 +124,21 @@ export declare class ChannelConnections implements OnApplicationShutdown {
|
|
|
122
124
|
private channelEntry;
|
|
123
125
|
/** The extension is not enabled in this organization -> `extension.not_enabled`. */
|
|
124
126
|
private assertEnabled;
|
|
125
|
-
/**
|
|
127
|
+
/**
|
|
128
|
+
* The connection schema's form JSON Schema (secret fields carry `x-secret`, no values), a copy.
|
|
129
|
+
* Never throws: when even the plain conversion fails, an empty object schema (a save is still
|
|
130
|
+
* validated here).
|
|
131
|
+
*/
|
|
126
132
|
jsonSchema(plugin: string): JsonSchema;
|
|
133
|
+
/**
|
|
134
|
+
* The connection schema's form JSON Schema; invalid hints are stripped and warned about once per
|
|
135
|
+
* `<plugin>@<version>` on this replica (spec §4.1: the connection level is checked at its first
|
|
136
|
+
* conversion, not at load). Never throws: a hint conversion that throws serves the plain schema,
|
|
137
|
+
* else null, with one warning per schema.
|
|
138
|
+
*/
|
|
139
|
+
private formSchema;
|
|
140
|
+
/** The loaded version of a channel extension, for warnings ("unknown" when not provided). */
|
|
141
|
+
private versionOf;
|
|
127
142
|
private parse;
|
|
128
143
|
private verify;
|
|
129
144
|
private reportSecrets;
|
|
@@ -16,7 +16,7 @@ import { queryEnabledPlugins } from "../ctx/create-ctx.js";
|
|
|
16
16
|
import { createSecrets } from "../ctx/secrets.js";
|
|
17
17
|
import { ApiError } from "../errors.js";
|
|
18
18
|
import { toIssues } from "../extensions/config-issues.js";
|
|
19
|
-
import {
|
|
19
|
+
import { configMessages, describeConfigUiProblems, safeConfigFormSchema, } from "../extensions/config-json-schema.js";
|
|
20
20
|
import { enabledExtensionsByOrg } from "../extensions/enabled.js";
|
|
21
21
|
import { ExtensionRegistry } from "../extensions/registry.js";
|
|
22
22
|
import { audit } from "../identity/audit.js";
|
|
@@ -121,6 +121,8 @@ let ChannelConnections = class ChannelConnections {
|
|
|
121
121
|
log;
|
|
122
122
|
txHost;
|
|
123
123
|
unsubscribe;
|
|
124
|
+
/** `<plugin>@<version>` whose invalid connection hints were warned about (a log dedupe only). */
|
|
125
|
+
hintsWarned = new Set();
|
|
124
126
|
constructor(db,
|
|
125
127
|
/** The org row lock (orgs/org-lock.ts): joins the transaction of the write. */
|
|
126
128
|
orgLock, keyring, registry, bus, redis,
|
|
@@ -171,14 +173,46 @@ let ChannelConnections = class ChannelConnections {
|
|
|
171
173
|
throw new ApiError("extension.not_enabled", `Extension ${plugin} is not enabled for this organization`);
|
|
172
174
|
}
|
|
173
175
|
}
|
|
174
|
-
/**
|
|
176
|
+
/**
|
|
177
|
+
* The connection schema's form JSON Schema (secret fields carry `x-secret`, no values), a copy.
|
|
178
|
+
* Never throws: when even the plain conversion fails, an empty object schema (a save is still
|
|
179
|
+
* validated here).
|
|
180
|
+
*/
|
|
175
181
|
jsonSchema(plugin) {
|
|
176
|
-
|
|
182
|
+
const entry = this.channelEntry(plugin);
|
|
183
|
+
const r = this.formSchema(plugin, entry.version, entry.impl.connectionSchema);
|
|
184
|
+
return r ? structuredClone(r.schema) : { type: "object" };
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* The connection schema's form JSON Schema; invalid hints are stripped and warned about once per
|
|
188
|
+
* `<plugin>@<version>` on this replica (spec §4.1: the connection level is checked at its first
|
|
189
|
+
* conversion, not at load). Never throws: a hint conversion that throws serves the plain schema,
|
|
190
|
+
* else null, with one warning per schema.
|
|
191
|
+
*/
|
|
192
|
+
formSchema(plugin, version, schema) {
|
|
193
|
+
const r = safeConfigFormSchema(schema, "connection", (err, fallback) => this.log.warn("the channel connection form schema could not be built with its UI hints; hints dropped", { plugin, version, schema: fallback, err }));
|
|
194
|
+
const key = `${plugin}@${version}`;
|
|
195
|
+
if (r && r.problems.length > 0 && !this.hintsWarned.has(key)) {
|
|
196
|
+
this.hintsWarned.add(key);
|
|
197
|
+
this.log.warn("extension config UI hints are invalid and ignored", {
|
|
198
|
+
plugin,
|
|
199
|
+
version,
|
|
200
|
+
detail: describeConfigUiProblems("connection", r.problems),
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
return r;
|
|
204
|
+
}
|
|
205
|
+
/** The loaded version of a channel extension, for warnings ("unknown" when not provided). */
|
|
206
|
+
versionOf(plugin) {
|
|
207
|
+
return (this.registry
|
|
208
|
+
.list("channel")
|
|
209
|
+
.find((x) => x.plugin === plugin && x.capability === CHANNEL_EXTENSION_POINT)?.version ??
|
|
210
|
+
"unknown");
|
|
177
211
|
}
|
|
178
212
|
parse(plugin, schema, config) {
|
|
179
213
|
const r = schema.safeParse(config);
|
|
180
214
|
if (!r.success) {
|
|
181
|
-
throw new ApiError("extension.config_invalid", `Invalid channel connection config for extension ${plugin}`, toIssues(r.error));
|
|
215
|
+
throw new ApiError("extension.config_invalid", `Invalid channel connection config for extension ${plugin}`, toIssues(r.error, configMessages(this.formSchema(plugin, this.versionOf(plugin), schema)?.schema)));
|
|
182
216
|
}
|
|
183
217
|
return r.data;
|
|
184
218
|
}
|
|
@@ -48,3 +48,8 @@ export declare function findThread(db: KernelSchemaDb, k: ThreadKey): Promise<st
|
|
|
48
48
|
export declare function insertThread(db: KernelSchemaDb, k: ThreadKey, conversationId: string): Promise<string>;
|
|
49
49
|
/** 私聊 `/new`:归档当前话题行(下一条消息新建对话)。返回是否归档了一行。 */
|
|
50
50
|
export declare function archiveThread(db: KernelSchemaDb, k: ThreadKey): Promise<boolean>;
|
|
51
|
+
/**
|
|
52
|
+
* Drops a conversation's channel bindings (its thread rows and its delivery target), so a deleted
|
|
53
|
+
* conversation neither receives inbound messages nor posts to the chat any more.
|
|
54
|
+
*/
|
|
55
|
+
export declare function unbindConversation(db: KernelSchemaDb, conversationId: string): Promise<void>;
|
package/dist/channels/threads.js
CHANGED
|
@@ -160,3 +160,11 @@ export async function archiveThread(db, k) {
|
|
|
160
160
|
.executeTakeFirst();
|
|
161
161
|
return r.numUpdatedRows > 0n;
|
|
162
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* Drops a conversation's channel bindings (its thread rows and its delivery target), so a deleted
|
|
165
|
+
* conversation neither receives inbound messages nor posts to the chat any more.
|
|
166
|
+
*/
|
|
167
|
+
export async function unbindConversation(db, conversationId) {
|
|
168
|
+
await db.deleteFrom("channel_threads").where("conversation_id", "=", conversationId).execute();
|
|
169
|
+
await db.deleteFrom("channel_deliveries").where("conversation_id", "=", conversationId).execute();
|
|
170
|
+
}
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -51,6 +51,7 @@ export declare const KernelConfigSchema: z.ZodObject<{
|
|
|
51
51
|
url: z.ZodString;
|
|
52
52
|
poolMax: z.ZodDefault<z.ZodNumber>;
|
|
53
53
|
connectTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
54
|
+
idleTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
54
55
|
}, z.core.$strip>>;
|
|
55
56
|
redis: z.ZodPrefault<z.ZodObject<{
|
|
56
57
|
url: z.ZodString;
|
|
@@ -119,6 +120,7 @@ export declare const KernelConfigSchema: z.ZodObject<{
|
|
|
119
120
|
}, z.core.$strip>>;
|
|
120
121
|
runtime: z.ZodDefault<z.ZodObject<{
|
|
121
122
|
maxSteps: z.ZodDefault<z.ZodNumber>;
|
|
123
|
+
wakeOnBackgroundTask: z.ZodDefault<z.ZodBoolean>;
|
|
122
124
|
maxRunSeconds: z.ZodDefault<z.ZodNumber>;
|
|
123
125
|
streamIdleMs: z.ZodDefault<z.ZodNumber>;
|
|
124
126
|
maxOutputTokens: z.ZodDefault<z.ZodNumber>;
|
|
@@ -179,11 +181,27 @@ export declare const KernelConfigSchema: z.ZodObject<{
|
|
|
179
181
|
maxDispatchesPerRun: z.ZodDefault<z.ZodNumber>;
|
|
180
182
|
maxTokensPerRun: z.ZodDefault<z.ZodNumber>;
|
|
181
183
|
}, z.core.$strip>>;
|
|
184
|
+
web: z.ZodPrefault<z.ZodObject<{
|
|
185
|
+
search: z.ZodPrefault<z.ZodObject<{
|
|
186
|
+
maxResults: z.ZodDefault<z.ZodNumber>;
|
|
187
|
+
}, z.core.$strip>>;
|
|
188
|
+
fetch: z.ZodPrefault<z.ZodObject<{
|
|
189
|
+
maxPageChars: z.ZodDefault<z.ZodNumber>;
|
|
190
|
+
maxAnswerTokens: z.ZodDefault<z.ZodNumber>;
|
|
191
|
+
cacheSeconds: z.ZodDefault<z.ZodNumber>;
|
|
192
|
+
timeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
193
|
+
}, z.core.$strip>>;
|
|
194
|
+
}, z.core.$strip>>;
|
|
182
195
|
sandbox: z.ZodPrefault<z.ZodObject<{
|
|
183
196
|
image: z.ZodDefault<z.ZodString>;
|
|
184
197
|
lockWaitSeconds: z.ZodDefault<z.ZodNumber>;
|
|
185
198
|
maxConcurrentCommands: z.ZodDefault<z.ZodNumber>;
|
|
186
199
|
deliverMaxBytes: z.ZodDefault<z.ZodNumber>;
|
|
200
|
+
background: z.ZodPrefault<z.ZodObject<{
|
|
201
|
+
maxTasks: z.ZodDefault<z.ZodNumber>;
|
|
202
|
+
maxSeconds: z.ZodDefault<z.ZodNumber>;
|
|
203
|
+
pollSeconds: z.ZodDefault<z.ZodNumber>;
|
|
204
|
+
}, z.core.$strip>>;
|
|
187
205
|
limits: z.ZodPrefault<z.ZodObject<{
|
|
188
206
|
cpu: z.ZodDefault<z.ZodNumber>;
|
|
189
207
|
memoryMb: z.ZodDefault<z.ZodNumber>;
|
package/dist/config/schema.js
CHANGED
|
@@ -179,6 +179,14 @@ export const KernelConfigSchema = z.object({
|
|
|
179
179
|
.positive()
|
|
180
180
|
.default(5000)
|
|
181
181
|
.meta(meta({ description: "PostgreSQL connect timeout in milliseconds" })),
|
|
182
|
+
idleTimeoutMs: z
|
|
183
|
+
.number()
|
|
184
|
+
.int()
|
|
185
|
+
.positive()
|
|
186
|
+
.default(60_000)
|
|
187
|
+
.meta(meta({
|
|
188
|
+
description: "How long an idle PostgreSQL connection stays in the pool, in milliseconds",
|
|
189
|
+
})),
|
|
182
190
|
})
|
|
183
191
|
.prefault({}),
|
|
184
192
|
redis: z
|
|
@@ -568,6 +576,12 @@ export const KernelConfigSchema = z.object({
|
|
|
568
576
|
.positive()
|
|
569
577
|
.default(50)
|
|
570
578
|
.meta(meta({ description: "Maximum model steps per agent run before the final wrap-up" })),
|
|
579
|
+
wakeOnBackgroundTask: z
|
|
580
|
+
.boolean()
|
|
581
|
+
.default(true)
|
|
582
|
+
.meta(meta({
|
|
583
|
+
description: "Whether the finish of a background Bash task starts a run on an idle conversation (the person who started the task's run is the trigger); an organization can override it in its settings. When false the event is only logged and shown, and the next message picks it up",
|
|
584
|
+
})),
|
|
571
585
|
maxRunSeconds: z
|
|
572
586
|
.number()
|
|
573
587
|
.int()
|
|
@@ -656,6 +670,7 @@ export const KernelConfigSchema = z.object({
|
|
|
656
670
|
})
|
|
657
671
|
.default({
|
|
658
672
|
maxSteps: 50,
|
|
673
|
+
wakeOnBackgroundTask: true,
|
|
659
674
|
maxRunSeconds: DEFAULT_MAX_RUN_SECONDS,
|
|
660
675
|
streamIdleMs: 60000,
|
|
661
676
|
maxOutputTokens: 16000,
|
|
@@ -946,6 +961,64 @@ export const KernelConfigSchema = z.object({
|
|
|
946
961
|
})),
|
|
947
962
|
})
|
|
948
963
|
.prefault({}),
|
|
964
|
+
web: z
|
|
965
|
+
.object({
|
|
966
|
+
search: z
|
|
967
|
+
.object({
|
|
968
|
+
maxResults: z
|
|
969
|
+
.number()
|
|
970
|
+
.int()
|
|
971
|
+
.min(1)
|
|
972
|
+
.max(20)
|
|
973
|
+
.default(10)
|
|
974
|
+
.meta(meta({
|
|
975
|
+
hot: true,
|
|
976
|
+
description: "Maximum number of results one WebSearch call returns to the model, however many the search provider returns",
|
|
977
|
+
})),
|
|
978
|
+
})
|
|
979
|
+
.prefault({}),
|
|
980
|
+
fetch: z
|
|
981
|
+
.object({
|
|
982
|
+
maxPageChars: z
|
|
983
|
+
.number()
|
|
984
|
+
.int()
|
|
985
|
+
.positive()
|
|
986
|
+
.default(400_000)
|
|
987
|
+
.meta(meta({
|
|
988
|
+
hot: true,
|
|
989
|
+
description: "Maximum characters of a fetched page kept for answering a WebFetch prompt; longer pages are truncated",
|
|
990
|
+
})),
|
|
991
|
+
maxAnswerTokens: z
|
|
992
|
+
.number()
|
|
993
|
+
.int()
|
|
994
|
+
.positive()
|
|
995
|
+
.default(4000)
|
|
996
|
+
.meta(meta({
|
|
997
|
+
hot: true,
|
|
998
|
+
description: "Maximum output tokens of the model call that answers a WebFetch prompt from the page",
|
|
999
|
+
})),
|
|
1000
|
+
cacheSeconds: z
|
|
1001
|
+
.number()
|
|
1002
|
+
.int()
|
|
1003
|
+
.min(0)
|
|
1004
|
+
.default(900)
|
|
1005
|
+
.meta(meta({
|
|
1006
|
+
hot: true,
|
|
1007
|
+
description: "Seconds a fetched page is cached per organization and URL; 0 turns the cache off",
|
|
1008
|
+
})),
|
|
1009
|
+
timeoutMs: z
|
|
1010
|
+
.number()
|
|
1011
|
+
.int()
|
|
1012
|
+
.positive()
|
|
1013
|
+
.default(180_000)
|
|
1014
|
+
.meta(meta({
|
|
1015
|
+
hot: true,
|
|
1016
|
+
description: "Time limit of one WebFetch call in milliseconds, the provider's fetch and the answering model call together; replaces tools.timeoutMs for WebFetch",
|
|
1017
|
+
})),
|
|
1018
|
+
})
|
|
1019
|
+
.prefault({}),
|
|
1020
|
+
})
|
|
1021
|
+
.prefault({}),
|
|
949
1022
|
sandbox: z
|
|
950
1023
|
.object({
|
|
951
1024
|
image: z
|
|
@@ -980,6 +1053,35 @@ export const KernelConfigSchema = z.object({
|
|
|
980
1053
|
.meta(meta({
|
|
981
1054
|
description: "Largest file in bytes an agent may deliver from its sandbox to the user as an artifact (Deliver)",
|
|
982
1055
|
})),
|
|
1056
|
+
background: z
|
|
1057
|
+
.object({
|
|
1058
|
+
maxTasks: z
|
|
1059
|
+
.number()
|
|
1060
|
+
.int()
|
|
1061
|
+
.min(1)
|
|
1062
|
+
.max(100)
|
|
1063
|
+
.default(4)
|
|
1064
|
+
.meta(meta({
|
|
1065
|
+
description: "Most background Bash tasks (run_in_background) that may run at once in one conversation's sandbox, sub-agents included; a launch beyond it is refused",
|
|
1066
|
+
})),
|
|
1067
|
+
maxSeconds: z
|
|
1068
|
+
.number()
|
|
1069
|
+
.int()
|
|
1070
|
+
.positive()
|
|
1071
|
+
.default(7200)
|
|
1072
|
+
.meta(meta({
|
|
1073
|
+
description: "Longest a background Bash task may run, in seconds; the task is killed at its deadline and ends as timed_out. Clamped to sandbox.limits.maxCommandSeconds; not limited by runtime.maxRunSeconds",
|
|
1074
|
+
})),
|
|
1075
|
+
pollSeconds: z
|
|
1076
|
+
.number()
|
|
1077
|
+
.int()
|
|
1078
|
+
.min(1)
|
|
1079
|
+
.default(10)
|
|
1080
|
+
.meta(meta({
|
|
1081
|
+
description: "How often the worker checks the running background Bash tasks of every sandbox for a finish, in seconds; read when the worker starts",
|
|
1082
|
+
})),
|
|
1083
|
+
})
|
|
1084
|
+
.prefault({}),
|
|
983
1085
|
limits: z
|
|
984
1086
|
.object({
|
|
985
1087
|
cpu: z
|
|
@@ -27,6 +27,7 @@ export const parentReachable = (viewerAccountId) => (eb) => eb.or([
|
|
|
27
27
|
.whereRef("p.org_id", "=", "conversations.org_id")
|
|
28
28
|
.where("p.account_id", "=", viewerAccountId)
|
|
29
29
|
.where("p.kind", "=", "chat")
|
|
30
|
+
.where("p.deleted_at", "is", null)
|
|
30
31
|
.where("agents.deleted_at", "is", null)
|
|
31
32
|
.where(visibleTo(viewerAccountId))),
|
|
32
33
|
]);
|
|
@@ -53,6 +54,7 @@ let ConversationAccess = class ConversationAccess {
|
|
|
53
54
|
.where("conversations.id", "=", conversationId)
|
|
54
55
|
.where("conversations.org_id", "=", orgId)
|
|
55
56
|
.where("conversations.account_id", "=", viewerAccountId)
|
|
57
|
+
.where("conversations.deleted_at", "is", null)
|
|
56
58
|
.where("conversations.kind", "in", ["chat", "subagent"])
|
|
57
59
|
.where(parentReachable(viewerAccountId))
|
|
58
60
|
.where((eb) => eb.and([
|
|
@@ -28,6 +28,16 @@ export declare const MAX_PENDING_SKILL_PACKAGES = 5;
|
|
|
28
28
|
* `POST /skills` 开始处理时再顺延这么久,处理期间不会被清理。
|
|
29
29
|
*/
|
|
30
30
|
export declare const SKILL_PACKAGE_TTL_HOURS = 1;
|
|
31
|
+
/** The image types an agent avatar upload may have (cropped square in the browser). */
|
|
32
|
+
export declare const AGENT_AVATAR_CONTENT_TYPES: readonly ["image/png", "image/jpeg", "image/webp"];
|
|
33
|
+
/** The size limit of an agent avatar upload (bytes). */
|
|
34
|
+
export declare const AGENT_AVATAR_MAX_BYTES: number;
|
|
35
|
+
/** Unfinished avatar uploads one account may have in an org (counted apart from other uploads). */
|
|
36
|
+
export declare const MAX_PENDING_AGENT_AVATARS = 10;
|
|
37
|
+
/** An avatar upload not saved into a profile expires (and is cleaned up) after this many hours. */
|
|
38
|
+
export declare const AGENT_AVATAR_TTL_HOURS = 24;
|
|
39
|
+
/** Download redirect lifetime of an avatar (seconds); also its `Cache-Control` max-age. */
|
|
40
|
+
export declare const AGENT_AVATAR_DOWNLOAD_TTL_SECONDS = 300;
|
|
31
41
|
/**
|
|
32
42
|
* 技能包解出的文件在 `kernel/<orgId>/` 下的前缀:按上传 id,同一上传重试写到同一批键(幂等),
|
|
33
43
|
* 并发处理同一上传也不会互相覆盖出不同内容。版本行记下这个前缀(`skill_versions.object_prefix`)。
|
|
@@ -169,6 +179,26 @@ export declare class AttachmentsService {
|
|
|
169
179
|
* 不是该账号的 ready 技能包(如已被并发的请求用掉)→ `attachment.not_found`。
|
|
170
180
|
*/
|
|
171
181
|
consumeSkillPackage(orgId: string, accountId: string, id: string): Promise<void>;
|
|
182
|
+
/**
|
|
183
|
+
* Makes an agent avatar upload ready to be saved into a profile (outside any transaction: it may
|
|
184
|
+
* read the object store): only the uploader, only the `agent-avatar` purpose, an image type, not
|
|
185
|
+
* expired and not yet used by another agent, else `attachment.not_found`. An upload still
|
|
186
|
+
* `pending` is finalized first (size, type and the image signature are checked). The expiry is
|
|
187
|
+
* extended so the cleanup job cannot delete it before {@link consumeAgentAvatar} runs.
|
|
188
|
+
*/
|
|
189
|
+
prepareAgentAvatar(orgId: string, accountId: string, id: string, agentId: string): Promise<void>;
|
|
190
|
+
/**
|
|
191
|
+
* In the caller's transaction, binds a ready avatar upload to `agentId` (row lock; the expiry is
|
|
192
|
+
* cleared, so the cleanup job keeps it while the profile uses it). An upload another agent
|
|
193
|
+
* already uses, or one that is not the account's ready avatar → `attachment.not_found`; saving
|
|
194
|
+
* the same upload to the same agent again is fine.
|
|
195
|
+
*/
|
|
196
|
+
consumeAgentAvatar(orgId: string, accountId: string, id: string, agentId: string): Promise<void>;
|
|
197
|
+
/**
|
|
198
|
+
* A short-lived presigned GET (inline, with the checked image type) of the avatar upload bound
|
|
199
|
+
* to `agentId`; `null` when there is none or its object is gone or no longer the finalized one.
|
|
200
|
+
*/
|
|
201
|
+
agentAvatarUrl(orgId: string, agentId: string, id: string): Promise<string | null>;
|
|
172
202
|
/**
|
|
173
203
|
* 删除过期(`expires_at < now()`)且从未绑定到会话的附件:先把行认领为 `deleting`(`FOR UPDATE SKIP LOCKED`,
|
|
174
204
|
* 多副本并发时互不重复),再删对象、删行。删对象失败的行保持 `deleting`,下次运行重试。
|