@lynn123411/dsh-subagent-judge-dispatch 0.3.0 → 0.3.1
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/DETAILS.md +2 -2
- package/lib/index.js +19 -8
- package/lib/index.js.map +2 -2
- package/package.json +1 -1
package/DETAILS.md
CHANGED
|
@@ -36,7 +36,7 @@ dsh plugin --profile web add @lynn123411/dsh-subagent-judge-dispatch
|
|
|
36
36
|
- **插手位置**:宿主的 `agent/request` 瀑布。注册在根层上下文,所以每个代理的每次请求都会经过;插件自己用 `subagentDepth`(会话头里的 `delegationDepth` 与运行时 `options.subagentDepth` 取较大者)筛出子代理。
|
|
37
37
|
- **判断次数**:按子代理 id 记住已判过的,一个跑十步的子代理只问一次。
|
|
38
38
|
- **判断模型读到的任务**:先是派出这次活的标签(`subagent/descriptor` 的 `label`,宿主用它当子会话标题),再是任务正文原文,两段之间空一行。标签只有三五个词,是派工方写的摘要;正文才是「这个活到底要做什么」。只发标签,判断只能看出个大概方向;只发正文,遇到开头是套话的任务就丢了派工方的意图。
|
|
39
|
-
- **任务正文怎么取**:取子代理自己那条 `source.kind === 'user'`
|
|
39
|
+
- **任务正文怎么取**:取子代理自己那条 `source.kind === 'user'` 的消息。用户角色不等于任务——工作目录提示、工作区指令、运行时快照、技能清单、时间快照都写成用户消息,按来源筛才分得开。这条消息在子代理一生里以两种形状出现,两种都要认:派工那一刻它是收件箱里的待办(`agent/inbox/spliced` 事件里 `inserted` 的那条),宿主在步边界认领之后才写成 `user/message`。判断发生在步内的那次请求里,比写成 `user/message` 早,只认后者会在唯一要紧的那次请求上读空、只剩标签。宿主的返回指引(「你的父代理 id 是……完事用 `send_message` 回报」)是那条消息末尾追加的一个文本块,讲的是怎么交作业,不算任务,取正文时整块去掉。
|
|
40
40
|
- **只读子代理自己的事件**:`ownEvents()`(或 `snapshotEvents(inheritedEventCount)`)是子代理自己产生的那一段。**不能从日志开头读**:叉出来的子代理,日志前缀是父代理已完成的回合,从开头读会把祖先的标签和父对话的第一条消息送给判断模型。
|
|
41
41
|
- **长任务按头尾截**:正文留开头 1400 字与结尾 400 字,中间省掉多少字写在省略号里(`… (N characters omitted) …`)。补充要求常写在末尾,所以留尾不留中;把省掉的字数写出来,读者看日志里那段请求才分得清「截断了」和「本来就这么短」。这句标记用英文,是判断协议本身的语言——中文写在英文任务里像任务的一部分,读者与判断模型都会读岔。
|
|
42
42
|
- **日志那一栏与判断输入分开算**:表格「子代理」一栏只放派工标签(没有标签时退回正文开头,截到 60 字),不把判断模型读过的那一整段塞回去——那一整段在同一行的弹窗里能看到。
|
|
@@ -78,7 +78,7 @@ cd plugins/dsh-subagent-judge-dispatch && pnpm check
|
|
|
78
78
|
`check` = `typecheck` + `build` + 五个自检 + `assert:lib-untracked`:
|
|
79
79
|
|
|
80
80
|
- **`scripts/test-decision.mjs`** 打的是 `src/`(Node 直接跑 `.ts`),守判断与改写这条纯逻辑:失败时**按身份**退回同一个对象(不是深比较——深比较会放过「复制一份再写回原值」这种仍然算覆盖的写法)、三项同写、残缺行不生效、答复读取的三种形状与七种空答复、四种失败原因各自的归类、超时不重试、日志留 20 条且读出来是副本、编号按位置从 1 连着。它还守判断模型读到的输入:标签在前正文在后、没标签时只发正文、两样都没有时输入为空、短任务一字不动、长任务留头尾并写明省掉多少字、刚好到上限的不算截断。另有一条守「判断带回的提示词就是发出去的那一段」:成功、失败、没发出请求三种情形各一条。
|
|
81
|
-
- **`scripts/smoke-host.mjs`** 起真 cordis `Context`,把 `agent/request` 瀑布按宿主的方式跑一遍(`await next()` 拿到解析好的配置,再把返回值交给断言):主代理不被判断、同一子代理只判一次、两个开关各自的短路、发送出去的请求体形状、判断输入是标签加正文两段、正文按 `source.kind`
|
|
81
|
+
- **`scripts/smoke-host.mjs`** 起真 cordis `Context`,把 `agent/request` 瀑布按宿主的方式跑一遍(`await next()` 拿到解析好的配置,再把返回值交给断言):主代理不被判断、同一子代理只判一次、两个开关各自的短路、发送出去的请求体形状、判断输入是标签加正文两段、正文按 `source.kind` 取(工作目录提示不算任务)、派工消息还在收件箱里与已经落盘成 `user/message` 两种形状都读得到、宿主追加的返回指引不送进去、长任务留头尾且写明省掉多少字、没有描述时取任务正文、叉出来的子代理只读自己的事件、日志的子代理一栏只写标签、判断过的记忆有上限。它的夹具照真实日志的形状搭:先是 `working-directory` 的通知,再是收件箱里那份待办;两种形状各有一组用例,一组把消息留在收件箱,一组用落盘后的 `user/message`。它另起一个挂着 `connection` 的实例,从日志路由读回那一行,断言 `prompt` 与真正发出去的请求体同值——判断成功与判断失败各一条。
|
|
82
82
|
- **`scripts/smoke-client.mjs`** 按 Web shell 的方式装载 `lib/client.js`(`window.__ModuleLoader__.load` 桩件),喂假 cordis 服务:断言卡片注册进 `plugins.bundle.config`、key 是包名、走 `slots.inject`、读本插件自己的命名空间与宿主那份「子代理可选用哪些模型」设置、以宿主在服务本条目为挂载条件、样式表在 `apply()` 追加一次且没写死色值,以及 zh/en 键集一致。它的守卫带反例断言(「守卫拒绝未声明的服务读取」必须抛),否则那一串绿灯是假的。
|
|
83
83
|
|
|
84
84
|
- **`scripts/test-card.mjs`** 打的是 `src/`,守配置卡片这层状态机:两节各存各的、清单没填全不挡端点、宿主回 `false` 算没存上、密钥跟着判断服务那次保存一起写且写的是固定引用名、密钥值从不进页面状态、草稿按字段保留(宿主改了一项而我在改另一项时,保存只写我改的那一项),以及「选择」一列把三项连成一段且不留悬空的分隔点。
|
package/lib/index.js
CHANGED
|
@@ -211,18 +211,29 @@ function readChildLabel(agent) {
|
|
|
211
211
|
}
|
|
212
212
|
function readTaskText(agent) {
|
|
213
213
|
for (const event of ownEventsOf(agent.session)) {
|
|
214
|
-
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
214
|
+
const candidates = delegationMessagesOf(event);
|
|
215
|
+
for (const data of candidates) {
|
|
216
|
+
const blocks = textBlocksOf(data);
|
|
217
|
+
if (blocks.length > 1 && blocks[blocks.length - 1].startsWith(RETURN_GUIDANCE_PREFIX)) {
|
|
218
|
+
blocks.pop();
|
|
219
|
+
}
|
|
220
|
+
const text = blocks.join("\n").trim();
|
|
221
|
+
if (text !== "") return text;
|
|
220
222
|
}
|
|
221
|
-
const text = blocks.join("\n").trim();
|
|
222
|
-
if (text !== "") return text;
|
|
223
223
|
}
|
|
224
224
|
return "";
|
|
225
225
|
}
|
|
226
|
+
function delegationMessagesOf(event) {
|
|
227
|
+
if (event.type === "user/message") {
|
|
228
|
+
return sourceOf(event.data)?.kind === "user" ? [event.data] : [];
|
|
229
|
+
}
|
|
230
|
+
if (event.type === "agent/inbox/spliced") {
|
|
231
|
+
const inserted = event.data?.inserted;
|
|
232
|
+
if (!Array.isArray(inserted)) return [];
|
|
233
|
+
return inserted.filter((message) => sourceOf(message)?.kind === "user");
|
|
234
|
+
}
|
|
235
|
+
return [];
|
|
236
|
+
}
|
|
226
237
|
function sourceOf(data) {
|
|
227
238
|
if (typeof data !== "object" || data === null) return void 0;
|
|
228
239
|
const source = data.source;
|
package/lib/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/server/config.ts", "../src/server/judge-prompt.ts", "../src/server/credentials.ts", "../src/server/dispatch.ts", "../src/server/identity.ts", "../src/server/judge-client.ts", "../src/server/log-store.ts", "../src/server/router.ts", "../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * This plugin's Config schema and the live read face the wiring consumes.\n *\n * Every field is `volatile()`, which is what lets the configuration card on the\n * Plugins page commit an edit into these refs without remounting the plugin:\n * the wiring reads the current value on every delegation rather than a snapshot\n * taken when the Host loaded.\n *\n * The type-list rows carry no sequence number. The page assigns `1..n` from the\n * array order, the judge answers with that number, and the log shows it \u2014 so a\n * number can never be duplicated, and the reader never has to maintain one.\n */\n\nimport type { Volatile } from '@deepseek-ai/cordis';\nimport z from '@deepseek-ai/schemastery';\nimport type Schema from '@deepseek-ai/schemastery';\nimport { isSelectableRow } from './judge-prompt.ts';\nimport type { TypeRow } from './types.ts';\n\n/**\n * The settings namespace this plugin owns. A namespace IS the Profile entry id,\n * so the user-editable layer is this plugin's own row in the active profile's\n * patch \u2014 the same literal as the `id` in `cordis.patch.yml`, and a different\n * literal from the npm package name the card is keyed by.\n */\nexport const SETTINGS_NAMESPACE = 'dsh-subagent-judge-dispatch';\n\n/** Endpoint the page prefills: one System One example. */\nexport const DEFAULT_ENDPOINT_URL = 'http://127.0.0.1:3425/v1/systemone';\n\n/** Judge model the page prefills. */\nexport const DEFAULT_JUDGE_MODEL = 'future-jev/jev-latest';\n\n/** One type-list row as the configuration document stores it. */\nconst TypeRowSchema = z.object({\n /** Criterion text the judge reads for this type. */\n description: z.string().default(''),\n /** Provider route of the model this type runs on. */\n provider: z.string().default(''),\n /** Model id interpreted by that provider. */\n model: z.string().default(''),\n /** Reasoning effort id this type runs at. */\n effort: z.string().default(''),\n});\n\n/** This plugin's live configuration. */\nexport const Config: Schema = z.object({\n /** Whether a judgement may rewrite a subagent's model and effort. */\n enabled: z.boolean().default(true).volatile(),\n /** Whether judgements are written to the plugin's own log. */\n logEnabled: z.boolean().default(true).volatile(),\n /** Full judge endpoint URL. */\n endpointUrl: z.string().default(DEFAULT_ENDPOINT_URL).volatile(),\n /** Judge model id. */\n judgeModel: z.string().default(DEFAULT_JUDGE_MODEL).volatile(),\n /** The task-type list, in page order. */\n rows: z.array(TypeRowSchema).default([]).volatile(),\n});\n\n/** Fields of this plugin's Config as the runtime resolved them. */\nexport interface PluginConfigRefs {\n readonly enabled: Volatile<boolean>;\n readonly logEnabled: Volatile<boolean>;\n readonly endpointUrl: Volatile<string>;\n readonly judgeModel: Volatile<string>;\n readonly rows: Volatile<StoredRow[]>;\n}\n\n/** One stored row, before the page assigns its sequence number. */\nexport interface StoredRow {\n description: string;\n provider: string;\n model: string;\n effort: string;\n}\n\n/** A detached plain read of this plugin's configuration. */\nexport interface ResolvedConfig {\n enabled: boolean;\n logEnabled: boolean;\n endpointUrl: string;\n judgeModel: string;\n rows: TypeRow[];\n}\n\n/**\n * Read the current committed value of every Config field, numbering the rows.\n * @param refs - the runtime-resolved Config object.\n * @returns a detached plain config value.\n */\nexport function readPluginConfig(refs: PluginConfigRefs): ResolvedConfig {\n const stored = refs.rows.get();\n const rows: TypeRow[] = (Array.isArray(stored) ? stored : []).map((row, index) => ({\n // The number is positional, so deleting a row renumbers the rest. A log\n // entry written before that edit may therefore name a different type; the\n // reader accepted that when choosing continuous numbering.\n id: index + 1,\n description: typeof row?.description === 'string' ? row.description : '',\n provider: typeof row?.provider === 'string' ? row.provider : '',\n model: typeof row?.model === 'string' ? row.model : '',\n effort: typeof row?.effort === 'string' ? row.effort : '',\n }));\n return {\n enabled: refs.enabled.get() !== false,\n logEnabled: refs.logEnabled.get() !== false,\n endpointUrl: typeof refs.endpointUrl.get() === 'string' ? refs.endpointUrl.get() : DEFAULT_ENDPOINT_URL,\n judgeModel: typeof refs.judgeModel.get() === 'string' ? refs.judgeModel.get() : DEFAULT_JUDGE_MODEL,\n rows,\n };\n}\n\n/**\n * Whether a judgement would do anything at all: the model-rewrite switch is on,\n * a judge endpoint and model are named, and at least one row is selectable.\n *\n * @param config - the resolved configuration.\n * @returns whether a delegation would be judged.\n */\nexport function isJudgeUsable(config: ResolvedConfig): boolean {\n if (!config.enabled) return false;\n if (config.endpointUrl.trim() === '' || config.judgeModel.trim() === '') return false;\n return config.rows.some(isSelectableRow);\n}\n", "/**\n * The judgement request the plugin sends, and the answer it reads back.\n *\n * Two pure functions live here, so the whole \"what did the judge decide\"\n * question is answerable without a Host, a socket, or a timer:\n *\n * - {@link buildJudgeRequest} turns the type list plus the task text into the\n * exact body that goes out.\n * - {@link readJudgeAnswer} turns whatever came back into a decision, treating\n * every malformed, empty or off-list answer as \"no decision\" rather than\n * inventing one.\n *\n * The protocol is the System One one: POST a body of `{ model, state,\n * questions }`, where each question is a `choice` carrying an `instructions`\n * line and a `criteria` map of option id to the text that option means. The\n * answer names the chosen option id.\n */\n\nimport type { TypeRow } from './types.ts';\n\n/** The one question key the plugin asks. */\nexport const QUESTION_KEY = 'task_type';\n\n/**\n * The question's instructions line.\n *\n * It says what to weigh, not what the types are: the types themselves arrive as\n * the criteria map, so the reader's own descriptions are the whole criterion\n * list and nothing here can drift from them.\n */\nexport const QUESTION_INSTRUCTIONS =\n 'Which single task type below does the delegated job best match? '\n + 'Weigh what the job actually requires, not how it is phrased. '\n + 'Answer with the number of exactly one listed type.';\n\n/** The option id the connectivity check offers: the only one it offers. */\nexport const PROBE_OPTION_ID = '1';\n\n/** The task text the connectivity check sends. */\nexport const PROBE_TASK_TEXT = '\u8FDE\u63A5\u6D4B\u8BD5\uFF1A\u8FD9\u662F\u4E00\u6B21\u5224\u65AD\u670D\u52A1\u7684\u8FDE\u901A\u6027\u68C0\u67E5\u3002';\n\n/**\n * The connectivity check's instructions.\n *\n * It asks for the one option that exists, so an endpoint that understands the\n * protocol at all can answer it: answering it wrong is a failure of the\n * protocol, not of the reader's task list.\n */\nexport const PROBE_INSTRUCTIONS =\n 'Answer with the number of the one option listed below.';\n\n/** The single criterion the connectivity check offers. */\nexport const PROBE_CRITERION = '\u786E\u8BA4\u8FD9\u6B21\u8C03\u7528\u53EF\u4EE5\u5B8C\u6210\u3002';\n\n/** One option of the judge's question. */\nexport interface JudgeChoice {\n type: 'choice';\n instructions: string;\n criteria: Record<string, string>;\n}\n\n/** The exact body sent to the judge endpoint. */\nexport interface JudgeRequest {\n model: string;\n state: { message: string };\n questions: { [QUESTION_KEY]: JudgeChoice };\n}\n\n/**\n * Whether one row is complete enough to be offered to the judge.\n *\n * A row needs its provider, model and effort: a half-filled row could otherwise\n * be chosen and then have nothing to write, which would turn the reader's\n * unfinished edit into a delegation that silently lost its route. This is the\n * ONE place that rule lives \u2014 the page, the config gate and the rewrite all ask\n * this function rather than restating the three comparisons.\n *\n * The parameter is narrowed to the three fields the rule reads, so the page's\n * draft row (which carries no sequence number) and the server's numbered row\n * both satisfy it without either shape being converted into the other.\n *\n * @param row - one row, numbered or drafted.\n * @returns whether the judge may choose it.\n */\nexport function isSelectableRow(row: Pick<TypeRow, 'provider' | 'model' | 'effort'>): boolean {\n return row.provider.trim() !== '' && row.model.trim() !== '' && row.effort.trim() !== '';\n}\n\n/**\n * Which rows may be offered to the judge.\n * @param rows - every row on the page, in page order.\n * @returns the rows the judge may choose, in page order.\n */\nexport function selectableRows(rows: readonly TypeRow[]): TypeRow[] {\n return rows.filter(isSelectableRow);\n}\n\n/**\n * How much of a delegated task's opening the judge reads.\n *\n * The judge answers one multiple-choice question; the opening of a task says\n * what kind of job it is, and the rest is usually detail the choice does not\n * turn on. Bounding it keeps the request small and keeps one enormous\n * delegation from crowding out the type list it is being weighed against.\n */\nexport const SUBJECT_HEAD_CHARS = 1400;\n\n/**\n * How much of a task's closing the judge reads.\n *\n * Supplementary requirements are written at the end \u2014 \"also run the review\" \u2014\n * so the tail is kept rather than the middle.\n */\nexport const SUBJECT_TAIL_CHARS = 400;\n\n/** Opening words of the label line the judge reads above the task. */\nexport const LABEL_PREFIX = 'Delegation label: ';\n\n/**\n * Build the subject the judge is asked to classify.\n *\n * Two parts, in the order the reader would put them: the delegation's own label\n * \u2014 a summary the delegating agent wrote, which carries its intent \u2014 and then\n * the task text itself. The label alone is a display phrase of a few words, too\n * little to classify by; the task text alone loses that intent when it opens\n * with boilerplate.\n *\n * @param label - the delegation label, or '' when the child has none.\n * @param taskText - the delegated task text, or '' when it cannot be read.\n * @returns the subject to send, bounded to {@link SUBJECT_HEAD_CHARS} plus\n * {@link SUBJECT_TAIL_CHARS} characters of task text.\n */\nexport function composeJudgeSubject(label: string, taskText: string): string {\n const task = clipTaskText(taskText.trim());\n const heading = label.trim();\n if (heading === '') return task;\n return task === '' ? LABEL_PREFIX + heading : `${LABEL_PREFIX}${heading}\\n\\n${task}`;\n}\n\n/**\n * Bound a task text to its opening and closing.\n *\n * The dropped middle is named rather than silently removed, so a reader looking\n * at the logged request can tell truncation from a short task. The marker is\n * English like the rest of the protocol, so it reads as a note from the plugin\n * rather than as part of the task, whatever language the task is written in.\n *\n * @param text - the task text.\n * @returns the text, or its head and tail around a count of what was dropped.\n */\nfunction clipTaskText(text: string): string {\n if (text.length <= SUBJECT_HEAD_CHARS + SUBJECT_TAIL_CHARS) return text;\n const dropped = text.length - SUBJECT_HEAD_CHARS - SUBJECT_TAIL_CHARS;\n const head = text.slice(0, SUBJECT_HEAD_CHARS);\n const tail = text.slice(text.length - SUBJECT_TAIL_CHARS);\n return `${head}\\n\u2026 (${dropped} characters omitted) \u2026\\n${tail}`;\n}\n\n/**\n * Build the exact body sent to the judge endpoint.\n *\n * @param rows - the selectable rows, in page order.\n * @param model - the judge model id.\n * @param subject - the text the judge classifies, from {@link composeJudgeSubject}.\n * @returns the request body.\n */\nexport function buildJudgeRequest(\n rows: readonly TypeRow[],\n model: string,\n subject: string,\n): JudgeRequest {\n const criteria: Record<string, string> = {};\n for (const row of rows) criteria[String(row.id)] = row.description;\n return {\n model,\n state: { message: subject },\n questions: {\n [QUESTION_KEY]: {\n type: 'choice',\n instructions: QUESTION_INSTRUCTIONS,\n criteria,\n },\n },\n };\n}\n\n/**\n * Build the body the connectivity check sends.\n *\n * It carries its own single option rather than the reader's task types: the\n * question the button answers is \"does this endpoint and model work\", and that\n * question has an answer before any type list exists. Requiring a finished list\n * first would make the check unable to tell a reader with an empty list from a\n * reader whose endpoint is down.\n *\n * @param model - the judge model id.\n * @returns the request body.\n */\nexport function buildProbeRequest(model: string): JudgeRequest {\n return {\n model,\n state: { message: PROBE_TASK_TEXT },\n questions: {\n [QUESTION_KEY]: {\n type: 'choice',\n instructions: PROBE_INSTRUCTIONS,\n criteria: { [PROBE_OPTION_ID]: PROBE_CRITERION },\n },\n },\n };\n}\n\n/**\n * Serialize one request body the way the page shows it.\n *\n * Both the preview and a logged judgement display this text, while the call\n * itself sends the same object compacted onto the wire. Deriving both from one\n * body is what keeps the shown text from describing a different request than\n * the one that went out.\n *\n * @param body - the request body.\n * @returns the pretty-printed JSON.\n */\nexport function formatJudgeRequest(body: JudgeRequest): string {\n return JSON.stringify(body, null, 2);\n}\n\n/**\n * The text the configuration page's preview shows.\n *\n * It is the request body minus the subject, because no delegation exists until\n * one happens; the preview marks both spots the subject will fill rather than\n * inventing a task. The two markers are told apart, because the real subject is\n * a label line followed by the task text and the preview should read that way.\n *\n * @param rows - the selectable rows, in page order.\n * @param model - the judge model id.\n * @returns the pretty-printed preview text.\n */\nexport function buildPreview(rows: readonly TypeRow[], model: string): string {\n return formatJudgeRequest(\n buildJudgeRequest(rows, model, composeJudgeSubject('\uFF08\u6D3E\u5DE5\u65F6\u624D\u586B\uFF1A\u6D3E\u5DE5\u6807\u7B7E\uFF09', '\uFF08\u6D3E\u5DE5\u65F6\u624D\u586B\uFF1A\u4EFB\u52A1\u6B63\u6587\uFF09')),\n );\n}\n\n/** What reading the judge's answer produced. */\nexport type JudgeDecision =\n | { kind: 'row'; row: TypeRow }\n | { kind: 'empty' }\n | { kind: 'unmatched' };\n\n/**\n * Find the chosen option id inside whatever the endpoint answered.\n *\n * The protocol's own shape is `answers.<key>.choice`; the two fallbacks are the\n * ones the reference clients read, so an endpoint that answers the shorter way\n * still works. Anything else \u2014 a missing body, a non-string, an empty string \u2014\n * yields `undefined` and is treated as no answer.\n *\n * @param payload - the decoded response body.\n * @returns the chosen option id, or undefined when the body names none.\n */\nexport function readChoice(payload: unknown): string | undefined {\n if (typeof payload !== 'object' || payload === null) return undefined;\n const body = payload as Record<string, unknown>;\n const answers = body.answers;\n const fromAnswers =\n typeof answers === 'object' && answers !== null\n ? (answers as Record<string, unknown>)[QUESTION_KEY]\n : undefined;\n const candidate = fromAnswers ?? body[QUESTION_KEY];\n if (typeof candidate === 'object' && candidate !== null) {\n const choice = (candidate as Record<string, unknown>).choice;\n if (typeof choice === 'string' && choice.trim() !== '') return choice.trim();\n if (typeof choice === 'number' && Number.isFinite(choice)) return String(choice);\n return undefined;\n }\n if (typeof candidate === 'string' && candidate.trim() !== '') return candidate.trim();\n if (typeof candidate === 'number' && Number.isFinite(candidate)) return String(candidate);\n return undefined;\n}\n\n/**\n * Turn the judge's answer into a decision.\n *\n * The choice is the type row's number. Matching is by that number's numeric\n * value, so an endpoint that answers `\"3\"` and one that answers `3` agree; a\n * number no listed row carries is `unmatched`, and a body naming no choice at\n * all is `empty`. Both travel to the log as their own reason, because \"it\n * answered nothing\" and \"it answered something I do not have\" send the reader\n * to different fixes.\n *\n * @param payload - the decoded response body.\n * @param rows - the selectable rows, in page order.\n * @returns the decision.\n */\nexport function readJudgeAnswer(payload: unknown, rows: readonly TypeRow[]): JudgeDecision {\n const choice = readChoice(payload);\n if (choice === undefined) return { kind: 'empty' };\n const wanted = Number(choice);\n if (!Number.isFinite(wanted)) return { kind: 'unmatched' };\n const row = rows.find((candidate) => candidate.id === wanted);\n return row === undefined ? { kind: 'unmatched' } : { kind: 'row', row };\n}\n", "/**\n * Where the judgement key lives, and the Host service it is read through.\n *\n * The reference name is a constant of the plugin rather than a setting: the\n * page asks for the key itself and stores it write-only through the DSH\n * credentials service, so a reader never has to know what a \"credential\n * reference\" is, and no surface can point the plugin at somebody else's key.\n * This module imports nothing, so both halves can share the literal.\n *\n * The key value crosses in one direction only: it is written, and read back by\n * the Host half alone. The browser face reads a status-only view.\n */\n\n/** Credential reference the judgement key is stored under. */\nexport const JUDGE_API_KEY_REF = 'SUBAGENT_JUDGE_API_KEY';\n\n/** Minimal structural shape of the Host's `ctx.credentials` service. */\nexport interface CredentialsServiceLike {\n /** Resolve a reference to its stored or inherited value. */\n resolve(ref: string): Promise<{ value: string; source?: string } | undefined>;\n /** Status-only view of a reference; never returns the plaintext value. */\n describe(ref: string): Promise<{ configured: boolean; source?: string; writable: boolean }>;\n /** Store a non-empty value under a reference. */\n set(ref: string, value: string): Promise<void>;\n /** Remove a reference from the document. */\n unset(ref: string): Promise<void>;\n}\n\n/** Minimal structural shape of the browser's `ctx.remote.credentials` face. */\nexport interface CredentialsRemoteLike {\n describe(refs: string[]): Promise<\n { ok: true; value: Record<string, { configured: boolean; source?: string; writable: boolean }> }\n | { ok: false; error: { message: string } }\n >;\n set(ref: string, value: string): Promise<{ ok: true } | { ok: false; error: { message: string } }>;\n unset(ref: string): Promise<{ ok: true } | { ok: false; error: { message: string } }>;\n}\n", "/**\n * The decision half: given what the judge said, decide exactly what to write\n * onto a subagent's next model call \u2014 or write nothing at all.\n *\n * This module holds the one rule that matters most, so it is a pure function\n * that a test can drive directly: **any** judgement that did not end in a\n * usable row writes nothing. Not \"writes the inherited values\", not \"writes a\n * partial route\" \u2014 nothing. Writing anything is a replacement, so a fallback\n * that wrote the parent's own values back would still be an overwrite, and a\n * partial write would drop the inherited reasoning effort\n * (`resolveChildAgentOptions` deletes it when a route changes without one).\n */\n\nimport { isSelectableRow } from './judge-prompt.ts';\nimport type { FallbackReason, JudgeOutcome, TypeRow } from './types.ts';\n\n/** The three fields a rewrite carries, all present or none at all. */\nexport interface RouteRewrite {\n provider: string;\n model: string;\n reasoningEffort: string;\n}\n\n/** What to write for one judged subagent. */\nexport type DispatchDecision =\n | { kind: 'rewrite'; rewrite: RouteRewrite }\n | { kind: 'none' };\n\n/**\n * Turn a judgement into the rewrite it authorises.\n *\n * The three fields travel as one object because they must be written as one:\n * a route change without an explicit effort makes the Host drop the inherited\n * effort, so a caller can never be handed two of the three.\n *\n * @param outcome - what the judge call produced.\n * @returns the rewrite, or `none` when the child must inherit unchanged.\n */\nexport function decideRewrite(outcome: JudgeOutcome): DispatchDecision {\n if (outcome.kind !== 'matched') return { kind: 'none' };\n const row: TypeRow = outcome.row;\n // A row the judge could select but that is missing a piece is not a rewrite\n // this plugin may make; leaving the child alone is always safe.\n if (!isSelectableRow(row)) return { kind: 'none' };\n return {\n kind: 'rewrite',\n rewrite: {\n provider: row.provider.trim(),\n model: row.model.trim(),\n reasoningEffort: row.effort.trim(),\n },\n };\n}\n\n/**\n * Why a delegation ended up inheriting, as the log reports it.\n *\n * The judge answering nothing, the judge answering a number this list does not\n * have, an unreachable endpoint and an expired budget are four different fixes\n * for the reader \u2014 \"it answered nothing\" points at the endpoint, \"no listed\n * type\" points at the descriptions. A selected row that turned out to be missing\n * a field is a fifth: the judgement worked, the row it named was not usable.\n *\n * A judgement that produced a rewrite has no reason at all.\n *\n * @param outcome - what the judge call produced.\n * @param decision - what was written for it.\n * @returns the reason to record, or undefined when the rewrite landed.\n */\nexport function outcomeReason(\n outcome: JudgeOutcome,\n decision: DispatchDecision,\n): FallbackReason | undefined {\n if (decision.kind === 'rewrite') return undefined;\n if (outcome.kind !== 'matched') return outcome.reason;\n return 'incomplete';\n}\n\n/**\n * Apply a rewrite onto one model-call configuration.\n *\n * The incoming configuration is returned untouched on `none`, which is what\n * makes \"the child inherits exactly what the main agent runs\" true rather than\n * approximated: no field is restated, so nothing about the call changes.\n *\n * @param config - the call configuration the Host resolved.\n * @param decision - what to write.\n * @returns the configuration to use.\n */\nexport function applyDecision<T extends { provider: string; model: string; reasoningEffort?: unknown }>(\n config: T,\n decision: DispatchDecision,\n): T {\n if (decision.kind === 'none') return config;\n return {\n ...config,\n provider: decision.rewrite.provider,\n model: decision.rewrite.model,\n reasoningEffort: decision.rewrite.reasoningEffort,\n };\n}\n", "/**\n * Reading one subagent's identity, from the child Agent alone.\n *\n * Inside `agent/request` the plugin holds a child Agent and nothing else: no\n * delegation record, no parent handle. Everything the log needs \u2014 what the child\n * was asked to do, and which conversation asked for it \u2014 has to be read back out\n * of the child's own durable session facts.\n */\n\n/**\n * The read surface of a Session this module needs; kept structural for tests.\n *\n * `ownEvents` (or `snapshotEvents(inheritedEventCount)`) is the child-owned tail\n * of the log. It matters: a forked child's log opens with the parent's finished\n * turns, so reading from the start would hand the judge the ANCESTOR's\n * descriptor label and the parent conversation's first message.\n */\nexport interface SessionLike {\n readonly header: { readonly parentSession?: string; readonly delegationDepth?: number };\n readonly inheritedEventCount?: number;\n ownEvents?(): readonly SessionEventLike[];\n snapshotEvents(from?: number): readonly SessionEventLike[];\n}\n\n/** One session event as this module reads it. */\nexport interface SessionEventLike {\n readonly type: string;\n readonly data?: unknown;\n}\n\n/** The read surface of an Agent this module needs. */\nexport interface AgentLike {\n readonly id: string;\n readonly options: { readonly subagentDepth?: number };\n readonly session: SessionLike;\n}\n\n/**\n * Read only the events this child itself produced.\n *\n * A forked child inherits the parent's completed prefix, and the inherited part\n * carries an ancestor's descriptor and the parent's own messages. Everything\n * this module reads must come from the child-owned tail, or the judge is told\n * about a different agent's job. Three shapes are accepted because the session\n * type is structural here: `ownEvents()`, `snapshotEvents(from)` with the\n * inherited count, and a plain full read as the last resort.\n *\n * @param session - the child's session.\n * @returns the child-owned events, in log order.\n */\nexport function ownEventsOf(session: SessionLike): readonly SessionEventLike[] {\n if (typeof session.ownEvents === 'function') return session.ownEvents();\n const inherited = session.inheritedEventCount;\n if (typeof inherited === 'number' && Number.isSafeInteger(inherited) && inherited > 0) {\n return session.snapshotEvents(inherited);\n }\n return session.snapshotEvents();\n}\n\n/**\n * Whether one agent is a subagent, rather than a top-level conversation.\n *\n * Two sources are consulted because neither alone is complete: the durable\n * header is authoritative and monotone (a resumed child keeps its depth), while\n * `options.subagentDepth` is stamped onto the live options and may deepen the\n * count but never lower it. Absence in both means a top-level agent.\n *\n * @param agent - the agent making the request.\n * @returns whether this is a subagent.\n */\nexport function isSubagent(agent: AgentLike): boolean {\n const fromHeader = agent.session.header.delegationDepth;\n const fromOptions = agent.options.subagentDepth;\n const depth = Math.max(\n typeof fromHeader === 'number' && Number.isSafeInteger(fromHeader) ? fromHeader : 0,\n typeof fromOptions === 'number' && Number.isSafeInteger(fromOptions) ? fromOptions : 0,\n );\n return depth >= 1;\n}\n\n/** Opening words of the return guidance the Host appends to a delegation. */\nexport const RETURN_GUIDANCE_PREFIX = 'Your parent agent id is ';\n\n/**\n * Read the child's delegation description.\n *\n * The subagent tool passes the model's `description` as the activation label,\n * and the child's durable descriptor keeps it \u2014 which is why it is readable here\n * without the parent's tool call. Only the child's OWN events are read, and the\n * LAST descriptor wins: a resumed or re-activated child appends a fresh one, and\n * the newest is the delegation actually running.\n *\n * The label is a display summary the delegating agent wrote, so it is returned\n * on its own and never merged with the task text: the judge gets the label as a\n * heading over the task, and the log's subagent column gets the label alone.\n *\n * @param agent - the subagent.\n * @returns the description, or '' when its descriptor carries none.\n */\nexport function readChildLabel(agent: AgentLike): string {\n let found = '';\n for (const event of ownEventsOf(agent.session)) {\n if (event.type !== 'subagent/descriptor') continue;\n const data = event.data;\n if (typeof data !== 'object' || data === null) continue;\n const label = (data as { label?: unknown }).label;\n if (typeof label === 'string' && label.trim() !== '') found = label.trim();\n }\n return found;\n}\n\n/**\n * Read the task the parent delegated.\n *\n * The delegation arrives as the child's first message whose `source.kind` is\n * `user`; every other producer writes user-role messages too \u2014 the working\n * directory notice, workspace instructions, the runtime snapshot, the skill\n * catalog, the time snapshot \u2014 so the source is what separates the task from\n * the context wrapped around it.\n *\n * The Host appends its return guidance as one trailing block of that same\n * message. It tells the child how to report back, not what the job is, so it is\n * dropped: leaving it in would spend most of the subject on boilerplate.\n *\n * Only the child's OWN events are read, because a forked child's log opens with\n * the parent's finished turns.\n *\n * @param agent - the subagent.\n * @returns the delegated task text, or '' when no delegation message exists.\n */\nexport function readTaskText(agent: AgentLike): string {\n for (const event of ownEventsOf(agent.session)) {\n if (event.type !== 'user/message') continue;\n const source = sourceOf(event.data);\n if (source?.kind !== 'user') continue;\n const blocks = textBlocksOf(event.data);\n // The guidance is appended last; a task that happens to open with the same\n // words is still honored because only the trailing block is examined.\n if (blocks.length > 1 && blocks[blocks.length - 1].startsWith(RETURN_GUIDANCE_PREFIX)) {\n blocks.pop();\n }\n const text = blocks.join('\\n').trim();\n if (text !== '') return text;\n }\n return '';\n}\n\n/** Read the durable producer facts off one message payload. */\nfunction sourceOf(data: unknown): { kind: string } | undefined {\n if (typeof data !== 'object' || data === null) return undefined;\n const source = (data as { source?: unknown }).source;\n if (typeof source !== 'object' || source === null) return undefined;\n const kind = (source as { kind?: unknown }).kind;\n return typeof kind === 'string' ? { kind } : undefined;\n}\n\n/** Pull the text blocks out of a message payload, in order. */\nfunction textBlocksOf(data: unknown): string[] {\n if (typeof data !== 'object' || data === null) return [];\n const content = (data as { content?: unknown }).content;\n if (!Array.isArray(content)) return [];\n const texts: string[] = [];\n for (const block of content) {\n if (typeof block !== 'object' || block === null) continue;\n const record = block as { type?: unknown; text?: unknown };\n if (record.type === 'text' && typeof record.text === 'string') texts.push(record.text);\n }\n return texts;\n}\n\n/**\n * Walk up a child's lineage to the top-level conversation that started it.\n *\n * A nested subagent's direct parent is another subagent, and the log's job is to\n * name the conversation the reader recognises \u2014 so the walk continues until a\n * session names no parent. The walk is bounded, because a resumed or forged\n * lineage must not be able to spin this loop.\n *\n * @param startId - the child's direct parent session id.\n * @param lookup - resolves a session id to its live session.\n * @returns the top-level session id, or the starting id when the walk stops.\n */\nexport function resolveRootSessionId(\n startId: string,\n lookup: (id: string) => SessionLike | undefined,\n): string {\n const seen = new Set<string>([startId]);\n let current = startId;\n for (let hop = 0; hop < 32; hop += 1) {\n const session = lookup(current);\n const parent = session?.header.parentSession;\n if (typeof parent !== 'string' || parent === '' || seen.has(parent)) return current;\n seen.add(parent);\n current = parent;\n }\n return current;\n}\n", "/**\n * The judge call itself: one POST, one budget, no retry.\n *\n * The call can fail in exactly four ways \u2014 unreachable, timed out, an answer\n * that names no option, an answer whose row is unusable \u2014 and every one of them\n * ends the same way for the caller: no rewrite happens and the child inherits\n * what the main agent is running. That is why nothing here throws: a delegation\n * must never fail because the judge was unavailable.\n */\n\nimport {\n PROBE_OPTION_ID,\n buildJudgeRequest,\n buildProbeRequest,\n formatJudgeRequest,\n readJudgeAnswer,\n} from './judge-prompt.ts';\nimport type { FallbackReason, JudgeOutcome, ProbeOutcome, TypeRow } from './types.ts';\n\n/** How long a judgement may take before it is abandoned. */\nexport const JUDGE_TIMEOUT_MS = 5000;\n\n/** What the judge client needs from the Host to make one call. */\nexport interface JudgeEndpoint {\n /** Full endpoint URL, including the protocol path. */\n url: string;\n /** Judge model id. */\n model: string;\n /** Authorization value, or '' when the endpoint needs none. */\n apiKey: string;\n}\n\n/** Structural `fetch` so tests can supply their own. */\nexport type FetchLike = (input: string, init: RequestInit) => Promise<Response>;\n\n/**\n * Ask the judge whether this endpoint and model can answer at all.\n *\n * This is a different question from a judgement, so it sends its own request\n * with its own single option: the reader uses the button to find out whether\n * the endpoint and model work, which is answerable before any task type has\n * been written. It never throws for the same reason a judgement never does \u2014\n * the page reports the reason, it does not handle an exception.\n *\n * @param endpoint - where and how to call the judge.\n * @param fetchImpl - the fetch to call; defaults to the global one.\n * @returns whether the endpoint answered, and how long it took.\n */\nexport async function probeJudge(\n endpoint: JudgeEndpoint,\n fetchImpl: FetchLike = fetch,\n): Promise<ProbeOutcome> {\n const startedAt = Date.now();\n const elapsed = (): number => Date.now() - startedAt;\n if (endpoint.url.trim() === '' || endpoint.model.trim() === '') {\n return { ok: false, latencyMs: elapsed(), reason: 'unconfigured' };\n }\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), JUDGE_TIMEOUT_MS);\n let timedOut = false;\n const onTimeout = (): void => {\n timedOut = true;\n };\n controller.signal.addEventListener('abort', onTimeout);\n\n try {\n const response = await fetchImpl(endpoint.url.trim(), {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n ...endpoint.apiKey.trim() === '' ? {} : { authorization: `Bearer ${endpoint.apiKey.trim()}` },\n },\n body: JSON.stringify(buildProbeRequest(endpoint.model.trim())),\n signal: controller.signal,\n });\n if (!response.ok) return { ok: false, latencyMs: elapsed(), reason: 'unreachable' };\n const payload: unknown = await response.json();\n // The check asked for the one option it offered: any answer that names a\n // different option means the endpoint did not read the request it was sent.\n return readJudgeAnswer(payload, [{\n id: Number(PROBE_OPTION_ID),\n description: '',\n provider: '',\n model: '',\n effort: '',\n }]).kind === 'row'\n ? { ok: true, latencyMs: elapsed() }\n : { ok: false, latencyMs: elapsed(), reason: 'unmatched' };\n } catch {\n return { ok: false, latencyMs: elapsed(), reason: timedOut ? 'timeout' : 'unreachable' };\n } finally {\n clearTimeout(timer);\n controller.signal.removeEventListener('abort', onTimeout);\n }\n}\n\n/**\n * Ask the judge which type one delegated job is.\n *\n * Never throws and never lets an abort cancel the delegation: every failure is\n * reported as a `fallback` outcome carrying its own reason, and the caller\n * writes nothing.\n *\n * @param endpoint - where and how to call the judge.\n * @param rows - the selectable rows, in page order.\n * @param subject - the text to classify, from `composeJudgeSubject`.\n * @param fetchImpl - the fetch to call; defaults to the global one.\n * @returns the decision, or the fallback reason that replaced it.\n */\nexport async function judgeTaskType(\n endpoint: JudgeEndpoint,\n rows: readonly TypeRow[],\n subject: string,\n fetchImpl: FetchLike = fetch,\n): Promise<JudgeOutcome> {\n const startedAt = Date.now();\n const elapsed = (): number => Date.now() - startedAt;\n if (endpoint.url.trim() === '' || endpoint.model.trim() === '' || rows.length === 0) {\n return { kind: 'fallback', reason: 'unreachable', latencyMs: elapsed(), prompt: '' };\n }\n\n const body = buildJudgeRequest(rows, endpoint.model.trim(), subject);\n // The body the log keeps is the one sent, not a re-built one: the reader\n // opens it to see exactly what the judge was asked.\n const prompt = formatJudgeRequest(body);\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), JUDGE_TIMEOUT_MS);\n let timedOut = false;\n const onTimeout = (): void => {\n timedOut = true;\n };\n controller.signal.addEventListener('abort', onTimeout);\n\n try {\n const response = await fetchImpl(endpoint.url.trim(), {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n // Sent only when there is a key: an unauthenticated local endpoint\n // would otherwise receive an empty Bearer and reject the call itself.\n ...endpoint.apiKey.trim() === '' ? {} : { authorization: `Bearer ${endpoint.apiKey.trim()}` },\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n if (!response.ok) {\n return { kind: 'fallback', reason: 'unreachable', latencyMs: elapsed(), prompt };\n }\n const payload: unknown = await response.json();\n const decision = readJudgeAnswer(payload, rows);\n if (decision.kind === 'empty') {\n return { kind: 'fallback', reason: 'unmatched', latencyMs: elapsed(), prompt };\n }\n if (decision.kind === 'unmatched') {\n return { kind: 'fallback', reason: 'unmatched', latencyMs: elapsed(), prompt };\n }\n return { kind: 'matched', row: decision.row, latencyMs: elapsed(), prompt };\n } catch {\n const reason: FallbackReason = timedOut ? 'timeout' : 'unreachable';\n return { kind: 'fallback', reason, latencyMs: elapsed(), prompt };\n } finally {\n clearTimeout(timer);\n controller.signal.removeEventListener('abort', onTimeout);\n }\n}\n", "/**\n * The plugin's own log, kept in the Host's memory.\n *\n * The Host's native subagent list has no model column and a plugin cannot add\n * one, so this log is the only place a delegation's assigned route is visible.\n * It is deliberately disposable: it lives for one Host process and is gone on\n * restart, which is what the reader asked for.\n */\n\nimport type { LogEntry } from './types.ts';\n\n/** How many entries the page keeps. */\nexport const LOG_LIMIT = 20;\n\n/** A bounded, newest-first ring of log entries. */\nexport class LogStore {\n private entries: LogEntry[] = [];\n\n /**\n * Append one entry, dropping the oldest beyond {@link LOG_LIMIT}.\n * @param entry - the entry to append.\n */\n append(entry: LogEntry): void {\n this.entries = [entry, ...this.entries].slice(0, LOG_LIMIT);\n }\n\n /**\n * Read the log, newest first.\n * @returns a detached copy, so a reader cannot mutate the ring.\n */\n list(): LogEntry[] {\n return this.entries.map((entry) => ({ ...entry }));\n }\n}\n", "/**\n * This plugin's exact Fetch routes, carried by Connection's authenticated\n * `/api` transport below the shared channel.\n *\n * Three surfaces, and no fourth: configuration is read and written through\n * DSH's own shared configuration form (`ctx.configForms`) and the `credentials`\n * Remote API, so nothing here touches either. What is left is what only the\n * Host knows \u2014 the runtime log, the exact text a judgement would send, and\n * whether the judge endpoint answers at all.\n */\n\nimport type { ConnectionFetchRoute } from '@deepseek-ai/dsh-client-connection';\nimport { buildPreview, selectableRows } from './judge-prompt.ts';\nimport { probeJudge } from './judge-client.ts';\nimport type { ResolvedConfig } from './config.ts';\nimport type { LogEntry } from './types.ts';\n\n/** Runtime log route path. */\nexport const LOG_ROUTE_PATH = '/api/dsh-subagent-judge-dispatch/log';\n\n/** Exact judge-prompt preview route path. */\nexport const PREVIEW_ROUTE_PATH = '/api/dsh-subagent-judge-dispatch/preview';\n\n/** Judge endpoint probe route path. */\nexport const PROBE_ROUTE_PATH = '/api/dsh-subagent-judge-dispatch/probe';\n\nfunction sendJson(status: number, body: unknown): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'Content-Type': 'application/json; charset=utf-8' },\n });\n}\n\n/** What one probe reports back to the page. */\nexport interface ProbeResult {\n ok: boolean;\n latencyMs: number;\n /** Which failure it was, when it failed. */\n reason?: string;\n}\n\n/** The Host-side face the routes read. */\nexport interface DispatchRouteFace {\n /** Current committed configuration. */\n config(): ResolvedConfig;\n /** Read the runtime log, newest first. */\n log(): LogEntry[];\n /** The judge key resolved from credentials, or '' when unset. */\n apiKey(): string;\n}\n\n/**\n * Build this plugin's Fetch routes.\n * @param face - the Host-side reads the routes serve.\n * @returns the routes to register on Connection.\n */\nexport function createFetchRoutes(face: DispatchRouteFace): ConnectionFetchRoute[] {\n const log: ConnectionFetchRoute = {\n path: LOG_ROUTE_PATH,\n methods: ['GET'],\n requestBody: 'buffered',\n fetch: async () => {\n try {\n return sendJson(200, { ok: true, entries: face.log() });\n } catch (err) {\n return sendJson(500, { ok: false, error: describe(err) });\n }\n },\n };\n\n const preview: ConnectionFetchRoute = {\n path: PREVIEW_ROUTE_PATH,\n methods: ['GET'],\n requestBody: 'buffered',\n fetch: async () => {\n try {\n const config = face.config();\n // Built by the same function the real call uses, so what the page shows\n // cannot drift from what goes out.\n const text = buildPreview(selectableRows(config.rows), config.judgeModel.trim());\n return sendJson(200, { ok: true, preview: text });\n } catch (err) {\n return sendJson(500, { ok: false, error: describe(err) });\n }\n },\n };\n\n const probe: ConnectionFetchRoute = {\n path: PROBE_ROUTE_PATH,\n methods: ['POST'],\n requestBody: 'buffered',\n fetch: async () => {\n try {\n const config = face.config();\n // \u53EA\u95EE\u300C\u8FD9\u4E2A\u5730\u5740\u4E0E\u6A21\u578B\u7B54\u4E0D\u7B54\u5F97\u4E0A\u6765\u300D\uFF0C\u6240\u4EE5\u4E0D\u95EE\u7C7B\u578B\u6E05\u5355\uFF1A\u6E05\u5355\u662F\u5224\u65AD\u65F6\u624D\n // \u9700\u8981\u7684\u8F93\u5165\uFF0C\u62FF\u5B83\u5F53\u4F5C\u80FD\u4E0D\u80FD\u6D4B\u7684\u524D\u63D0\uFF0C\u4F1A\u8BA9\u7A7A\u6E05\u5355\u7684\u8BFB\u8005\u4EE5\u4E3A\u670D\u52A1\u574F\u4E86\u3002\n const outcome = await probeJudge({\n url: config.endpointUrl,\n model: config.judgeModel,\n apiKey: face.apiKey(),\n });\n return sendJson(200, { ...outcome } satisfies ProbeResult);\n } catch (err) {\n return sendJson(500, { ok: false, error: describe(err) });\n }\n },\n };\n\n return [log, preview, probe];\n}\n\n/** One-line error text for a route failure. */\nfunction describe(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n", "import type { HostConnectionHandle } from '@deepseek-ai/dsh-client-connection';\nimport {\n isJudgeUsable,\n readPluginConfig,\n SETTINGS_NAMESPACE,\n type PluginConfigRefs,\n type ResolvedConfig,\n} from './server/config.ts';\nimport { JUDGE_API_KEY_REF, type CredentialsServiceLike } from './server/credentials.ts';\nimport { applyDecision, decideRewrite, outcomeReason } from './server/dispatch.ts';\nimport {\n isSubagent,\n readChildLabel,\n readTaskText,\n resolveRootSessionId,\n type SessionLike,\n} from './server/identity.ts';\nimport { judgeTaskType } from './server/judge-client.ts';\nimport { composeJudgeSubject, selectableRows } from './server/judge-prompt.ts';\nimport { LogStore } from './server/log-store.ts';\nimport { createFetchRoutes } from './server/router.ts';\nimport type { LogEntry } from './server/types.ts';\n\n/**\n * Stable Cordis loader name; the same literal as the `id` of this plugin's row\n * in `cordis.patch.yml` and as its settings namespace.\n */\nexport const name = SETTINGS_NAMESPACE;\n\n/**\n * Declared services. `credentials` is the DSH-owned secret surface the judge key\n * is read through; `sessions` and `sessionTitle` turn a child Agent's id into the\n * conversation title the log shows. The Fetch routes ride Connection's\n * authenticated transport, which only a Web carrier composes, so they are taken\n * through `ctx.inject` and the plugin still loads without one.\n */\nexport const inject = ['credentials', 'sessions', 'sessionTitle'];\n\nexport { Config, DEFAULT_ENDPOINT_URL, DEFAULT_JUDGE_MODEL } from './server/config.ts';\nexport { JUDGE_API_KEY_REF } from './server/credentials.ts';\n\n/** Minimal structural shape of the `sessionTitle` service. */\ninterface SessionTitleLike {\n get(session: unknown): { title: string } | undefined;\n}\n\n/** Minimal structural shape of this plugin's own `settings` usage. */\ninterface SettingsLike {\n configure(presentation: { auto?: boolean }, owner?: unknown): () => void;\n}\n\n/**\n * The Host context surface this plugin uses.\n *\n * Declared structurally rather than by extending `Context`: the fields read here\n * (`inject` with a narrower dependency list, `on` for one waterfall event) are a\n * subset with a deliberately looser signature, and widening `inject` to satisfy\n * the interface would hide the services this plugin actually declares through\n * the exported `inject` table.\n */\ninterface HostContext {\n credentials: CredentialsServiceLike;\n sessions: { get(id: string): unknown };\n sessionTitle: SessionTitleLike;\n settings: SettingsLike;\n fiber: unknown;\n on(event: string, listener: (...args: any[]) => unknown): () => void;\n effect(factory: () => void | (() => void), label?: string): unknown;\n inject(names: string[] | string, callback: (ctx: HostContext) => void | (() => void)): void;\n}\n\n/** Which Agent fields this plugin reads inside the request hook. */\ninterface RequestAgent {\n id: string;\n options: { subagentDepth?: number };\n session: SessionLike;\n}\n\n/** One resolved model call the waterfall hands through. */\ninterface CallConfig {\n provider: string;\n model: string;\n reasoningEffort?: unknown;\n [key: string]: unknown;\n}\n\n/** How many judged children to remember before forgetting the oldest. */\nconst JUDGED_LIMIT = 500;\n\n/**\n * The set of children already judged, bounded so a long-running instance cannot\n * grow without limit.\n *\n * Insertion order is eviction order: a `Set` iterates oldest-first, so the one\n * entry that goes is always the earliest judgement. Forgetting a very old child\n * costs at most one extra judgement should it somehow make another request,\n * which is strictly better than an unbounded map keyed by every subagent ever\n * delegated.\n */\nclass JudgedChildren {\n private readonly seen = new Set<string>();\n\n /** Record a child as judged, forgetting the oldest entry past the cap. */\n add(id: string): void {\n this.seen.add(id);\n if (this.seen.size <= JUDGED_LIMIT) return;\n const oldest = this.seen.values().next();\n if (!oldest.done) this.seen.delete(oldest.value);\n }\n\n /** Whether this child has already been judged. */\n has(id: string): boolean {\n return this.seen.has(id);\n }\n}\n\n/**\n * Mount the host half: judge every subagent once, write the route the judgement\n * chose, and keep the log the Plugins page reads.\n *\n * @param ctx - DSH Host context.\n * @param config - this plugin's runtime-resolved Config (every field a live ref).\n */\nexport function apply(ctx: HostContext, config: PluginConfigRefs): void {\n const log = new LogStore();\n /** Children already judged, so a ten-step child is asked about once. */\n const judged = new JudgedChildren();\n let apiKey = '';\n\n const resolvedConfig = (): ResolvedConfig => readPluginConfig(config);\n\n /** Re-read the judge key; a key set outside the page takes effect at once. */\n const refreshKey = async (): Promise<void> => {\n try {\n apiKey = ((await ctx.credentials.resolve(JUDGE_API_KEY_REF))?.value ?? '').trim();\n } catch {\n apiKey = '';\n }\n };\n void refreshKey();\n ctx.on('credentials/reference-updated', () => {\n void refreshKey();\n });\n\n // Settings are read and written through the Plugins page's own card; DSH must\n // not also derive a generic settings form for the same entry.\n ctx.inject(['settings'], (child) => {\n child.effect(\n () => child.settings.configure({ auto: false }, child.fiber),\n 'dsh-subagent-judge-dispatch: settings page policy'\n );\n });\n\n /**\n * The delegation hook: one judgement per subagent, then the route it chose.\n *\n * Never throws and never cancels: every failure path returns the config the\n * Host already resolved, which is what makes a judge outage cost latency and\n * nothing else. The judgement is awaited because the Host awaits this\n * waterfall, so it can add up to the judge budget to that one delegation.\n */\n ctx.on('agent/request', async (payload: { agent?: RequestAgent }, next: () => Promise<CallConfig>) => {\n const resolved = await next();\n const agent = payload?.agent;\n if (agent === undefined) return resolved;\n const cfg = resolvedConfig();\n // Only subagents, and only while the model-rewrite switch is on: with it off\n // nothing is judged and nothing is logged, so the log only ever holds\n // judgements that actually ran.\n if (!isSubagent(agent) || !cfg.enabled) return resolved;\n if (judged.has(agent.id)) return resolved;\n if (!isJudgeUsable(cfg)) return resolved;\n judged.add(agent.id);\n\n const label = readChildLabel(agent);\n const taskText = readTaskText(agent);\n const outcome = await judgeTaskType(\n { url: cfg.endpointUrl, model: cfg.judgeModel, apiKey },\n selectableRows(cfg.rows),\n composeJudgeSubject(label, taskText),\n );\n const decision = decideRewrite(outcome);\n\n if (cfg.logEnabled) {\n log.append(buildEntry(ctx, agent, resolved, decision, outcome, label, taskText));\n }\n return applyDecision(resolved, decision);\n });\n\n // The three reads only the Host can answer. Connection's /api transport is\n // composed by a Web carrier; without one the plugin loads and simply has no\n // HTTP surface.\n ctx.inject(['connection'], (connectionCtx) => {\n const routes = createFetchRoutes({\n config: resolvedConfig,\n log: () => log.list(),\n apiKey: () => apiKey,\n });\n const disposers = routes.map((route) =>\n (connectionCtx as HostContext & { connection: HostConnectionHandle }).connection.fetch.register(route)\n );\n return () => {\n for (const dispose of disposers) void Promise.resolve(dispose()).catch(() => {});\n };\n });\n}\n\n/**\n * Build the log row for one judged delegation.\n *\n * A fallback row reports the route the child actually keeps \u2014 the main agent's\n * resolved provider, model and effort \u2014 rather than a blank, because the point\n * of the row is what the child ran on. The subagent column carries the\n * delegation's own label; the task text is part of the request shown in the\n * row's detail, not repeated in the table.\n */\nfunction buildEntry(\n ctx: HostContext,\n agent: RequestAgent,\n resolved: CallConfig,\n decision: ReturnType<typeof decideRewrite>,\n outcome: Awaited<ReturnType<typeof judgeTaskType>>,\n label: string,\n taskText: string,\n): LogEntry {\n const matched = decision.kind === 'rewrite';\n const reason = outcomeReason(outcome, decision);\n return {\n typeId: matched && outcome.kind === 'matched' ? outcome.row.id : null,\n provider: matched ? decision.rewrite.provider : resolved.provider,\n model: matched ? decision.rewrite.model : resolved.model,\n effort: matched\n ? decision.rewrite.reasoningEffort\n : typeof resolved.reasoningEffort === 'string' ? resolved.reasoningEffort : '',\n ...reason === undefined ? {} : { reason },\n at: Date.now(),\n latencyMs: outcome.latencyMs,\n parentTitle: readParentTitle(ctx, agent.session.header.parentSession),\n childLabel: truncate(label !== '' ? label : taskText),\n prompt: outcome.prompt,\n };\n}\n\n/**\n * Name the conversation that delegated, the way the reader sees it in the\n * session list: its title, falling back to the session id when it has none.\n */\nfunction readParentTitle(ctx: HostContext, parentId: string | undefined): string {\n if (typeof parentId !== 'string' || parentId === '') return '';\n try {\n const rootId = resolveRootSessionId(parentId, (id) => ctx.sessions.get(id) as SessionLike | undefined);\n const session = ctx.sessions.get(rootId);\n if (session === undefined) return rootId;\n const title = ctx.sessionTitle.get(session)?.title;\n return typeof title === 'string' && title.trim() !== '' ? title.trim() : rootId;\n } catch {\n return parentId;\n }\n}\n\n/** Keep one log label to a readable length. */\nfunction truncate(text: string, max = 60): string {\n const collapsed = text.replace(/\\s+/g, ' ').trim();\n return collapsed.length <= max ? collapsed : `${collapsed.slice(0, max - 1)}\u2026`;\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * This plugin's Config schema and the live read face the wiring consumes.\n *\n * Every field is `volatile()`, which is what lets the configuration card on the\n * Plugins page commit an edit into these refs without remounting the plugin:\n * the wiring reads the current value on every delegation rather than a snapshot\n * taken when the Host loaded.\n *\n * The type-list rows carry no sequence number. The page assigns `1..n` from the\n * array order, the judge answers with that number, and the log shows it \u2014 so a\n * number can never be duplicated, and the reader never has to maintain one.\n */\n\nimport type { Volatile } from '@deepseek-ai/cordis';\nimport z from '@deepseek-ai/schemastery';\nimport type Schema from '@deepseek-ai/schemastery';\nimport { isSelectableRow } from './judge-prompt.ts';\nimport type { TypeRow } from './types.ts';\n\n/**\n * The settings namespace this plugin owns. A namespace IS the Profile entry id,\n * so the user-editable layer is this plugin's own row in the active profile's\n * patch \u2014 the same literal as the `id` in `cordis.patch.yml`, and a different\n * literal from the npm package name the card is keyed by.\n */\nexport const SETTINGS_NAMESPACE = 'dsh-subagent-judge-dispatch';\n\n/** Endpoint the page prefills: one System One example. */\nexport const DEFAULT_ENDPOINT_URL = 'http://127.0.0.1:3425/v1/systemone';\n\n/** Judge model the page prefills. */\nexport const DEFAULT_JUDGE_MODEL = 'future-jev/jev-latest';\n\n/** One type-list row as the configuration document stores it. */\nconst TypeRowSchema = z.object({\n /** Criterion text the judge reads for this type. */\n description: z.string().default(''),\n /** Provider route of the model this type runs on. */\n provider: z.string().default(''),\n /** Model id interpreted by that provider. */\n model: z.string().default(''),\n /** Reasoning effort id this type runs at. */\n effort: z.string().default(''),\n});\n\n/** This plugin's live configuration. */\nexport const Config: Schema = z.object({\n /** Whether a judgement may rewrite a subagent's model and effort. */\n enabled: z.boolean().default(true).volatile(),\n /** Whether judgements are written to the plugin's own log. */\n logEnabled: z.boolean().default(true).volatile(),\n /** Full judge endpoint URL. */\n endpointUrl: z.string().default(DEFAULT_ENDPOINT_URL).volatile(),\n /** Judge model id. */\n judgeModel: z.string().default(DEFAULT_JUDGE_MODEL).volatile(),\n /** The task-type list, in page order. */\n rows: z.array(TypeRowSchema).default([]).volatile(),\n});\n\n/** Fields of this plugin's Config as the runtime resolved them. */\nexport interface PluginConfigRefs {\n readonly enabled: Volatile<boolean>;\n readonly logEnabled: Volatile<boolean>;\n readonly endpointUrl: Volatile<string>;\n readonly judgeModel: Volatile<string>;\n readonly rows: Volatile<StoredRow[]>;\n}\n\n/** One stored row, before the page assigns its sequence number. */\nexport interface StoredRow {\n description: string;\n provider: string;\n model: string;\n effort: string;\n}\n\n/** A detached plain read of this plugin's configuration. */\nexport interface ResolvedConfig {\n enabled: boolean;\n logEnabled: boolean;\n endpointUrl: string;\n judgeModel: string;\n rows: TypeRow[];\n}\n\n/**\n * Read the current committed value of every Config field, numbering the rows.\n * @param refs - the runtime-resolved Config object.\n * @returns a detached plain config value.\n */\nexport function readPluginConfig(refs: PluginConfigRefs): ResolvedConfig {\n const stored = refs.rows.get();\n const rows: TypeRow[] = (Array.isArray(stored) ? stored : []).map((row, index) => ({\n // The number is positional, so deleting a row renumbers the rest. A log\n // entry written before that edit may therefore name a different type; the\n // reader accepted that when choosing continuous numbering.\n id: index + 1,\n description: typeof row?.description === 'string' ? row.description : '',\n provider: typeof row?.provider === 'string' ? row.provider : '',\n model: typeof row?.model === 'string' ? row.model : '',\n effort: typeof row?.effort === 'string' ? row.effort : '',\n }));\n return {\n enabled: refs.enabled.get() !== false,\n logEnabled: refs.logEnabled.get() !== false,\n endpointUrl: typeof refs.endpointUrl.get() === 'string' ? refs.endpointUrl.get() : DEFAULT_ENDPOINT_URL,\n judgeModel: typeof refs.judgeModel.get() === 'string' ? refs.judgeModel.get() : DEFAULT_JUDGE_MODEL,\n rows,\n };\n}\n\n/**\n * Whether a judgement would do anything at all: the model-rewrite switch is on,\n * a judge endpoint and model are named, and at least one row is selectable.\n *\n * @param config - the resolved configuration.\n * @returns whether a delegation would be judged.\n */\nexport function isJudgeUsable(config: ResolvedConfig): boolean {\n if (!config.enabled) return false;\n if (config.endpointUrl.trim() === '' || config.judgeModel.trim() === '') return false;\n return config.rows.some(isSelectableRow);\n}\n", "/**\n * The judgement request the plugin sends, and the answer it reads back.\n *\n * Two pure functions live here, so the whole \"what did the judge decide\"\n * question is answerable without a Host, a socket, or a timer:\n *\n * - {@link buildJudgeRequest} turns the type list plus the task text into the\n * exact body that goes out.\n * - {@link readJudgeAnswer} turns whatever came back into a decision, treating\n * every malformed, empty or off-list answer as \"no decision\" rather than\n * inventing one.\n *\n * The protocol is the System One one: POST a body of `{ model, state,\n * questions }`, where each question is a `choice` carrying an `instructions`\n * line and a `criteria` map of option id to the text that option means. The\n * answer names the chosen option id.\n */\n\nimport type { TypeRow } from './types.ts';\n\n/** The one question key the plugin asks. */\nexport const QUESTION_KEY = 'task_type';\n\n/**\n * The question's instructions line.\n *\n * It says what to weigh, not what the types are: the types themselves arrive as\n * the criteria map, so the reader's own descriptions are the whole criterion\n * list and nothing here can drift from them.\n */\nexport const QUESTION_INSTRUCTIONS =\n 'Which single task type below does the delegated job best match? '\n + 'Weigh what the job actually requires, not how it is phrased. '\n + 'Answer with the number of exactly one listed type.';\n\n/** The option id the connectivity check offers: the only one it offers. */\nexport const PROBE_OPTION_ID = '1';\n\n/** The task text the connectivity check sends. */\nexport const PROBE_TASK_TEXT = '\u8FDE\u63A5\u6D4B\u8BD5\uFF1A\u8FD9\u662F\u4E00\u6B21\u5224\u65AD\u670D\u52A1\u7684\u8FDE\u901A\u6027\u68C0\u67E5\u3002';\n\n/**\n * The connectivity check's instructions.\n *\n * It asks for the one option that exists, so an endpoint that understands the\n * protocol at all can answer it: answering it wrong is a failure of the\n * protocol, not of the reader's task list.\n */\nexport const PROBE_INSTRUCTIONS =\n 'Answer with the number of the one option listed below.';\n\n/** The single criterion the connectivity check offers. */\nexport const PROBE_CRITERION = '\u786E\u8BA4\u8FD9\u6B21\u8C03\u7528\u53EF\u4EE5\u5B8C\u6210\u3002';\n\n/** One option of the judge's question. */\nexport interface JudgeChoice {\n type: 'choice';\n instructions: string;\n criteria: Record<string, string>;\n}\n\n/** The exact body sent to the judge endpoint. */\nexport interface JudgeRequest {\n model: string;\n state: { message: string };\n questions: { [QUESTION_KEY]: JudgeChoice };\n}\n\n/**\n * Whether one row is complete enough to be offered to the judge.\n *\n * A row needs its provider, model and effort: a half-filled row could otherwise\n * be chosen and then have nothing to write, which would turn the reader's\n * unfinished edit into a delegation that silently lost its route. This is the\n * ONE place that rule lives \u2014 the page, the config gate and the rewrite all ask\n * this function rather than restating the three comparisons.\n *\n * The parameter is narrowed to the three fields the rule reads, so the page's\n * draft row (which carries no sequence number) and the server's numbered row\n * both satisfy it without either shape being converted into the other.\n *\n * @param row - one row, numbered or drafted.\n * @returns whether the judge may choose it.\n */\nexport function isSelectableRow(row: Pick<TypeRow, 'provider' | 'model' | 'effort'>): boolean {\n return row.provider.trim() !== '' && row.model.trim() !== '' && row.effort.trim() !== '';\n}\n\n/**\n * Which rows may be offered to the judge.\n * @param rows - every row on the page, in page order.\n * @returns the rows the judge may choose, in page order.\n */\nexport function selectableRows(rows: readonly TypeRow[]): TypeRow[] {\n return rows.filter(isSelectableRow);\n}\n\n/**\n * How much of a delegated task's opening the judge reads.\n *\n * The judge answers one multiple-choice question; the opening of a task says\n * what kind of job it is, and the rest is usually detail the choice does not\n * turn on. Bounding it keeps the request small and keeps one enormous\n * delegation from crowding out the type list it is being weighed against.\n */\nexport const SUBJECT_HEAD_CHARS = 1400;\n\n/**\n * How much of a task's closing the judge reads.\n *\n * Supplementary requirements are written at the end \u2014 \"also run the review\" \u2014\n * so the tail is kept rather than the middle.\n */\nexport const SUBJECT_TAIL_CHARS = 400;\n\n/** Opening words of the label line the judge reads above the task. */\nexport const LABEL_PREFIX = 'Delegation label: ';\n\n/**\n * Build the subject the judge is asked to classify.\n *\n * Two parts, in the order the reader would put them: the delegation's own label\n * \u2014 a summary the delegating agent wrote, which carries its intent \u2014 and then\n * the task text itself. The label alone is a display phrase of a few words, too\n * little to classify by; the task text alone loses that intent when it opens\n * with boilerplate.\n *\n * @param label - the delegation label, or '' when the child has none.\n * @param taskText - the delegated task text, or '' when it cannot be read.\n * @returns the subject to send, bounded to {@link SUBJECT_HEAD_CHARS} plus\n * {@link SUBJECT_TAIL_CHARS} characters of task text.\n */\nexport function composeJudgeSubject(label: string, taskText: string): string {\n const task = clipTaskText(taskText.trim());\n const heading = label.trim();\n if (heading === '') return task;\n return task === '' ? LABEL_PREFIX + heading : `${LABEL_PREFIX}${heading}\\n\\n${task}`;\n}\n\n/**\n * Bound a task text to its opening and closing.\n *\n * The dropped middle is named rather than silently removed, so a reader looking\n * at the logged request can tell truncation from a short task. The marker is\n * English like the rest of the protocol, so it reads as a note from the plugin\n * rather than as part of the task, whatever language the task is written in.\n *\n * @param text - the task text.\n * @returns the text, or its head and tail around a count of what was dropped.\n */\nfunction clipTaskText(text: string): string {\n if (text.length <= SUBJECT_HEAD_CHARS + SUBJECT_TAIL_CHARS) return text;\n const dropped = text.length - SUBJECT_HEAD_CHARS - SUBJECT_TAIL_CHARS;\n const head = text.slice(0, SUBJECT_HEAD_CHARS);\n const tail = text.slice(text.length - SUBJECT_TAIL_CHARS);\n return `${head}\\n\u2026 (${dropped} characters omitted) \u2026\\n${tail}`;\n}\n\n/**\n * Build the exact body sent to the judge endpoint.\n *\n * @param rows - the selectable rows, in page order.\n * @param model - the judge model id.\n * @param subject - the text the judge classifies, from {@link composeJudgeSubject}.\n * @returns the request body.\n */\nexport function buildJudgeRequest(\n rows: readonly TypeRow[],\n model: string,\n subject: string,\n): JudgeRequest {\n const criteria: Record<string, string> = {};\n for (const row of rows) criteria[String(row.id)] = row.description;\n return {\n model,\n state: { message: subject },\n questions: {\n [QUESTION_KEY]: {\n type: 'choice',\n instructions: QUESTION_INSTRUCTIONS,\n criteria,\n },\n },\n };\n}\n\n/**\n * Build the body the connectivity check sends.\n *\n * It carries its own single option rather than the reader's task types: the\n * question the button answers is \"does this endpoint and model work\", and that\n * question has an answer before any type list exists. Requiring a finished list\n * first would make the check unable to tell a reader with an empty list from a\n * reader whose endpoint is down.\n *\n * @param model - the judge model id.\n * @returns the request body.\n */\nexport function buildProbeRequest(model: string): JudgeRequest {\n return {\n model,\n state: { message: PROBE_TASK_TEXT },\n questions: {\n [QUESTION_KEY]: {\n type: 'choice',\n instructions: PROBE_INSTRUCTIONS,\n criteria: { [PROBE_OPTION_ID]: PROBE_CRITERION },\n },\n },\n };\n}\n\n/**\n * Serialize one request body the way the page shows it.\n *\n * Both the preview and a logged judgement display this text, while the call\n * itself sends the same object compacted onto the wire. Deriving both from one\n * body is what keeps the shown text from describing a different request than\n * the one that went out.\n *\n * @param body - the request body.\n * @returns the pretty-printed JSON.\n */\nexport function formatJudgeRequest(body: JudgeRequest): string {\n return JSON.stringify(body, null, 2);\n}\n\n/**\n * The text the configuration page's preview shows.\n *\n * It is the request body minus the subject, because no delegation exists until\n * one happens; the preview marks both spots the subject will fill rather than\n * inventing a task. The two markers are told apart, because the real subject is\n * a label line followed by the task text and the preview should read that way.\n *\n * @param rows - the selectable rows, in page order.\n * @param model - the judge model id.\n * @returns the pretty-printed preview text.\n */\nexport function buildPreview(rows: readonly TypeRow[], model: string): string {\n return formatJudgeRequest(\n buildJudgeRequest(rows, model, composeJudgeSubject('\uFF08\u6D3E\u5DE5\u65F6\u624D\u586B\uFF1A\u6D3E\u5DE5\u6807\u7B7E\uFF09', '\uFF08\u6D3E\u5DE5\u65F6\u624D\u586B\uFF1A\u4EFB\u52A1\u6B63\u6587\uFF09')),\n );\n}\n\n/** What reading the judge's answer produced. */\nexport type JudgeDecision =\n | { kind: 'row'; row: TypeRow }\n | { kind: 'empty' }\n | { kind: 'unmatched' };\n\n/**\n * Find the chosen option id inside whatever the endpoint answered.\n *\n * The protocol's own shape is `answers.<key>.choice`; the two fallbacks are the\n * ones the reference clients read, so an endpoint that answers the shorter way\n * still works. Anything else \u2014 a missing body, a non-string, an empty string \u2014\n * yields `undefined` and is treated as no answer.\n *\n * @param payload - the decoded response body.\n * @returns the chosen option id, or undefined when the body names none.\n */\nexport function readChoice(payload: unknown): string | undefined {\n if (typeof payload !== 'object' || payload === null) return undefined;\n const body = payload as Record<string, unknown>;\n const answers = body.answers;\n const fromAnswers =\n typeof answers === 'object' && answers !== null\n ? (answers as Record<string, unknown>)[QUESTION_KEY]\n : undefined;\n const candidate = fromAnswers ?? body[QUESTION_KEY];\n if (typeof candidate === 'object' && candidate !== null) {\n const choice = (candidate as Record<string, unknown>).choice;\n if (typeof choice === 'string' && choice.trim() !== '') return choice.trim();\n if (typeof choice === 'number' && Number.isFinite(choice)) return String(choice);\n return undefined;\n }\n if (typeof candidate === 'string' && candidate.trim() !== '') return candidate.trim();\n if (typeof candidate === 'number' && Number.isFinite(candidate)) return String(candidate);\n return undefined;\n}\n\n/**\n * Turn the judge's answer into a decision.\n *\n * The choice is the type row's number. Matching is by that number's numeric\n * value, so an endpoint that answers `\"3\"` and one that answers `3` agree; a\n * number no listed row carries is `unmatched`, and a body naming no choice at\n * all is `empty`. Both travel to the log as their own reason, because \"it\n * answered nothing\" and \"it answered something I do not have\" send the reader\n * to different fixes.\n *\n * @param payload - the decoded response body.\n * @param rows - the selectable rows, in page order.\n * @returns the decision.\n */\nexport function readJudgeAnswer(payload: unknown, rows: readonly TypeRow[]): JudgeDecision {\n const choice = readChoice(payload);\n if (choice === undefined) return { kind: 'empty' };\n const wanted = Number(choice);\n if (!Number.isFinite(wanted)) return { kind: 'unmatched' };\n const row = rows.find((candidate) => candidate.id === wanted);\n return row === undefined ? { kind: 'unmatched' } : { kind: 'row', row };\n}\n", "/**\n * Where the judgement key lives, and the Host service it is read through.\n *\n * The reference name is a constant of the plugin rather than a setting: the\n * page asks for the key itself and stores it write-only through the DSH\n * credentials service, so a reader never has to know what a \"credential\n * reference\" is, and no surface can point the plugin at somebody else's key.\n * This module imports nothing, so both halves can share the literal.\n *\n * The key value crosses in one direction only: it is written, and read back by\n * the Host half alone. The browser face reads a status-only view.\n */\n\n/** Credential reference the judgement key is stored under. */\nexport const JUDGE_API_KEY_REF = 'SUBAGENT_JUDGE_API_KEY';\n\n/** Minimal structural shape of the Host's `ctx.credentials` service. */\nexport interface CredentialsServiceLike {\n /** Resolve a reference to its stored or inherited value. */\n resolve(ref: string): Promise<{ value: string; source?: string } | undefined>;\n /** Status-only view of a reference; never returns the plaintext value. */\n describe(ref: string): Promise<{ configured: boolean; source?: string; writable: boolean }>;\n /** Store a non-empty value under a reference. */\n set(ref: string, value: string): Promise<void>;\n /** Remove a reference from the document. */\n unset(ref: string): Promise<void>;\n}\n\n/** Minimal structural shape of the browser's `ctx.remote.credentials` face. */\nexport interface CredentialsRemoteLike {\n describe(refs: string[]): Promise<\n { ok: true; value: Record<string, { configured: boolean; source?: string; writable: boolean }> }\n | { ok: false; error: { message: string } }\n >;\n set(ref: string, value: string): Promise<{ ok: true } | { ok: false; error: { message: string } }>;\n unset(ref: string): Promise<{ ok: true } | { ok: false; error: { message: string } }>;\n}\n", "/**\n * The decision half: given what the judge said, decide exactly what to write\n * onto a subagent's next model call \u2014 or write nothing at all.\n *\n * This module holds the one rule that matters most, so it is a pure function\n * that a test can drive directly: **any** judgement that did not end in a\n * usable row writes nothing. Not \"writes the inherited values\", not \"writes a\n * partial route\" \u2014 nothing. Writing anything is a replacement, so a fallback\n * that wrote the parent's own values back would still be an overwrite, and a\n * partial write would drop the inherited reasoning effort\n * (`resolveChildAgentOptions` deletes it when a route changes without one).\n */\n\nimport { isSelectableRow } from './judge-prompt.ts';\nimport type { FallbackReason, JudgeOutcome, TypeRow } from './types.ts';\n\n/** The three fields a rewrite carries, all present or none at all. */\nexport interface RouteRewrite {\n provider: string;\n model: string;\n reasoningEffort: string;\n}\n\n/** What to write for one judged subagent. */\nexport type DispatchDecision =\n | { kind: 'rewrite'; rewrite: RouteRewrite }\n | { kind: 'none' };\n\n/**\n * Turn a judgement into the rewrite it authorises.\n *\n * The three fields travel as one object because they must be written as one:\n * a route change without an explicit effort makes the Host drop the inherited\n * effort, so a caller can never be handed two of the three.\n *\n * @param outcome - what the judge call produced.\n * @returns the rewrite, or `none` when the child must inherit unchanged.\n */\nexport function decideRewrite(outcome: JudgeOutcome): DispatchDecision {\n if (outcome.kind !== 'matched') return { kind: 'none' };\n const row: TypeRow = outcome.row;\n // A row the judge could select but that is missing a piece is not a rewrite\n // this plugin may make; leaving the child alone is always safe.\n if (!isSelectableRow(row)) return { kind: 'none' };\n return {\n kind: 'rewrite',\n rewrite: {\n provider: row.provider.trim(),\n model: row.model.trim(),\n reasoningEffort: row.effort.trim(),\n },\n };\n}\n\n/**\n * Why a delegation ended up inheriting, as the log reports it.\n *\n * The judge answering nothing, the judge answering a number this list does not\n * have, an unreachable endpoint and an expired budget are four different fixes\n * for the reader \u2014 \"it answered nothing\" points at the endpoint, \"no listed\n * type\" points at the descriptions. A selected row that turned out to be missing\n * a field is a fifth: the judgement worked, the row it named was not usable.\n *\n * A judgement that produced a rewrite has no reason at all.\n *\n * @param outcome - what the judge call produced.\n * @param decision - what was written for it.\n * @returns the reason to record, or undefined when the rewrite landed.\n */\nexport function outcomeReason(\n outcome: JudgeOutcome,\n decision: DispatchDecision,\n): FallbackReason | undefined {\n if (decision.kind === 'rewrite') return undefined;\n if (outcome.kind !== 'matched') return outcome.reason;\n return 'incomplete';\n}\n\n/**\n * Apply a rewrite onto one model-call configuration.\n *\n * The incoming configuration is returned untouched on `none`, which is what\n * makes \"the child inherits exactly what the main agent runs\" true rather than\n * approximated: no field is restated, so nothing about the call changes.\n *\n * @param config - the call configuration the Host resolved.\n * @param decision - what to write.\n * @returns the configuration to use.\n */\nexport function applyDecision<T extends { provider: string; model: string; reasoningEffort?: unknown }>(\n config: T,\n decision: DispatchDecision,\n): T {\n if (decision.kind === 'none') return config;\n return {\n ...config,\n provider: decision.rewrite.provider,\n model: decision.rewrite.model,\n reasoningEffort: decision.rewrite.reasoningEffort,\n };\n}\n", "/**\n * Reading one subagent's identity, from the child Agent alone.\n *\n * Inside `agent/request` the plugin holds a child Agent and nothing else: no\n * delegation record, no parent handle. Everything the log needs \u2014 what the child\n * was asked to do, and which conversation asked for it \u2014 has to be read back out\n * of the child's own durable session facts.\n */\n\n/**\n * The read surface of a Session this module needs; kept structural for tests.\n *\n * `ownEvents` (or `snapshotEvents(inheritedEventCount)`) is the child-owned tail\n * of the log. It matters: a forked child's log opens with the parent's finished\n * turns, so reading from the start would hand the judge the ANCESTOR's\n * descriptor label and the parent conversation's first message.\n */\nexport interface SessionLike {\n readonly header: { readonly parentSession?: string; readonly delegationDepth?: number };\n readonly inheritedEventCount?: number;\n ownEvents?(): readonly SessionEventLike[];\n snapshotEvents(from?: number): readonly SessionEventLike[];\n}\n\n/** One session event as this module reads it. */\nexport interface SessionEventLike {\n readonly type: string;\n readonly data?: unknown;\n}\n\n/** The read surface of an Agent this module needs. */\nexport interface AgentLike {\n readonly id: string;\n readonly options: { readonly subagentDepth?: number };\n readonly session: SessionLike;\n}\n\n/**\n * Read only the events this child itself produced.\n *\n * A forked child inherits the parent's completed prefix, and the inherited part\n * carries an ancestor's descriptor and the parent's own messages. Everything\n * this module reads must come from the child-owned tail, or the judge is told\n * about a different agent's job. Three shapes are accepted because the session\n * type is structural here: `ownEvents()`, `snapshotEvents(from)` with the\n * inherited count, and a plain full read as the last resort.\n *\n * @param session - the child's session.\n * @returns the child-owned events, in log order.\n */\nexport function ownEventsOf(session: SessionLike): readonly SessionEventLike[] {\n if (typeof session.ownEvents === 'function') return session.ownEvents();\n const inherited = session.inheritedEventCount;\n if (typeof inherited === 'number' && Number.isSafeInteger(inherited) && inherited > 0) {\n return session.snapshotEvents(inherited);\n }\n return session.snapshotEvents();\n}\n\n/**\n * Whether one agent is a subagent, rather than a top-level conversation.\n *\n * Two sources are consulted because neither alone is complete: the durable\n * header is authoritative and monotone (a resumed child keeps its depth), while\n * `options.subagentDepth` is stamped onto the live options and may deepen the\n * count but never lower it. Absence in both means a top-level agent.\n *\n * @param agent - the agent making the request.\n * @returns whether this is a subagent.\n */\nexport function isSubagent(agent: AgentLike): boolean {\n const fromHeader = agent.session.header.delegationDepth;\n const fromOptions = agent.options.subagentDepth;\n const depth = Math.max(\n typeof fromHeader === 'number' && Number.isSafeInteger(fromHeader) ? fromHeader : 0,\n typeof fromOptions === 'number' && Number.isSafeInteger(fromOptions) ? fromOptions : 0,\n );\n return depth >= 1;\n}\n\n/** Opening words of the return guidance the Host appends to a delegation. */\nexport const RETURN_GUIDANCE_PREFIX = 'Your parent agent id is ';\n\n/**\n * Read the child's delegation description.\n *\n * The subagent tool passes the model's `description` as the activation label,\n * and the child's durable descriptor keeps it \u2014 which is why it is readable here\n * without the parent's tool call. Only the child's OWN events are read, and the\n * LAST descriptor wins: a resumed or re-activated child appends a fresh one, and\n * the newest is the delegation actually running.\n *\n * The label is a display summary the delegating agent wrote, so it is returned\n * on its own and never merged with the task text: the judge gets the label as a\n * heading over the task, and the log's subagent column gets the label alone.\n *\n * @param agent - the subagent.\n * @returns the description, or '' when its descriptor carries none.\n */\nexport function readChildLabel(agent: AgentLike): string {\n let found = '';\n for (const event of ownEventsOf(agent.session)) {\n if (event.type !== 'subagent/descriptor') continue;\n const data = event.data;\n if (typeof data !== 'object' || data === null) continue;\n const label = (data as { label?: unknown }).label;\n if (typeof label === 'string' && label.trim() !== '') found = label.trim();\n }\n return found;\n}\n\n/**\n * Read the task the parent delegated.\n *\n * The delegation is the message whose `source.kind` is `user`; every other\n * producer writes user-role messages too \u2014 the working directory notice,\n * workspace instructions, the runtime snapshot, the skill catalog, the time\n * snapshot \u2014 so the source is what separates the task from the context wrapped\n * around it.\n *\n * Two shapes carry that message, and both are read because the judgement runs\n * before the second one exists. A delegation starts as pending inbox input, so\n * it first appears inside an `agent/inbox/spliced` event; the Host claims it at\n * the step boundary and only then appends the `user/message` carrying it. The\n * judge is asked during the step's request, which is before that append \u2014 so\n * reading only `user/message` would find nothing on the very request that\n * matters. From the next step on the `user/message` is there instead.\n *\n * The Host appends its return guidance as one trailing block of that message.\n * It tells the child how to report back, not what the job is, so it is dropped:\n * leaving it in would spend most of the subject on boilerplate.\n *\n * Only the child's OWN events are read, because a forked child's log opens with\n * the parent's finished turns.\n *\n * @param agent - the subagent.\n * @returns the delegated task text, or '' when no delegation message exists.\n */\nexport function readTaskText(agent: AgentLike): string {\n for (const event of ownEventsOf(agent.session)) {\n const candidates = delegationMessagesOf(event);\n for (const data of candidates) {\n const blocks = textBlocksOf(data);\n // The guidance is appended last; a task that happens to open with the same\n // words is still honored because only the trailing block is examined.\n if (blocks.length > 1 && blocks[blocks.length - 1].startsWith(RETURN_GUIDANCE_PREFIX)) {\n blocks.pop();\n }\n const text = blocks.join('\\n').trim();\n if (text !== '') return text;\n }\n }\n return '';\n}\n\n/**\n * The messages one event contributes, in log order.\n *\n * A delegation message is written twice across a child's life \u2014 once as pending\n * inbox input, once as the committed `user/message` \u2014 so both event shapes are\n * unwrapped here and the caller keeps the first usable one.\n *\n * @param event - one child-owned session event.\n * @returns the delegation-shaped messages the event carries, possibly none.\n */\nfunction delegationMessagesOf(event: SessionEventLike): unknown[] {\n if (event.type === 'user/message') {\n return sourceOf(event.data)?.kind === 'user' ? [event.data] : [];\n }\n if (event.type === 'agent/inbox/spliced') {\n const inserted = (event.data as { inserted?: unknown } | undefined)?.inserted;\n if (!Array.isArray(inserted)) return [];\n return inserted.filter((message) => sourceOf(message)?.kind === 'user');\n }\n return [];\n}\n\n/** Read the durable producer facts off one message payload. */\nfunction sourceOf(data: unknown): { kind: string } | undefined {\n if (typeof data !== 'object' || data === null) return undefined;\n const source = (data as { source?: unknown }).source;\n if (typeof source !== 'object' || source === null) return undefined;\n const kind = (source as { kind?: unknown }).kind;\n return typeof kind === 'string' ? { kind } : undefined;\n}\n\n/** Pull the text blocks out of a message payload, in order. */\nfunction textBlocksOf(data: unknown): string[] {\n if (typeof data !== 'object' || data === null) return [];\n const content = (data as { content?: unknown }).content;\n if (!Array.isArray(content)) return [];\n const texts: string[] = [];\n for (const block of content) {\n if (typeof block !== 'object' || block === null) continue;\n const record = block as { type?: unknown; text?: unknown };\n if (record.type === 'text' && typeof record.text === 'string') texts.push(record.text);\n }\n return texts;\n}\n\n/**\n * Walk up a child's lineage to the top-level conversation that started it.\n *\n * A nested subagent's direct parent is another subagent, and the log's job is to\n * name the conversation the reader recognises \u2014 so the walk continues until a\n * session names no parent. The walk is bounded, because a resumed or forged\n * lineage must not be able to spin this loop.\n *\n * @param startId - the child's direct parent session id.\n * @param lookup - resolves a session id to its live session.\n * @returns the top-level session id, or the starting id when the walk stops.\n */\nexport function resolveRootSessionId(\n startId: string,\n lookup: (id: string) => SessionLike | undefined,\n): string {\n const seen = new Set<string>([startId]);\n let current = startId;\n for (let hop = 0; hop < 32; hop += 1) {\n const session = lookup(current);\n const parent = session?.header.parentSession;\n if (typeof parent !== 'string' || parent === '' || seen.has(parent)) return current;\n seen.add(parent);\n current = parent;\n }\n return current;\n}\n", "/**\n * The judge call itself: one POST, one budget, no retry.\n *\n * The call can fail in exactly four ways \u2014 unreachable, timed out, an answer\n * that names no option, an answer whose row is unusable \u2014 and every one of them\n * ends the same way for the caller: no rewrite happens and the child inherits\n * what the main agent is running. That is why nothing here throws: a delegation\n * must never fail because the judge was unavailable.\n */\n\nimport {\n PROBE_OPTION_ID,\n buildJudgeRequest,\n buildProbeRequest,\n formatJudgeRequest,\n readJudgeAnswer,\n} from './judge-prompt.ts';\nimport type { FallbackReason, JudgeOutcome, ProbeOutcome, TypeRow } from './types.ts';\n\n/** How long a judgement may take before it is abandoned. */\nexport const JUDGE_TIMEOUT_MS = 5000;\n\n/** What the judge client needs from the Host to make one call. */\nexport interface JudgeEndpoint {\n /** Full endpoint URL, including the protocol path. */\n url: string;\n /** Judge model id. */\n model: string;\n /** Authorization value, or '' when the endpoint needs none. */\n apiKey: string;\n}\n\n/** Structural `fetch` so tests can supply their own. */\nexport type FetchLike = (input: string, init: RequestInit) => Promise<Response>;\n\n/**\n * Ask the judge whether this endpoint and model can answer at all.\n *\n * This is a different question from a judgement, so it sends its own request\n * with its own single option: the reader uses the button to find out whether\n * the endpoint and model work, which is answerable before any task type has\n * been written. It never throws for the same reason a judgement never does \u2014\n * the page reports the reason, it does not handle an exception.\n *\n * @param endpoint - where and how to call the judge.\n * @param fetchImpl - the fetch to call; defaults to the global one.\n * @returns whether the endpoint answered, and how long it took.\n */\nexport async function probeJudge(\n endpoint: JudgeEndpoint,\n fetchImpl: FetchLike = fetch,\n): Promise<ProbeOutcome> {\n const startedAt = Date.now();\n const elapsed = (): number => Date.now() - startedAt;\n if (endpoint.url.trim() === '' || endpoint.model.trim() === '') {\n return { ok: false, latencyMs: elapsed(), reason: 'unconfigured' };\n }\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), JUDGE_TIMEOUT_MS);\n let timedOut = false;\n const onTimeout = (): void => {\n timedOut = true;\n };\n controller.signal.addEventListener('abort', onTimeout);\n\n try {\n const response = await fetchImpl(endpoint.url.trim(), {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n ...endpoint.apiKey.trim() === '' ? {} : { authorization: `Bearer ${endpoint.apiKey.trim()}` },\n },\n body: JSON.stringify(buildProbeRequest(endpoint.model.trim())),\n signal: controller.signal,\n });\n if (!response.ok) return { ok: false, latencyMs: elapsed(), reason: 'unreachable' };\n const payload: unknown = await response.json();\n // The check asked for the one option it offered: any answer that names a\n // different option means the endpoint did not read the request it was sent.\n return readJudgeAnswer(payload, [{\n id: Number(PROBE_OPTION_ID),\n description: '',\n provider: '',\n model: '',\n effort: '',\n }]).kind === 'row'\n ? { ok: true, latencyMs: elapsed() }\n : { ok: false, latencyMs: elapsed(), reason: 'unmatched' };\n } catch {\n return { ok: false, latencyMs: elapsed(), reason: timedOut ? 'timeout' : 'unreachable' };\n } finally {\n clearTimeout(timer);\n controller.signal.removeEventListener('abort', onTimeout);\n }\n}\n\n/**\n * Ask the judge which type one delegated job is.\n *\n * Never throws and never lets an abort cancel the delegation: every failure is\n * reported as a `fallback` outcome carrying its own reason, and the caller\n * writes nothing.\n *\n * @param endpoint - where and how to call the judge.\n * @param rows - the selectable rows, in page order.\n * @param subject - the text to classify, from `composeJudgeSubject`.\n * @param fetchImpl - the fetch to call; defaults to the global one.\n * @returns the decision, or the fallback reason that replaced it.\n */\nexport async function judgeTaskType(\n endpoint: JudgeEndpoint,\n rows: readonly TypeRow[],\n subject: string,\n fetchImpl: FetchLike = fetch,\n): Promise<JudgeOutcome> {\n const startedAt = Date.now();\n const elapsed = (): number => Date.now() - startedAt;\n if (endpoint.url.trim() === '' || endpoint.model.trim() === '' || rows.length === 0) {\n return { kind: 'fallback', reason: 'unreachable', latencyMs: elapsed(), prompt: '' };\n }\n\n const body = buildJudgeRequest(rows, endpoint.model.trim(), subject);\n // The body the log keeps is the one sent, not a re-built one: the reader\n // opens it to see exactly what the judge was asked.\n const prompt = formatJudgeRequest(body);\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), JUDGE_TIMEOUT_MS);\n let timedOut = false;\n const onTimeout = (): void => {\n timedOut = true;\n };\n controller.signal.addEventListener('abort', onTimeout);\n\n try {\n const response = await fetchImpl(endpoint.url.trim(), {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n // Sent only when there is a key: an unauthenticated local endpoint\n // would otherwise receive an empty Bearer and reject the call itself.\n ...endpoint.apiKey.trim() === '' ? {} : { authorization: `Bearer ${endpoint.apiKey.trim()}` },\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n if (!response.ok) {\n return { kind: 'fallback', reason: 'unreachable', latencyMs: elapsed(), prompt };\n }\n const payload: unknown = await response.json();\n const decision = readJudgeAnswer(payload, rows);\n if (decision.kind === 'empty') {\n return { kind: 'fallback', reason: 'unmatched', latencyMs: elapsed(), prompt };\n }\n if (decision.kind === 'unmatched') {\n return { kind: 'fallback', reason: 'unmatched', latencyMs: elapsed(), prompt };\n }\n return { kind: 'matched', row: decision.row, latencyMs: elapsed(), prompt };\n } catch {\n const reason: FallbackReason = timedOut ? 'timeout' : 'unreachable';\n return { kind: 'fallback', reason, latencyMs: elapsed(), prompt };\n } finally {\n clearTimeout(timer);\n controller.signal.removeEventListener('abort', onTimeout);\n }\n}\n", "/**\n * The plugin's own log, kept in the Host's memory.\n *\n * The Host's native subagent list has no model column and a plugin cannot add\n * one, so this log is the only place a delegation's assigned route is visible.\n * It is deliberately disposable: it lives for one Host process and is gone on\n * restart, which is what the reader asked for.\n */\n\nimport type { LogEntry } from './types.ts';\n\n/** How many entries the page keeps. */\nexport const LOG_LIMIT = 20;\n\n/** A bounded, newest-first ring of log entries. */\nexport class LogStore {\n private entries: LogEntry[] = [];\n\n /**\n * Append one entry, dropping the oldest beyond {@link LOG_LIMIT}.\n * @param entry - the entry to append.\n */\n append(entry: LogEntry): void {\n this.entries = [entry, ...this.entries].slice(0, LOG_LIMIT);\n }\n\n /**\n * Read the log, newest first.\n * @returns a detached copy, so a reader cannot mutate the ring.\n */\n list(): LogEntry[] {\n return this.entries.map((entry) => ({ ...entry }));\n }\n}\n", "/**\n * This plugin's exact Fetch routes, carried by Connection's authenticated\n * `/api` transport below the shared channel.\n *\n * Three surfaces, and no fourth: configuration is read and written through\n * DSH's own shared configuration form (`ctx.configForms`) and the `credentials`\n * Remote API, so nothing here touches either. What is left is what only the\n * Host knows \u2014 the runtime log, the exact text a judgement would send, and\n * whether the judge endpoint answers at all.\n */\n\nimport type { ConnectionFetchRoute } from '@deepseek-ai/dsh-client-connection';\nimport { buildPreview, selectableRows } from './judge-prompt.ts';\nimport { probeJudge } from './judge-client.ts';\nimport type { ResolvedConfig } from './config.ts';\nimport type { LogEntry } from './types.ts';\n\n/** Runtime log route path. */\nexport const LOG_ROUTE_PATH = '/api/dsh-subagent-judge-dispatch/log';\n\n/** Exact judge-prompt preview route path. */\nexport const PREVIEW_ROUTE_PATH = '/api/dsh-subagent-judge-dispatch/preview';\n\n/** Judge endpoint probe route path. */\nexport const PROBE_ROUTE_PATH = '/api/dsh-subagent-judge-dispatch/probe';\n\nfunction sendJson(status: number, body: unknown): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'Content-Type': 'application/json; charset=utf-8' },\n });\n}\n\n/** What one probe reports back to the page. */\nexport interface ProbeResult {\n ok: boolean;\n latencyMs: number;\n /** Which failure it was, when it failed. */\n reason?: string;\n}\n\n/** The Host-side face the routes read. */\nexport interface DispatchRouteFace {\n /** Current committed configuration. */\n config(): ResolvedConfig;\n /** Read the runtime log, newest first. */\n log(): LogEntry[];\n /** The judge key resolved from credentials, or '' when unset. */\n apiKey(): string;\n}\n\n/**\n * Build this plugin's Fetch routes.\n * @param face - the Host-side reads the routes serve.\n * @returns the routes to register on Connection.\n */\nexport function createFetchRoutes(face: DispatchRouteFace): ConnectionFetchRoute[] {\n const log: ConnectionFetchRoute = {\n path: LOG_ROUTE_PATH,\n methods: ['GET'],\n requestBody: 'buffered',\n fetch: async () => {\n try {\n return sendJson(200, { ok: true, entries: face.log() });\n } catch (err) {\n return sendJson(500, { ok: false, error: describe(err) });\n }\n },\n };\n\n const preview: ConnectionFetchRoute = {\n path: PREVIEW_ROUTE_PATH,\n methods: ['GET'],\n requestBody: 'buffered',\n fetch: async () => {\n try {\n const config = face.config();\n // Built by the same function the real call uses, so what the page shows\n // cannot drift from what goes out.\n const text = buildPreview(selectableRows(config.rows), config.judgeModel.trim());\n return sendJson(200, { ok: true, preview: text });\n } catch (err) {\n return sendJson(500, { ok: false, error: describe(err) });\n }\n },\n };\n\n const probe: ConnectionFetchRoute = {\n path: PROBE_ROUTE_PATH,\n methods: ['POST'],\n requestBody: 'buffered',\n fetch: async () => {\n try {\n const config = face.config();\n // \u53EA\u95EE\u300C\u8FD9\u4E2A\u5730\u5740\u4E0E\u6A21\u578B\u7B54\u4E0D\u7B54\u5F97\u4E0A\u6765\u300D\uFF0C\u6240\u4EE5\u4E0D\u95EE\u7C7B\u578B\u6E05\u5355\uFF1A\u6E05\u5355\u662F\u5224\u65AD\u65F6\u624D\n // \u9700\u8981\u7684\u8F93\u5165\uFF0C\u62FF\u5B83\u5F53\u4F5C\u80FD\u4E0D\u80FD\u6D4B\u7684\u524D\u63D0\uFF0C\u4F1A\u8BA9\u7A7A\u6E05\u5355\u7684\u8BFB\u8005\u4EE5\u4E3A\u670D\u52A1\u574F\u4E86\u3002\n const outcome = await probeJudge({\n url: config.endpointUrl,\n model: config.judgeModel,\n apiKey: face.apiKey(),\n });\n return sendJson(200, { ...outcome } satisfies ProbeResult);\n } catch (err) {\n return sendJson(500, { ok: false, error: describe(err) });\n }\n },\n };\n\n return [log, preview, probe];\n}\n\n/** One-line error text for a route failure. */\nfunction describe(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n", "import type { HostConnectionHandle } from '@deepseek-ai/dsh-client-connection';\nimport {\n isJudgeUsable,\n readPluginConfig,\n SETTINGS_NAMESPACE,\n type PluginConfigRefs,\n type ResolvedConfig,\n} from './server/config.ts';\nimport { JUDGE_API_KEY_REF, type CredentialsServiceLike } from './server/credentials.ts';\nimport { applyDecision, decideRewrite, outcomeReason } from './server/dispatch.ts';\nimport {\n isSubagent,\n readChildLabel,\n readTaskText,\n resolveRootSessionId,\n type SessionLike,\n} from './server/identity.ts';\nimport { judgeTaskType } from './server/judge-client.ts';\nimport { composeJudgeSubject, selectableRows } from './server/judge-prompt.ts';\nimport { LogStore } from './server/log-store.ts';\nimport { createFetchRoutes } from './server/router.ts';\nimport type { LogEntry } from './server/types.ts';\n\n/**\n * Stable Cordis loader name; the same literal as the `id` of this plugin's row\n * in `cordis.patch.yml` and as its settings namespace.\n */\nexport const name = SETTINGS_NAMESPACE;\n\n/**\n * Declared services. `credentials` is the DSH-owned secret surface the judge key\n * is read through; `sessions` and `sessionTitle` turn a child Agent's id into the\n * conversation title the log shows. The Fetch routes ride Connection's\n * authenticated transport, which only a Web carrier composes, so they are taken\n * through `ctx.inject` and the plugin still loads without one.\n */\nexport const inject = ['credentials', 'sessions', 'sessionTitle'];\n\nexport { Config, DEFAULT_ENDPOINT_URL, DEFAULT_JUDGE_MODEL } from './server/config.ts';\nexport { JUDGE_API_KEY_REF } from './server/credentials.ts';\n\n/** Minimal structural shape of the `sessionTitle` service. */\ninterface SessionTitleLike {\n get(session: unknown): { title: string } | undefined;\n}\n\n/** Minimal structural shape of this plugin's own `settings` usage. */\ninterface SettingsLike {\n configure(presentation: { auto?: boolean }, owner?: unknown): () => void;\n}\n\n/**\n * The Host context surface this plugin uses.\n *\n * Declared structurally rather than by extending `Context`: the fields read here\n * (`inject` with a narrower dependency list, `on` for one waterfall event) are a\n * subset with a deliberately looser signature, and widening `inject` to satisfy\n * the interface would hide the services this plugin actually declares through\n * the exported `inject` table.\n */\ninterface HostContext {\n credentials: CredentialsServiceLike;\n sessions: { get(id: string): unknown };\n sessionTitle: SessionTitleLike;\n settings: SettingsLike;\n fiber: unknown;\n on(event: string, listener: (...args: any[]) => unknown): () => void;\n effect(factory: () => void | (() => void), label?: string): unknown;\n inject(names: string[] | string, callback: (ctx: HostContext) => void | (() => void)): void;\n}\n\n/** Which Agent fields this plugin reads inside the request hook. */\ninterface RequestAgent {\n id: string;\n options: { subagentDepth?: number };\n session: SessionLike;\n}\n\n/** One resolved model call the waterfall hands through. */\ninterface CallConfig {\n provider: string;\n model: string;\n reasoningEffort?: unknown;\n [key: string]: unknown;\n}\n\n/** How many judged children to remember before forgetting the oldest. */\nconst JUDGED_LIMIT = 500;\n\n/**\n * The set of children already judged, bounded so a long-running instance cannot\n * grow without limit.\n *\n * Insertion order is eviction order: a `Set` iterates oldest-first, so the one\n * entry that goes is always the earliest judgement. Forgetting a very old child\n * costs at most one extra judgement should it somehow make another request,\n * which is strictly better than an unbounded map keyed by every subagent ever\n * delegated.\n */\nclass JudgedChildren {\n private readonly seen = new Set<string>();\n\n /** Record a child as judged, forgetting the oldest entry past the cap. */\n add(id: string): void {\n this.seen.add(id);\n if (this.seen.size <= JUDGED_LIMIT) return;\n const oldest = this.seen.values().next();\n if (!oldest.done) this.seen.delete(oldest.value);\n }\n\n /** Whether this child has already been judged. */\n has(id: string): boolean {\n return this.seen.has(id);\n }\n}\n\n/**\n * Mount the host half: judge every subagent once, write the route the judgement\n * chose, and keep the log the Plugins page reads.\n *\n * @param ctx - DSH Host context.\n * @param config - this plugin's runtime-resolved Config (every field a live ref).\n */\nexport function apply(ctx: HostContext, config: PluginConfigRefs): void {\n const log = new LogStore();\n /** Children already judged, so a ten-step child is asked about once. */\n const judged = new JudgedChildren();\n let apiKey = '';\n\n const resolvedConfig = (): ResolvedConfig => readPluginConfig(config);\n\n /** Re-read the judge key; a key set outside the page takes effect at once. */\n const refreshKey = async (): Promise<void> => {\n try {\n apiKey = ((await ctx.credentials.resolve(JUDGE_API_KEY_REF))?.value ?? '').trim();\n } catch {\n apiKey = '';\n }\n };\n void refreshKey();\n ctx.on('credentials/reference-updated', () => {\n void refreshKey();\n });\n\n // Settings are read and written through the Plugins page's own card; DSH must\n // not also derive a generic settings form for the same entry.\n ctx.inject(['settings'], (child) => {\n child.effect(\n () => child.settings.configure({ auto: false }, child.fiber),\n 'dsh-subagent-judge-dispatch: settings page policy'\n );\n });\n\n /**\n * The delegation hook: one judgement per subagent, then the route it chose.\n *\n * Never throws and never cancels: every failure path returns the config the\n * Host already resolved, which is what makes a judge outage cost latency and\n * nothing else. The judgement is awaited because the Host awaits this\n * waterfall, so it can add up to the judge budget to that one delegation.\n */\n ctx.on('agent/request', async (payload: { agent?: RequestAgent }, next: () => Promise<CallConfig>) => {\n const resolved = await next();\n const agent = payload?.agent;\n if (agent === undefined) return resolved;\n const cfg = resolvedConfig();\n // Only subagents, and only while the model-rewrite switch is on: with it off\n // nothing is judged and nothing is logged, so the log only ever holds\n // judgements that actually ran.\n if (!isSubagent(agent) || !cfg.enabled) return resolved;\n if (judged.has(agent.id)) return resolved;\n if (!isJudgeUsable(cfg)) return resolved;\n judged.add(agent.id);\n\n const label = readChildLabel(agent);\n const taskText = readTaskText(agent);\n const outcome = await judgeTaskType(\n { url: cfg.endpointUrl, model: cfg.judgeModel, apiKey },\n selectableRows(cfg.rows),\n composeJudgeSubject(label, taskText),\n );\n const decision = decideRewrite(outcome);\n\n if (cfg.logEnabled) {\n log.append(buildEntry(ctx, agent, resolved, decision, outcome, label, taskText));\n }\n return applyDecision(resolved, decision);\n });\n\n // The three reads only the Host can answer. Connection's /api transport is\n // composed by a Web carrier; without one the plugin loads and simply has no\n // HTTP surface.\n ctx.inject(['connection'], (connectionCtx) => {\n const routes = createFetchRoutes({\n config: resolvedConfig,\n log: () => log.list(),\n apiKey: () => apiKey,\n });\n const disposers = routes.map((route) =>\n (connectionCtx as HostContext & { connection: HostConnectionHandle }).connection.fetch.register(route)\n );\n return () => {\n for (const dispose of disposers) void Promise.resolve(dispose()).catch(() => {});\n };\n });\n}\n\n/**\n * Build the log row for one judged delegation.\n *\n * A fallback row reports the route the child actually keeps \u2014 the main agent's\n * resolved provider, model and effort \u2014 rather than a blank, because the point\n * of the row is what the child ran on. The subagent column carries the\n * delegation's own label; the task text is part of the request shown in the\n * row's detail, not repeated in the table.\n */\nfunction buildEntry(\n ctx: HostContext,\n agent: RequestAgent,\n resolved: CallConfig,\n decision: ReturnType<typeof decideRewrite>,\n outcome: Awaited<ReturnType<typeof judgeTaskType>>,\n label: string,\n taskText: string,\n): LogEntry {\n const matched = decision.kind === 'rewrite';\n const reason = outcomeReason(outcome, decision);\n return {\n typeId: matched && outcome.kind === 'matched' ? outcome.row.id : null,\n provider: matched ? decision.rewrite.provider : resolved.provider,\n model: matched ? decision.rewrite.model : resolved.model,\n effort: matched\n ? decision.rewrite.reasoningEffort\n : typeof resolved.reasoningEffort === 'string' ? resolved.reasoningEffort : '',\n ...reason === undefined ? {} : { reason },\n at: Date.now(),\n latencyMs: outcome.latencyMs,\n parentTitle: readParentTitle(ctx, agent.session.header.parentSession),\n childLabel: truncate(label !== '' ? label : taskText),\n prompt: outcome.prompt,\n };\n}\n\n/**\n * Name the conversation that delegated, the way the reader sees it in the\n * session list: its title, falling back to the session id when it has none.\n */\nfunction readParentTitle(ctx: HostContext, parentId: string | undefined): string {\n if (typeof parentId !== 'string' || parentId === '') return '';\n try {\n const rootId = resolveRootSessionId(parentId, (id) => ctx.sessions.get(id) as SessionLike | undefined);\n const session = ctx.sessions.get(rootId);\n if (session === undefined) return rootId;\n const title = ctx.sessionTitle.get(session)?.title;\n return typeof title === 'string' && title.trim() !== '' ? title.trim() : rootId;\n } catch {\n return parentId;\n }\n}\n\n/** Keep one log label to a readable length. */\nfunction truncate(text: string, max = 60): string {\n const collapsed = text.replace(/\\s+/g, ' ').trim();\n return collapsed.length <= max ? collapsed : `${collapsed.slice(0, max - 1)}\u2026`;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|