@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
|
@@ -20,6 +20,7 @@ import { ApiTokenService, FutureIsoDate } from "./api-tokens.js";
|
|
|
20
20
|
import { MAX_PASSWORD_LENGTH } from "./login.js";
|
|
21
21
|
import { CurrentPrincipal, requireUser, SessionOnly } from "./principal.js";
|
|
22
22
|
import { SessionService } from "./sessions.js";
|
|
23
|
+
import { SettingsService } from "./settings.js";
|
|
23
24
|
/** The credential throttlers of a password change (per account, per client IP). */
|
|
24
25
|
const CHANGE_PASSWORD_THROTTLERS = ["change-password", "change-password-ip"];
|
|
25
26
|
const ChangePasswordSchema = z.object({
|
|
@@ -45,13 +46,15 @@ let MeController = class MeController {
|
|
|
45
46
|
tokens;
|
|
46
47
|
throttler;
|
|
47
48
|
log;
|
|
48
|
-
|
|
49
|
+
settings;
|
|
50
|
+
constructor(accounts, sessions, orgs, tokens, throttler, log, settings) {
|
|
49
51
|
this.accounts = accounts;
|
|
50
52
|
this.sessions = sessions;
|
|
51
53
|
this.orgs = orgs;
|
|
52
54
|
this.tokens = tokens;
|
|
53
55
|
this.throttler = throttler;
|
|
54
56
|
this.log = log;
|
|
57
|
+
this.settings = settings;
|
|
55
58
|
}
|
|
56
59
|
async me(principal) {
|
|
57
60
|
const p = requireUser(principal);
|
|
@@ -62,7 +65,12 @@ let MeController = class MeController {
|
|
|
62
65
|
name: o.name,
|
|
63
66
|
role: o.role,
|
|
64
67
|
}));
|
|
65
|
-
|
|
68
|
+
// a session's org the account has since left reads as none (removal clears it, but a session
|
|
69
|
+
// created at that moment may still name it)
|
|
70
|
+
const current = memberships.some((m) => m.orgId === currentOrgId) ? currentOrgId : null;
|
|
71
|
+
const { orgCreation } = await this.settings.get();
|
|
72
|
+
const canCreateOrg = orgCreation === "anyone" || p.account.isEnterpriseAdmin;
|
|
73
|
+
return { account: p.account, via: p.via, currentOrgId: current, memberships, canCreateOrg };
|
|
66
74
|
}
|
|
67
75
|
/** 切换当前组织(仅 Web 会话);`null` 清空。非成员或已软删除的组织 → `org.not_found`。 */
|
|
68
76
|
async setCurrentOrg(principal, body) {
|
|
@@ -160,6 +168,7 @@ MeController = __decorate([
|
|
|
160
168
|
__param(2, Inject(OrgsService)),
|
|
161
169
|
__param(3, Inject(ApiTokenService)),
|
|
162
170
|
__param(4, Inject(CredentialThrottler)),
|
|
163
|
-
__param(5, Inject(KERNEL_LOGGER))
|
|
171
|
+
__param(5, Inject(KERNEL_LOGGER)),
|
|
172
|
+
__param(6, Inject(SettingsService))
|
|
164
173
|
], MeController);
|
|
165
174
|
export { MeController };
|
|
@@ -36,6 +36,18 @@ export declare class SessionService {
|
|
|
36
36
|
userAgent?: string;
|
|
37
37
|
ip?: string;
|
|
38
38
|
}): Promise<IssuedSession>;
|
|
39
|
+
/**
|
|
40
|
+
* The org a new session starts in: the account's last chosen one while it is still a member of it
|
|
41
|
+
* (and the org is not deleted), else its only org when it has exactly one, else none (the web
|
|
42
|
+
* app then asks to choose or create one).
|
|
43
|
+
*/
|
|
44
|
+
private startingOrg;
|
|
45
|
+
/**
|
|
46
|
+
* Read-only: of several refresh tokens (a cookie header carrying the legacy and the `Path=/`
|
|
47
|
+
* cookie), the first that is the current token of a live session, if any. Choosing before
|
|
48
|
+
* rotating keeps a stale token from tripping replay detection on a still valid session.
|
|
49
|
+
*/
|
|
50
|
+
pickCurrentRefresh(candidates: readonly string[]): Promise<string | undefined>;
|
|
39
51
|
refresh(refreshToken: string, meta: {
|
|
40
52
|
userAgent?: string;
|
|
41
53
|
ip?: string;
|
|
@@ -72,11 +72,49 @@ let SessionService = class SessionService {
|
|
|
72
72
|
user_agent: meta.userAgent ?? null,
|
|
73
73
|
ip: meta.ip ?? null,
|
|
74
74
|
expires_at: sql `now() + make_interval(days => ${refreshTtlDays})`,
|
|
75
|
+
current_org_id: await this.startingOrg(accountId),
|
|
75
76
|
})
|
|
76
77
|
.returning(["expires_at"])
|
|
77
78
|
.executeTakeFirstOrThrow();
|
|
78
79
|
return this.issue(id, accountId, refreshToken, row.expires_at);
|
|
79
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* The org a new session starts in: the account's last chosen one while it is still a member of it
|
|
83
|
+
* (and the org is not deleted), else its only org when it has exactly one, else none (the web
|
|
84
|
+
* app then asks to choose or create one).
|
|
85
|
+
*/
|
|
86
|
+
async startingOrg(accountId) {
|
|
87
|
+
const orgs = await this.db
|
|
88
|
+
.selectFrom("memberships as m")
|
|
89
|
+
.innerJoin("orgs as o", "o.id", "m.org_id")
|
|
90
|
+
.innerJoin("accounts as a", "a.id", "m.account_id")
|
|
91
|
+
.select(["m.org_id as orgId", "a.last_org_id as lastOrgId"])
|
|
92
|
+
.where("m.account_id", "=", accountId)
|
|
93
|
+
.where("o.deleted_at", "is", null)
|
|
94
|
+
.execute();
|
|
95
|
+
const last = orgs.find((o) => o.orgId === o.lastOrgId);
|
|
96
|
+
if (last)
|
|
97
|
+
return last.orgId;
|
|
98
|
+
return orgs.length === 1 ? (orgs[0]?.orgId ?? null) : null;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Read-only: of several refresh tokens (a cookie header carrying the legacy and the `Path=/`
|
|
102
|
+
* cookie), the first that is the current token of a live session, if any. Choosing before
|
|
103
|
+
* rotating keeps a stale token from tripping replay detection on a still valid session.
|
|
104
|
+
*/
|
|
105
|
+
async pickCurrentRefresh(candidates) {
|
|
106
|
+
if (candidates.length === 0)
|
|
107
|
+
return undefined;
|
|
108
|
+
const rows = await this.db
|
|
109
|
+
.selectFrom("sessions")
|
|
110
|
+
.select("refresh_hash")
|
|
111
|
+
.where("refresh_hash", "in", candidates.map((c) => sha256Hex(c)))
|
|
112
|
+
.where("revoked_at", "is", null)
|
|
113
|
+
.where("expires_at", ">", sql `now()`)
|
|
114
|
+
.execute();
|
|
115
|
+
const live = new Set(rows.map((r) => r.refresh_hash));
|
|
116
|
+
return candidates.find((c) => live.has(sha256Hex(c)));
|
|
117
|
+
}
|
|
80
118
|
async refresh(refreshToken, meta) {
|
|
81
119
|
const oldHash = sha256Hex(refreshToken);
|
|
82
120
|
const next = newRefreshToken();
|
|
@@ -277,6 +315,13 @@ let SessionService = class SessionService {
|
|
|
277
315
|
.executeTakeFirst());
|
|
278
316
|
if (!m)
|
|
279
317
|
throw new ApiError("org.not_found", "Organization not found");
|
|
318
|
+
// remembered for the next sign-in; the account row before the session row, the order a
|
|
319
|
+
// password change takes (account, then its sessions)
|
|
320
|
+
await this.db
|
|
321
|
+
.updateTable("accounts")
|
|
322
|
+
.set({ last_org_id: orgId })
|
|
323
|
+
.where("id", "=", accountId)
|
|
324
|
+
.execute();
|
|
280
325
|
const r = await this.db
|
|
281
326
|
.updateTable("sessions")
|
|
282
327
|
.set({ current_org_id: orgId })
|
package/dist/index.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ export { type ApprovalDecision, ExtensionApprovals, type PendingExtension, type
|
|
|
31
31
|
export { blobKey, ExtensionBlobIntegrityError, type ExtensionBlobSource, ExtensionBlobs, } from "./extensions/blobs.js";
|
|
32
32
|
export { BOOTSTRAP_MANIFEST_FILE, type BootstrapEntryResult, BootstrapFailedError, type BootstrapFailure, type BootstrapResolveOptions, type BootstrapResult, BootstrapService, bootstrapManifestSha256, readBootstrapManifest, resolveBootstrapPackages, } from "./extensions/bootstrap.js";
|
|
33
33
|
export { type ExtensionCli, openExtensionCli } from "./extensions/cli.js";
|
|
34
|
-
export { DEPLOYMENT_SECRETS_ORG, type DeploymentConfigField, type DeploymentConfigView, ExtensionDeploymentConfigService, mergeDeploymentConfig, PLUGIN_DEPLOYMENT_CONFIG_TOPIC, readStoredDeploymentConfig, } from "./extensions/deployment-config.js";
|
|
34
|
+
export { DEPLOYMENT_SECRETS_ORG, type DeploymentConfigField, type DeploymentConfigView, ExtensionDeploymentConfigService, mergeDeploymentConfig, PLUGIN_DEPLOYMENT_CONFIG_TOPIC, readStoredDeploymentConfig, type SecretEntry, } from "./extensions/deployment-config.js";
|
|
35
35
|
export { assertSecretsReachable, ExtensionHost, type ExtensionLoadPlan, type InspectedExtension, type InstalledExtensionLoader, type InstallFailureReason, } from "./extensions/host.js";
|
|
36
36
|
export { ExtensionUnloadedError, type InflightEntry, InflightTracker, PLUGIN_UNLOADED_TOOL_ERROR, type TrackOptions, trackCalls, } from "./extensions/inflight.js";
|
|
37
37
|
export { type DesiredExtension, type ExtensionInstallRow, ExtensionInstalls, type IngestSource, type PackageReview, } from "./extensions/installs.js";
|
|
@@ -34,6 +34,12 @@ const HOT_APPLIERS = {
|
|
|
34
34
|
"subagents.maxConcurrentPerRun": () => { },
|
|
35
35
|
"subagents.maxDispatchesPerRun": () => { },
|
|
36
36
|
"subagents.maxTokensPerRun": () => { },
|
|
37
|
+
// The web tools read these limits through the config service on every call.
|
|
38
|
+
"web.search.maxResults": () => { },
|
|
39
|
+
"web.fetch.maxPageChars": () => { },
|
|
40
|
+
"web.fetch.maxAnswerTokens": () => { },
|
|
41
|
+
"web.fetch.cacheSeconds": () => { },
|
|
42
|
+
"web.fetch.timeoutMs": () => { },
|
|
37
43
|
};
|
|
38
44
|
const HOT_PATHS = configFields(KernelConfigSchema)
|
|
39
45
|
.filter((f) => f.meta?.hot)
|
|
@@ -66,6 +66,7 @@ let DatabaseModule = class DatabaseModule {
|
|
|
66
66
|
const { db } = config.get();
|
|
67
67
|
return createDb(db.url, db.poolMax, (err) => log.error("pg idle client error", { err }), {
|
|
68
68
|
connectTimeoutMs: db.connectTimeoutMs,
|
|
69
|
+
idleTimeoutMs: db.idleTimeoutMs,
|
|
69
70
|
onClientError: (err) => log.warn("pg client error", { err }),
|
|
70
71
|
});
|
|
71
72
|
},
|
|
@@ -78,6 +78,14 @@ export interface KernelEventMap {
|
|
|
78
78
|
"run.orphaned": {
|
|
79
79
|
runId: string;
|
|
80
80
|
};
|
|
81
|
+
/**
|
|
82
|
+
* A background task of the conversation finished (after the commit of its
|
|
83
|
+
* `background_task_finished` event). `BackgroundWake` asks the run queue for a wake run; a lost
|
|
84
|
+
* event is re-derived by the next `ensureRun` (a message, the end of a run, a page opening).
|
|
85
|
+
*/
|
|
86
|
+
"background_task.finished": {
|
|
87
|
+
conversationId: string;
|
|
88
|
+
};
|
|
81
89
|
}
|
|
82
90
|
type Handler<K extends keyof KernelEventMap> = (payload: KernelEventMap[K]) => Promise<void> | void;
|
|
83
91
|
/** Typed in-process event emitter; handlers run in order, a failing one is logged and skipped. */
|
|
@@ -39,6 +39,10 @@ export function createFatalRecorder(deps) {
|
|
|
39
39
|
let recent = [];
|
|
40
40
|
let open = false;
|
|
41
41
|
return (kind, err) => {
|
|
42
|
+
// Exiting already: log nothing more. A failing log write (EPIPE on a dead stdout) raises the
|
|
43
|
+
// next event itself; logging it again would keep the process busy forever and starve the exit.
|
|
44
|
+
if (open)
|
|
45
|
+
return;
|
|
42
46
|
try {
|
|
43
47
|
const t = now();
|
|
44
48
|
recent = recent.filter((x) => t - x < GUARD_CIRCUIT_WINDOW_MS);
|
|
@@ -6,8 +6,10 @@ export interface LoggingModuleOptions {
|
|
|
6
6
|
logger?: KernelLogger;
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
|
-
* Provides the process logger as `
|
|
10
|
-
*
|
|
9
|
+
* Provides the process logger as `KERNEL_ROOT_LOGGER` and, as `KERNEL_LOGGER`, one child per injecting
|
|
10
|
+
* class with `context` = the class name (the `[context=…]` of the pretty output; octok's
|
|
11
|
+
* per-class Nest logger context). The logger is created before the Nest context (it must exist for
|
|
12
|
+
* startup errors), so it is passed in or derived from the startup config here.
|
|
11
13
|
*/
|
|
12
14
|
export declare class LoggingModule {
|
|
13
15
|
static forRoot(config: KernelConfig, opts?: LoggingModuleOptions): DynamicModule;
|
|
@@ -5,12 +5,15 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
7
|
var LoggingModule_1;
|
|
8
|
-
import { Global, Module } from "@nestjs/common";
|
|
8
|
+
import { Global, Module, Scope } from "@nestjs/common";
|
|
9
|
+
import { INQUIRER } from "@nestjs/core";
|
|
9
10
|
import { createConfiguredLogger } from "../../log-config.js";
|
|
10
|
-
import { KERNEL_LOGGER } from "../tokens.js";
|
|
11
|
+
import { KERNEL_LOGGER, KERNEL_ROOT_LOGGER } from "../tokens.js";
|
|
11
12
|
/**
|
|
12
|
-
* Provides the process logger as `
|
|
13
|
-
*
|
|
13
|
+
* Provides the process logger as `KERNEL_ROOT_LOGGER` and, as `KERNEL_LOGGER`, one child per injecting
|
|
14
|
+
* class with `context` = the class name (the `[context=…]` of the pretty output; octok's
|
|
15
|
+
* per-class Nest logger context). The logger is created before the Nest context (it must exist for
|
|
16
|
+
* startup errors), so it is passed in or derived from the startup config here.
|
|
14
17
|
*/
|
|
15
18
|
let LoggingModule = LoggingModule_1 = class LoggingModule {
|
|
16
19
|
static forRoot(config, opts = {}) {
|
|
@@ -21,8 +24,16 @@ let LoggingModule = LoggingModule_1 = class LoggingModule {
|
|
|
21
24
|
return {
|
|
22
25
|
module: LoggingModule_1,
|
|
23
26
|
global: true,
|
|
24
|
-
providers: [
|
|
25
|
-
|
|
27
|
+
providers: [
|
|
28
|
+
{ provide: KERNEL_ROOT_LOGGER, useValue: logger },
|
|
29
|
+
{
|
|
30
|
+
provide: KERNEL_LOGGER,
|
|
31
|
+
scope: Scope.TRANSIENT,
|
|
32
|
+
inject: [KERNEL_ROOT_LOGGER, INQUIRER],
|
|
33
|
+
useFactory: (root, inquirer) => contextLogger(root, inquirer),
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
exports: [KERNEL_ROOT_LOGGER, KERNEL_LOGGER],
|
|
26
37
|
};
|
|
27
38
|
}
|
|
28
39
|
};
|
|
@@ -32,3 +43,10 @@ LoggingModule = LoggingModule_1 = __decorate([
|
|
|
32
43
|
// biome-ignore lint/complexity/noStaticOnlyClass: a Nest dynamic module
|
|
33
44
|
], LoggingModule);
|
|
34
45
|
export { LoggingModule };
|
|
46
|
+
/** The child of `root` for the class instance `inquirer`; `root` itself for a factory provider. */
|
|
47
|
+
function contextLogger(root, inquirer) {
|
|
48
|
+
const name = inquirer?.constructor?.name;
|
|
49
|
+
return typeof name === "string" && name !== "" && name !== "Object"
|
|
50
|
+
? root.child({ context: name })
|
|
51
|
+
: root;
|
|
52
|
+
}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import type { RedisOptions } from "bullmq";
|
|
2
|
+
/**
|
|
3
|
+
* TCP keepalive of every long-lived kernel Redis connection: probes start after 10 s idle (the OS
|
|
4
|
+
* default is 2 hours), so a connection a dropped network left half-open is found and reconnected
|
|
5
|
+
* before a command waits on it, not at the next write (EPIPE).
|
|
6
|
+
*/
|
|
7
|
+
export declare const REDIS_KEEPALIVE_MS = 10000;
|
|
2
8
|
/**
|
|
3
9
|
* BullMQ connection options from the kernel's Redis URL, the same connection the hand-made
|
|
4
10
|
* `new Redis(url, { maxRetriesPerRequest: null })` workers use: BullMQ requires
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TCP keepalive of every long-lived kernel Redis connection: probes start after 10 s idle (the OS
|
|
3
|
+
* default is 2 hours), so a connection a dropped network left half-open is found and reconnected
|
|
4
|
+
* before a command waits on it, not at the next write (EPIPE).
|
|
5
|
+
*/
|
|
6
|
+
export const REDIS_KEEPALIVE_MS = 10_000;
|
|
1
7
|
/**
|
|
2
8
|
* BullMQ connection options from the kernel's Redis URL, the same connection the hand-made
|
|
3
9
|
* `new Redis(url, { maxRetriesPerRequest: null })` workers use: BullMQ requires
|
|
@@ -14,5 +20,6 @@ export function bullConnectionOptions(url) {
|
|
|
14
20
|
...(db !== undefined && Number.isInteger(db) && { db }),
|
|
15
21
|
...(u.protocol === "rediss:" && { tls: {} }),
|
|
16
22
|
maxRetriesPerRequest: null,
|
|
23
|
+
keepAlive: REDIS_KEEPALIVE_MS,
|
|
17
24
|
};
|
|
18
25
|
}
|
|
@@ -14,6 +14,7 @@ import { closeWithin } from "../close-within.js";
|
|
|
14
14
|
import { KernelConfigService } from "../config/kernel-config.service.js";
|
|
15
15
|
import { GracefulShutdownModule } from "../lifecycle/graceful-shutdown.module.js";
|
|
16
16
|
import { GracefulShutdown } from "../lifecycle/graceful-shutdown.service.js";
|
|
17
|
+
import { REDIS_KEEPALIVE_MS } from "../queue/redis-connection.js";
|
|
17
18
|
import { KERNEL_BUS, KERNEL_LOGGER, REDIS, REDIS_SUBSCRIBER } from "../tokens.js";
|
|
18
19
|
/**
|
|
19
20
|
* Closes the bus subscriber, then the main connection, at the end of the shutdown
|
|
@@ -64,7 +65,10 @@ RedisModule = __decorate([
|
|
|
64
65
|
provide: REDIS,
|
|
65
66
|
inject: [KernelConfigService, KERNEL_LOGGER],
|
|
66
67
|
useFactory: (config, log) => {
|
|
67
|
-
const redis = new Redis(config.get().redis.url, {
|
|
68
|
+
const redis = new Redis(config.get().redis.url, {
|
|
69
|
+
lazyConnect: true,
|
|
70
|
+
keepAlive: REDIS_KEEPALIVE_MS,
|
|
71
|
+
});
|
|
68
72
|
redis.on("error", (err) => log.error("redis connection error", { err }));
|
|
69
73
|
return redis;
|
|
70
74
|
},
|
|
@@ -72,7 +76,7 @@ RedisModule = __decorate([
|
|
|
72
76
|
{
|
|
73
77
|
provide: REDIS_SUBSCRIBER,
|
|
74
78
|
inject: [KernelConfigService, KERNEL_LOGGER],
|
|
75
|
-
useFactory: (config, log) => createBusSubscriber(() => new Redis(config.get().redis.url), log),
|
|
79
|
+
useFactory: (config, log) => createBusSubscriber(() => new Redis(config.get().redis.url, { keepAlive: REDIS_KEEPALIVE_MS }), log),
|
|
76
80
|
},
|
|
77
81
|
{
|
|
78
82
|
provide: KERNEL_BUS,
|
package/dist/infra/tokens.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/** Injection tokens of the global infra modules. */
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* A class's logger (`KernelLogger`): the process logger with `context` set to the injecting class's
|
|
4
|
+
* name. Transient, so `app.get` cannot resolve it; use `KERNEL_ROOT_LOGGER` outside an injection.
|
|
5
|
+
*/
|
|
3
6
|
export declare const KERNEL_LOGGER: unique symbol;
|
|
7
|
+
/** The process logger itself (`KernelLogger`), without a context. */
|
|
8
|
+
export declare const KERNEL_ROOT_LOGGER: unique symbol;
|
|
4
9
|
/** The startup input of `KernelConfigService`: `{ config, env }`. */
|
|
5
10
|
export declare const KERNEL_CONFIG_INPUT: unique symbol;
|
|
6
11
|
/** The Kysely pool, untyped (`Kysely<any>`); infra only, services inject `KernelDb`. */
|
package/dist/infra/tokens.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/** Injection tokens of the global infra modules. */
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* A class's logger (`KernelLogger`): the process logger with `context` set to the injecting class's
|
|
4
|
+
* name. Transient, so `app.get` cannot resolve it; use `KERNEL_ROOT_LOGGER` outside an injection.
|
|
5
|
+
*/
|
|
3
6
|
export const KERNEL_LOGGER = Symbol("KERNEL_LOGGER");
|
|
7
|
+
/** The process logger itself (`KernelLogger`), without a context. */
|
|
8
|
+
export const KERNEL_ROOT_LOGGER = Symbol("KERNEL_ROOT_LOGGER");
|
|
4
9
|
/** The startup input of `KernelConfigService`: `{ config, env }`. */
|
|
5
10
|
export const KERNEL_CONFIG_INPUT = Symbol("KERNEL_CONFIG_INPUT");
|
|
6
11
|
/** The Kysely pool, untyped (`Kysely<any>`); infra only, services inject `KernelDb`. */
|
package/dist/log.d.ts
CHANGED
|
@@ -30,6 +30,11 @@ export interface KernelLogger extends Logger {
|
|
|
30
30
|
redact(text: string): string;
|
|
31
31
|
/** Waits until buffered records reached every output (Loki); never rejects. Call before exit. */
|
|
32
32
|
flush(): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* A logger that adds `fields` to every record (e.g. `{ context: "RunWorker" }`); call fields cannot
|
|
35
|
+
* overwrite them. It shares everything else with this logger: level, secrets, outputs.
|
|
36
|
+
*/
|
|
37
|
+
child(fields: Record<string, string>): KernelLogger;
|
|
33
38
|
}
|
|
34
39
|
/**
|
|
35
40
|
* 用与 logger 相同的匹配器打码任意文本(工具结果等不经过日志的内容用):展开规则同 `addOrgSecrets`
|
|
@@ -37,13 +42,15 @@ export interface KernelLogger extends Logger {
|
|
|
37
42
|
* 不得小于匹配器的分桶前缀长度 4)。没有可用的值时原样返回。
|
|
38
43
|
*/
|
|
39
44
|
export declare function createRedactor(values: readonly unknown[], minLength?: number): (s: string) => string;
|
|
40
|
-
/** Console format: `pretty` (pino-pretty
|
|
45
|
+
/** Console format: `pretty` (pino-pretty), `json`, or `auto` (pretty on a TTY). */
|
|
41
46
|
export declare const LOG_FORMATS: readonly ["auto", "pretty", "json"];
|
|
42
47
|
/** Console format. */
|
|
43
48
|
export type LogFormat = (typeof LOG_FORMATS)[number];
|
|
44
49
|
/** A minimal writable the console output goes to (`process.stdout` by default). */
|
|
45
50
|
export interface LogOutput {
|
|
46
51
|
write(chunk: string): unknown;
|
|
52
|
+
/** A stream's `error` event (EPIPE when its reader is gone). */
|
|
53
|
+
on?(event: "error", listener: (err: unknown) => void): unknown;
|
|
47
54
|
}
|
|
48
55
|
/** Options of {@link createLogger}. */
|
|
49
56
|
export interface LoggerOptions {
|
|
@@ -58,6 +65,11 @@ export interface LoggerOptions {
|
|
|
58
65
|
out?: LogOutput;
|
|
59
66
|
/** Whether the console is a terminal (picks `auto` and colors); default `process.stdout.isTTY`. */
|
|
60
67
|
isTTY?: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Pretty output in colors; default: `FORCE_COLOR` (set and not `0`) turns them on, `NO_COLOR` off,
|
|
70
|
+
* else a TTY. `pnpm dev:kernel` forces them, since its output goes through `tee`.
|
|
71
|
+
*/
|
|
72
|
+
colors?: boolean;
|
|
61
73
|
/** Also push every record to Loki. */
|
|
62
74
|
loki?: LokiOptions;
|
|
63
75
|
/** Fields on every record (e.g. `role`); log calls cannot overwrite them. */
|
package/dist/log.js
CHANGED
|
@@ -105,15 +105,16 @@ function expandSecret(value) {
|
|
|
105
105
|
}
|
|
106
106
|
return out;
|
|
107
107
|
}
|
|
108
|
-
/** Console format: `pretty` (pino-pretty
|
|
108
|
+
/** Console format: `pretty` (pino-pretty), `json`, or `auto` (pretty on a TTY). */
|
|
109
109
|
export const LOG_FORMATS = ["auto", "pretty", "json"];
|
|
110
|
-
/** Fields shown in the pretty prefix instead of the
|
|
111
|
-
const PREFIX_FIELDS = ["reqId", "
|
|
112
|
-
/**
|
|
110
|
+
/** Fields shown in the pretty prefix instead of the lines below, in this order. */
|
|
111
|
+
const PREFIX_FIELDS = ["traceId", "reqId", "queue", "jobId", "runId", "context", "plugin"];
|
|
112
|
+
/**
|
|
113
|
+
* pino-pretty's message format, the octok layout:
|
|
114
|
+
* `[pid=…] - [traceId=…] [reqId=…] [context=…] msg`, each bracket only when present.
|
|
115
|
+
*/
|
|
113
116
|
function prettyMessage(log, messageKey) {
|
|
114
|
-
const parts = [];
|
|
115
|
-
if (typeof log.role === "string")
|
|
116
|
-
parts.push(`[${log.role}]`);
|
|
117
|
+
const parts = [`[pid=${process.pid}] -`];
|
|
117
118
|
for (const k of PREFIX_FIELDS) {
|
|
118
119
|
const v = log[k];
|
|
119
120
|
if (typeof v === "string" || typeof v === "number")
|
|
@@ -122,6 +123,24 @@ function prettyMessage(log, messageKey) {
|
|
|
122
123
|
parts.push(String(log[messageKey] ?? ""));
|
|
123
124
|
return parts.join(" ");
|
|
124
125
|
}
|
|
126
|
+
/** The HTTP access record (`http/app.ts`) as one sentence: method, path, status and duration. */
|
|
127
|
+
function foldAccessLine(log) {
|
|
128
|
+
if (log.msg !== "request" || typeof log.method !== "string" || typeof log.url !== "string") {
|
|
129
|
+
return log;
|
|
130
|
+
}
|
|
131
|
+
const { method, url, status, ms, ...rest } = log;
|
|
132
|
+
return { ...rest, msg: `${method} ${url} ${status} ${ms}ms` };
|
|
133
|
+
}
|
|
134
|
+
function colorsOn(opts, isTTY) {
|
|
135
|
+
if (opts.colors !== undefined)
|
|
136
|
+
return opts.colors;
|
|
137
|
+
const force = process.env.FORCE_COLOR;
|
|
138
|
+
if (force !== undefined && force !== "0")
|
|
139
|
+
return true;
|
|
140
|
+
if (process.env.NO_COLOR)
|
|
141
|
+
return false;
|
|
142
|
+
return isTTY;
|
|
143
|
+
}
|
|
125
144
|
function consoleStream(opts) {
|
|
126
145
|
if (opts.write) {
|
|
127
146
|
const write = opts.write;
|
|
@@ -132,23 +151,50 @@ function consoleStream(opts) {
|
|
|
132
151
|
if (!LOG_FORMATS.includes(format)) {
|
|
133
152
|
throw new Error(`invalid log.format "${format}" (expected ${LOG_FORMATS.join(", ")})`);
|
|
134
153
|
}
|
|
135
|
-
const
|
|
154
|
+
const target = opts.out ?? process.stdout;
|
|
155
|
+
// A console whose reader is gone (the `tee` of `pnpm dev:kernel` stopped by Ctrl-C, a closed
|
|
156
|
+
// pipe) fails every write with EPIPE. Unhandled, each failure is an uncaught exception the
|
|
157
|
+
// process guard logs, which writes again: an endless chain that starves the event loop. So the
|
|
158
|
+
// first stream error ends console output for good (Loki, when configured, still gets records).
|
|
159
|
+
let broken = false;
|
|
160
|
+
target.on?.("error", () => {
|
|
161
|
+
broken = true;
|
|
162
|
+
});
|
|
163
|
+
const out = {
|
|
164
|
+
write: (s) => {
|
|
165
|
+
if (!broken)
|
|
166
|
+
target.write(s);
|
|
167
|
+
},
|
|
168
|
+
};
|
|
136
169
|
const isTTY = opts.isTTY ?? process.stdout.isTTY === true;
|
|
137
170
|
const pretty = format === "pretty" || (format === "auto" && isTTY);
|
|
138
171
|
if (!pretty)
|
|
139
172
|
return { write: (s) => out.write(s) };
|
|
140
|
-
|
|
173
|
+
const colors = colorsOn(opts, isTTY);
|
|
174
|
+
// In process (no worker thread): pino-pretty formats each record synchronously. The layout follows
|
|
175
|
+
// octok's buildPinoPrettyOptions: full local time, colored level, the bracket prefix, and
|
|
176
|
+
// the remaining fields as indented `key: value` lines below the message (errors with their stack).
|
|
141
177
|
const format_ = prettyFactory({
|
|
142
|
-
colorize:
|
|
143
|
-
colorizeObjects:
|
|
144
|
-
singleLine:
|
|
145
|
-
|
|
146
|
-
errorLikeObjectKeys: [],
|
|
147
|
-
translateTime: "SYS:HH:MM:ss.l",
|
|
178
|
+
colorize: colors,
|
|
179
|
+
colorizeObjects: colors,
|
|
180
|
+
singleLine: false,
|
|
181
|
+
translateTime: "SYS:standard",
|
|
148
182
|
ignore: ["pid", "hostname", "role", "spanId", ...PREFIX_FIELDS].join(","),
|
|
149
183
|
messageFormat: (log, messageKey) => prettyMessage(log, messageKey),
|
|
150
184
|
});
|
|
151
|
-
return {
|
|
185
|
+
return {
|
|
186
|
+
write: (s) => {
|
|
187
|
+
let record;
|
|
188
|
+
try {
|
|
189
|
+
record = JSON.parse(s);
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
out.write(s);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
out.write(format_(foldAccessLine(record)));
|
|
196
|
+
},
|
|
197
|
+
};
|
|
152
198
|
}
|
|
153
199
|
/**
|
|
154
200
|
* Creates the kernel logger on pino. Each record is `{ time, level, msg, ...base, ...fields }` with
|
|
@@ -254,7 +300,7 @@ export function createLogger(level = "info", opts = {}) {
|
|
|
254
300
|
}
|
|
255
301
|
sink[lvl](obj, redactMsg(msg));
|
|
256
302
|
};
|
|
257
|
-
|
|
303
|
+
const root = {
|
|
258
304
|
debug: (msg, fields) => emit("debug", msg, fields),
|
|
259
305
|
info: (msg, fields) => emit("info", msg, fields),
|
|
260
306
|
warn: (msg, fields) => emit("warn", msg, fields),
|
|
@@ -299,5 +345,35 @@ export function createLogger(level = "info", opts = {}) {
|
|
|
299
345
|
}
|
|
300
346
|
}
|
|
301
347
|
},
|
|
348
|
+
// a method, so a wrapper of this logger (a test proxy) is the parent its children forward to
|
|
349
|
+
child(fields) {
|
|
350
|
+
return childLogger(this, fields);
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
return root;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* A logger that adds `bound` to every record and forwards it to `parent`'s public methods (so a spy
|
|
357
|
+
* on the parent sees its children's records); call fields cannot overwrite the bound ones.
|
|
358
|
+
*/
|
|
359
|
+
function childLogger(parent, bound) {
|
|
360
|
+
const merged = (fields) => ({ ...fields, ...bound });
|
|
361
|
+
const child = {
|
|
362
|
+
debug: (msg, fields) => parent.debug(msg, merged(fields)),
|
|
363
|
+
info: (msg, fields) => parent.info(msg, merged(fields)),
|
|
364
|
+
warn: (msg, fields) => parent.warn(msg, merged(fields)),
|
|
365
|
+
error: (msg, fields) => parent.error(msg, merged(fields)),
|
|
366
|
+
get level() {
|
|
367
|
+
return parent.level;
|
|
368
|
+
},
|
|
369
|
+
setLevel: (level) => parent.setLevel(level),
|
|
370
|
+
addSecrets: (values) => parent.addSecrets(values),
|
|
371
|
+
addOrgSecrets: (values) => parent.addOrgSecrets(values),
|
|
372
|
+
redact: (text) => parent.redact(text),
|
|
373
|
+
flush: () => parent.flush(),
|
|
374
|
+
child(fields) {
|
|
375
|
+
return childLogger(this, fields);
|
|
376
|
+
},
|
|
302
377
|
};
|
|
378
|
+
return child;
|
|
303
379
|
}
|
|
@@ -35,6 +35,13 @@ export declare const KERNEL_PURPOSES: readonly [{
|
|
|
35
35
|
readonly zh: "会话标题";
|
|
36
36
|
readonly en: "Conversation titles";
|
|
37
37
|
};
|
|
38
|
+
}, {
|
|
39
|
+
readonly purpose: "web_fetch";
|
|
40
|
+
readonly kind: "chat";
|
|
41
|
+
readonly label: {
|
|
42
|
+
readonly zh: "网页提取";
|
|
43
|
+
readonly en: "Web page extraction";
|
|
44
|
+
};
|
|
38
45
|
}, {
|
|
39
46
|
readonly purpose: "subagent_fast";
|
|
40
47
|
readonly kind: "chat";
|
|
@@ -140,6 +147,19 @@ export declare class ModelPurposesService {
|
|
|
140
147
|
setOrg(orgId: string, purpose: string, target: PurposeTarget | null, actor: string, actorTokenId?: string): Promise<PurposeView>;
|
|
141
148
|
/** 生效的映射:组织覆盖,否则部署默认;都没有 → `model.unconfigured`;用途未声明 → `model.purpose_unknown`。 */
|
|
142
149
|
resolve(orgId: string, purpose: string): Promise<PurposeTarget>;
|
|
150
|
+
/** The target a {@link chat} call on `purpose` uses: the mapping, else `fallback` when unmapped. */
|
|
151
|
+
private chatTarget;
|
|
152
|
+
/**
|
|
153
|
+
* The limits of the model a {@link chat} call on `purpose` would use now (the same mapping and
|
|
154
|
+
* `fallback` rule): its context window and output cap, so a caller can size its request (e.g.
|
|
155
|
+
* the `WebFetch` page) before the call. Fails as `chat` does before sending.
|
|
156
|
+
*/
|
|
157
|
+
chatLimits(orgId: string, purpose: string, opts?: {
|
|
158
|
+
fallback?: PurposeTarget;
|
|
159
|
+
}): Promise<{
|
|
160
|
+
contextWindow: number;
|
|
161
|
+
maxOutputTokens: number;
|
|
162
|
+
}>;
|
|
143
163
|
/**
|
|
144
164
|
* 按用途发一次对话请求:解析映射、按目标模型的声明宽松处理参数(补默认值,与运行时一致),用 `consumeModelStream`
|
|
145
165
|
* 收完(只有流空闲看门狗)。输出上限取 `min(req.maxOutputTokens, 目标模型的 maxOutputTokens)`。
|
|
@@ -147,10 +167,13 @@ export declare class ModelPurposesService {
|
|
|
147
167
|
* `opts.fallback`: the target used when the purpose is not mapped (`model.unconfigured`).
|
|
148
168
|
* `opts.beforeSend`: called with the resolved target and the final request just before sending;
|
|
149
169
|
* what it throws is thrown as is (a pre-send failure, e.g. a denied `model.request` hook).
|
|
170
|
+
* `opts.paramDefaults`: values for parameters the target does not set, kept only where the
|
|
171
|
+
* target model declares the key and the value fits it (e.g. `thinking: "off"` for a short call).
|
|
150
172
|
*/
|
|
151
173
|
chat(orgId: string, purpose: string, req: Omit<ModelRequest, "model" | "params">, signal: AbortSignal, opts?: {
|
|
152
174
|
fallback?: PurposeTarget;
|
|
153
175
|
beforeSend?: (target: ModelRef, request: ModelRequest) => Promise<void>;
|
|
176
|
+
paramDefaults?: Record<string, ParamValue>;
|
|
154
177
|
}): Promise<{
|
|
155
178
|
content: ContentBlock[];
|
|
156
179
|
stopReason: StopReason;
|