mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-11-10 04:27:42 +08:00
refactor(job-controller): drop the subagent ownership fence from job.kill
The registry's owner fence is the only access rule for a human kill: a job is killable by the session whose list carries it, and a child session's own jobs are no exception. The fence had pulled two implementation helpers out of the Session Controller's package entry (plus a safe-host-export exception for them) so the job controller could import them; both go, and the job controller no longer depends on the agent registry or the Session Controller.
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-08-26-human-job-kill.md
|
||||
2026-08-26-human-job-kill.md: b80fdb1e4d4c9b68846b3d63fc1b0de819267aa2
|
||||
2026-08-26-human-job-kill.zh.md: 53c16c75ab07957b2e03e171185e9fd64fd8406e
|
||||
2026-08-26-human-job-kill.md: efffdbd85bf2fa665565ff584a6938cfee827516
|
||||
2026-08-26-human-job-kill.zh.md: 206acca008518dd689351e40a99ff1571f3d1a8f
|
||||
|
||||
@@ -16,7 +16,7 @@ A delivery claim means "the terminal state has a committed delivery path to the
|
||||
|
||||
- **A human kill claims no delivery.** `JobRegistry.kill(id, { reason? })` records the reason only; the delivery ledger lives in `dsh-tool-jobs`, which claims a job for the model's own `job_kill` and for its waits, so a `job.kill` from the task list leaves the settlement to the existing completion reporter (wakeup/inject, wake budget, truncation all unchanged). It never clears a claim the model already holds, and a terminal record stays exactly as settlement left it.
|
||||
- **A recorded kill reason merges into a `killed` settlement's detail** — producer facts first (`signal: SIGTERM; cancelled by the user`) — so the completion notice and the web row both say who stopped the work without a new snapshot field or notice template. A job that outruns its kill (settles `completed`/`failed`) keeps the producer detail alone. This is the reconciliation Codex spells out in its `<turn_aborted>` guidance: when the user stops something, the model is told rather than left to infer.
|
||||
- **`job.kill` is a Job Controller Remote** (`@deepseek-ai/dsh-api-job-controller`, beside `job.follow`; it began life as `session.killJob` on the Session Controller and moved with the observation stream when that package split out) with the same live-only Agent lookup (`ctx.agents.get`) as `session.cancel`: a running job's owner is alive by the registry's ownership contract, and killing from a list must not revive a Session any more than listing does. Unknown and foreign jobs collapse to one `job/not-found` rejection scoped to the lookup — a producer-cancel throw propagates per the registry contract instead of masquerading as a lookup failure; the controller requires `ctx.jobs` to load, so a composition without a registry has no kill Remote at all, and the Session Controller's subagent ownership fence applies exactly as it does to `cancel`. No approval interaction: this single-user local BFF treats it as the same class of action as the turn-cancel button.
|
||||
- **`job.kill` is a Job Controller Remote** (`@deepseek-ai/dsh-api-job-controller`, beside `job.follow`; it began life as `session.killJob` on the Session Controller and moved with the observation stream when that package split out) with the same live-only Agent lookup (`ctx.agents.get`) as `session.cancel`: a running job's owner is alive by the registry's ownership contract, and killing from a list must not revive a Session any more than listing does. Unknown and foreign jobs collapse to one `job/not-found` rejection scoped to the lookup — a producer-cancel throw propagates per the registry contract instead of masquerading as a lookup failure; the controller requires `ctx.jobs` to load, so a composition without a registry has no kill Remote at all. The registry's owner fence is the only access rule: the Session Controller's subagent ownership fence is not applied, so a child session's own jobs are killable from its list (review rejected exporting that fence's helpers from the Session Controller package, and the rule adds nothing for a job the child owns), and the controller depends on neither the agent registry nor the Session Controller. No approval interaction: this single-user local BFF treats it as the same class of action as the turn-cancel button.
|
||||
- **The task list's stop control is two-press** (arm, then confirm within 3s, matching the Kimi `s`+`y` and OpenCode double-Esc shape), rendered only on running job rows — rows with a `jobId`. Standalone activity rows (workflow runs) have no kill handle and no button. The pending press disables the control and stays pending on an admitted kill — the unary response and the jobs frames have no cross-carrier ordering, so only the authoritative frame (the row leaving the killable set) releases it; a rejected kill shows a brief hint. Because the kill addresses `ctx.jobs` by id, every job kind gains the control at once — bash, pwsh, pty, and one-shot background subagents.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
@@ -16,7 +16,7 @@ Update:本文引入的 `reported` 选项已随 [jobs seam 收敛](../architect
|
||||
|
||||
- **人类的 kill 不认领送达。** `JobRegistry.kill(id, { reason? })` 只记录原因;送达台账在 `dsh-tool-jobs` 里,它只为模型自己的 `job_kill` 与等待认领任务,因此来自任务列表的 `job.kill` 让结算照常走完成播报员(唤醒/注入、唤醒预算、截断全部不变)。它绝不清除模型已持有的认领,终态记录也保持结算时的原样。
|
||||
- **记录下来的 kill reason 合入 `killed` 结算的 detail**——生产者事实在前(`signal: SIGTERM; cancelled by the user`)——完成通知与 Web 行因此都能说清是谁停的工作,无需新快照字段或新通知模板。跑赢 kill 的任务(结算为 `completed`/`failed`)只保留生产者 detail。这正是 Codex 在 `<turn_aborted>` 指引里写明的对账原则:用户停掉了什么,要告诉模型,而不是让它去猜。
|
||||
- **`job.kill` 是 Job Controller 的 Remote**(`@deepseek-ai/dsh-api-job-controller`,与 `job.follow` 并列;它最初是 Session Controller 上的 `session.killJob`,该包拆出时随观测流一起迁走),与 `session.cancel` 一样只查活体 Agent(`ctx.agents.get`):按注册表的所有权契约,在跑任务的 owner 必然活着;从列表里 kill 与列表本身一样绝不复活 Session。未知与他人任务合并为一个只作用于查找的 `job/not-found` 拒绝——生产者 cancel 抛错按注册表契约原样传播,绝不伪装成查找失败;控制器加载就要求 `ctx.jobs`,没有注册表的组合根本没有 kill Remote,Session Controller 的 subagent 所有权栅栏与 `cancel` 完全一致地适用。不加 approval 交互:这个单用户本地 BFF 把它视为与停轮按钮同级的操作。
|
||||
- **`job.kill` 是 Job Controller 的 Remote**(`@deepseek-ai/dsh-api-job-controller`,与 `job.follow` 并列;它最初是 Session Controller 上的 `session.killJob`,该包拆出时随观测流一起迁走),与 `session.cancel` 一样只查活体 Agent(`ctx.agents.get`):按注册表的所有权契约,在跑任务的 owner 必然活着;从列表里 kill 与列表本身一样绝不复活 Session。未知与他人任务合并为一个只作用于查找的 `job/not-found` 拒绝——生产者 cancel 抛错按注册表契约原样传播,绝不伪装成查找失败;控制器加载就要求 `ctx.jobs`,没有注册表的组合根本没有 kill Remote。注册表的所有者围栏是唯一的访问规则:不施加 Session Controller 的 subagent 所有权栅栏,子会话自己的 job 可以从它的列表里被杀停(评审否决了从 Session Controller 包导出该栅栏的实现函数,而这条规则对子会话自有的 job 也没有额外意义),控制器既不依赖 agent 注册表也不依赖 Session Controller。不加 approval 交互:这个单用户本地 BFF 把它视为与停轮按钮同级的操作。
|
||||
- **任务列表的停止控件为两击式**(先武装、3 秒内确认,对齐 Kimi 的 `s`+`y` 与 OpenCode 的双 Esc),只渲染在运行中的 job 行——即带 `jobId` 的行。独立 activity 行(workflow 运行)没有 kill 句柄也就没有按钮。请求进行中按钮禁用,且受理成功后保持 pending——unary 响应与 jobs 帧没有跨载体顺序保证,只有权威帧(行离开可杀集合)才释放控件;被拒绝的 kill 短暂提示。由于 kill 按 id 寻址 `ctx.jobs`,所有任务 kind 一次性获得该控件——bash、pwsh、pty 与一次性后台 subagent。
|
||||
|
||||
## 备选方案
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/config-catalog.md
|
||||
config-catalog.md: e373bc9536fd7c7f1e9019db589f21d7440efef3
|
||||
config-catalog.zh.md: dd7078c8cabc0c1edbd8e1952c2e72cb998d7974
|
||||
config-catalog.md: a9f82169c1a0d1ced020a84c56e994be1c963d01
|
||||
config-catalog.zh.md: 572131ec9efc2149fb1e81850135d8e9687ccd13
|
||||
|
||||
@@ -205,7 +205,7 @@ Source: [`packages/api/gateway/src/index.ts:144`](../packages/api/gateway/src/in
|
||||
|
||||
## `@deepseek-ai/dsh-api-job-controller`
|
||||
|
||||
Requires: `agents` · `jobs` · `typert`
|
||||
Requires: `jobs` · `typert`
|
||||
|
||||
```ts config-catalog
|
||||
/** Job Controller deployment policy. */
|
||||
@@ -217,7 +217,7 @@ export interface Config {
|
||||
}
|
||||
```
|
||||
|
||||
Source: [`packages/api/job-controller/src/index.ts:37`](../packages/api/job-controller/src/index.ts)
|
||||
Source: [`packages/api/job-controller/src/index.ts:35`](../packages/api/job-controller/src/index.ts)
|
||||
|
||||
<a id="deepseek-aidsh-api-session-controller"></a>
|
||||
|
||||
|
||||
@@ -219,7 +219,7 @@ export interface Config {
|
||||
}
|
||||
```
|
||||
|
||||
来源:[`packages/api/job-controller/src/index.ts:37`](../packages/api/job-controller/src/index.ts)
|
||||
来源:[`packages/api/job-controller/src/index.ts:35`](../packages/api/job-controller/src/index.ts)
|
||||
|
||||
<a id="deepseek-aidsh-api-session-controller"></a>
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/subsystems/jobs.md
|
||||
jobs.md: 85400f905e1d313f5f903863f572b0bedbcadfd7
|
||||
jobs.zh.md: 1397e1b93bbdd2f57ab219122b1c1ad8c0938538
|
||||
jobs.md: 0d235c6101ff20c9ef530bc08efc98000ce05fd0
|
||||
jobs.zh.md: fbb650bbc10e4c35451b193cdf7317b28980752c
|
||||
|
||||
@@ -369,12 +369,12 @@ Host service backing the generated `ctx.remote.job` namespace.
|
||||
|
||||
/**
|
||||
* Kill one background job on a human's behalf. The request's session is
|
||||
* the fenced read's caller, so the job must be one that session can see;
|
||||
* the subagent ownership fence applies exactly as it does to
|
||||
* `session.cancel`. The kill records `cancelled by the user` as its reason;
|
||||
* it is not one the model requested, so the owning agent still receives
|
||||
* the completion notice, and a shell tool waiting on that job reads the
|
||||
* reason in its own result.
|
||||
* the fenced read's caller, so the job must be one that session can see:
|
||||
* the registry's owner fence is the only access rule, and a child session's
|
||||
* own jobs are killable from its list like any other. The kill records
|
||||
* `cancelled by the user` as its reason; it is not one the model requested,
|
||||
* so the owning agent still receives the completion notice, and a shell
|
||||
* tool waiting on that job reads the reason in its own result.
|
||||
* @param request - Session whose job list carries the job, and the job id.
|
||||
* @returns the registry's admission of the kill request.
|
||||
*/
|
||||
|
||||
@@ -369,12 +369,12 @@ Host service backing the generated `ctx.remote.job` namespace.
|
||||
|
||||
/**
|
||||
* Kill one background job on a human's behalf. The request's session is
|
||||
* the fenced read's caller, so the job must be one that session can see;
|
||||
* the subagent ownership fence applies exactly as it does to
|
||||
* `session.cancel`. The kill records `cancelled by the user` as its reason;
|
||||
* it is not one the model requested, so the owning agent still receives
|
||||
* the completion notice, and a shell tool waiting on that job reads the
|
||||
* reason in its own result.
|
||||
* the fenced read's caller, so the job must be one that session can see:
|
||||
* the registry's owner fence is the only access rule, and a child session's
|
||||
* own jobs are killable from its list like any other. The kill records
|
||||
* `cancelled by the user` as its reason; it is not one the model requested,
|
||||
* so the owning agent still receives the completion notice, and a shell
|
||||
* tool waiting on that job reads the reason in its own result.
|
||||
* @param request - Session whose job list carries the job, and the job id.
|
||||
* @returns the registry's admission of the kill request.
|
||||
*/
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/api/job-controller/README.md
|
||||
README.md: 891416d050aaad36c2f9926bdf7bbbac7bb90d4b
|
||||
README.zh.md: 67dca2b58730ce680b2acf257184dace26989077
|
||||
README.md: ea343a8a201b50901af0033b39662fab4dd50555
|
||||
README.zh.md: ce8f166cec4e180e491cd48377f20db2e7177e75
|
||||
|
||||
@@ -24,7 +24,7 @@ English | [中文](README.zh.md)
|
||||
|
||||
The Host controller requires the live Agent registry and the job registry (`dsh-jobs-local` in the shipped compositions) and fails to load without them. `job.list({ sessionId })` yields the session's visible set — its own jobs plus every unowned job — once on open and again after each coalesced burst of lifecycle commits (registration, progress, stopping, settlement, removal); output appends never refresh the roster, since a settled projection already carries the final byte count. `job.follow({ sessionId?, jobId, from? })` passes `sessionId` to registry `get` and `readAt` — an unowned job needs no session — and yields one `opened` anchor carrying the job projection, coalesced `output` frames, then one terminal `status` once the job has settled and its ring is drained, after which the stream closes normally; a removal announced mid-stream (the owner's teardown) closes it with the removed job's terminal projection. Both reads are non-consuming: the model's `job_output` cursor and notice state never observe them.
|
||||
|
||||
`job.kill({ sessionId, jobId })` cancels one job the session can see with the reason `cancelled by the user`, which the registry merges into the killed job's detail. It is not a kill the model requested — `dsh-tool-jobs` withholds a notice only for the model's own `job_kill` and for settlements a live wait collected — so the owning agent's completion notice stays due, reason included, and a shell tool still waiting on that job reads the reason as `[stopped: cancelled by the user]` in its own result. It answers `{ outcome: 'requested' }` or `{ outcome: 'already-finished' }`, rejects an id the session cannot see as `job/not-found`, and applies the subagent ownership fence exactly as `session.cancel` does.
|
||||
`job.kill({ sessionId, jobId })` cancels one job the session can see with the reason `cancelled by the user`, which the registry merges into the killed job's detail. It is not a kill the model requested — `dsh-tool-jobs` withholds a notice only for the model's own `job_kill` and for settlements a live wait collected — so the owning agent's completion notice stays due, reason included, and a shell tool still waiting on that job reads the reason as `[stopped: cancelled by the user]` in its own result. It answers `{ outcome: 'requested' }` or `{ outcome: 'already-finished' }` and rejects an id the session cannot see as `job/not-found`; the registry's owner fence is the only access rule, so a child session's own jobs are killable from its list like any other.
|
||||
|
||||
The Client entry installs `ctx.jobs` (`IJobs`), backed by the package-internal `ClientJobsModel`. `kill(sessionId, jobId)` forwards to `job.kill` and returns the Remote result for the caller's admission verdict. `watchRows(sessionId)` keeps one roster stream open per watched session however many viewers hold it and drops the rows after the last release; a reconnect's first frame is already the whole truth. `observe(sessionId, jobId)` opens one Gateway stream per job however many viewers expand it, keeps a bounded render tail per job with `gapBefore` marking eviction or resume gaps, closes the view on the terminal frame or records a stream failure on it, and drops the view after the last viewer releases. The plugin resolves the Gateway stream factory and the `job` namespace while its own context is current, because stream (re)opens run on caller stacks whose dynamic context has not declared `remote.job`.
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ kind: "package-reference"
|
||||
|
||||
Host 控制器要求活体 Agent 注册表与 job 注册表(已发布组合里是 `dsh-jobs-local`),缺少任一则不加载。`job.list({ sessionId })` 产出该会话可见的集合——自己的 job 加上所有无主 job——打开时一次,之后每一轮聚合过的生命周期提交(注册、进度、停止中、结算、移除)后再一次;输出追加从不刷新名册,因为结算后的投影已经带着最终字节数。`job.follow({ sessionId?, jobId, from? })` 把 `sessionId` 传给注册表的 `get` 与 `readAt`——无主 job 不需要 session——先产出一个携带 job 投影的 `opened` 锚帧,再是聚合的 `output` 帧,job 结算且环排干后产出一个终态 `status`,随后流正常关闭;流中若通告了移除(拥有者 teardown),则以被移除 job 的终态投影收尾。两种读取都是非消耗的:模型侧 `job_output` 游标与完成播报状态永远观察不到它们。
|
||||
|
||||
`job.kill({ sessionId, jobId })` 以 `cancelled by the user` 为原因取消一个该会话看得到的 job,注册表把该原因合并进被杀 job 的 detail。它不是模型请求的杀停——`dsh-tool-jobs` 只对模型自己的 `job_kill` 与存活等待收走的结算不发通知——因此拥有者 agent 的完成通知照常送达,并带上原因;仍在等待该 job 的 shell 工具则在自己的结果里以 `[stopped: cancelled by the user]` 读到原因。它回答 `{ outcome: 'requested' }` 或 `{ outcome: 'already-finished' }`,对该会话看不到的 id 以 `job/not-found` 拒绝,并与 `session.cancel` 一样施加 subagent 所有权围栏。
|
||||
`job.kill({ sessionId, jobId })` 以 `cancelled by the user` 为原因取消一个该会话看得到的 job,注册表把该原因合并进被杀 job 的 detail。它不是模型请求的杀停——`dsh-tool-jobs` 只对模型自己的 `job_kill` 与存活等待收走的结算不发通知——因此拥有者 agent 的完成通知照常送达,并带上原因;仍在等待该 job 的 shell 工具则在自己的结果里以 `[stopped: cancelled by the user]` 读到原因。它回答 `{ outcome: 'requested' }` 或 `{ outcome: 'already-finished' }`,对该会话看不到的 id 以 `job/not-found` 拒绝;注册表的所有者围栏是唯一的访问规则,因此子会话自己的 job 也能像其他 job 一样从它的列表里被杀停。
|
||||
|
||||
Client 入口安装 `ctx.jobs`(`IJobs`),由包内部的 `ClientJobsModel` 支撑。`kill(sessionId, jobId)` 转发到 `job.kill` 并把 Remote 结果交给调用方判定准入。`watchRows(sessionId)` 不论多少查看器持有都只为每个被关注的会话开一条名册流,最后一个释放后丢弃行;重连后的首帧已经是完整事实。`observe(sessionId, jobId)` 不论多少查看器展开同一 job 都只开一条 Gateway 流,按 job 保留有界的渲染尾部并用 `gapBefore` 标记淘汰或续读缺口,在终态帧上关闭视图或把流失败记到视图上,最后一个查看器释放后丢弃视图。插件在自己的上下文仍是当前上下文时解析 Gateway 流工厂与 `job` namespace,因为流的(重)开启跑在未声明 `remote.job` 的调用栈上。
|
||||
|
||||
|
||||
@@ -65,7 +65,6 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@deepseek-ai/dsh-api-session-controller": "workspace:^",
|
||||
"@deepseek-ai/dsh-typert-protocol": "workspace:^",
|
||||
"@deepseek-ai/schemastery": "workspace:^",
|
||||
"zod": "^4.4.3"
|
||||
|
||||
@@ -10,9 +10,7 @@
|
||||
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import z from '@deepseek-ai/schemastery'
|
||||
import type {} from '@deepseek-ai/dsh-agent'
|
||||
import type {} from '@deepseek-ai/dsh-jobs'
|
||||
import { apiSessionSubagentOwnershipError, hasApiSessionSubagentOwner } from '@deepseek-ai/dsh-api-session-controller'
|
||||
import { Remote, RemoteError, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol'
|
||||
import { observeJobOutput } from './observe.ts'
|
||||
import { streamJobRows } from './rows.ts'
|
||||
@@ -43,7 +41,7 @@ export interface Config {
|
||||
|
||||
/** Host service backing the generated `ctx.remote.job` namespace. */
|
||||
export class JobController extends TypertRemoteService {
|
||||
static inject = ['agents', 'jobs', 'typert']
|
||||
static inject = ['jobs', 'typert']
|
||||
|
||||
static Config: z<Config> = z.object({
|
||||
observeFlushMs: z.natural().min(1).default(DEFAULT_OBSERVE_FLUSH_MS),
|
||||
@@ -99,21 +97,17 @@ export class JobController extends TypertRemoteService {
|
||||
|
||||
/**
|
||||
* Kill one background job on a human's behalf. The request's session is
|
||||
* the fenced read's caller, so the job must be one that session can see;
|
||||
* the subagent ownership fence applies exactly as it does to
|
||||
* `session.cancel`. The kill records `cancelled by the user` as its reason;
|
||||
* it is not one the model requested, so the owning agent still receives
|
||||
* the completion notice, and a shell tool waiting on that job reads the
|
||||
* reason in its own result.
|
||||
* the fenced read's caller, so the job must be one that session can see:
|
||||
* the registry's owner fence is the only access rule, and a child session's
|
||||
* own jobs are killable from its list like any other. The kill records
|
||||
* `cancelled by the user` as its reason; it is not one the model requested,
|
||||
* so the owning agent still receives the completion notice, and a shell
|
||||
* tool waiting on that job reads the reason in its own result.
|
||||
* @param request - Session whose job list carries the job, and the job id.
|
||||
* @returns the registry's admission of the kill request.
|
||||
*/
|
||||
@Remote('kill')
|
||||
kill(request: JobKillRequest): JobKillValue {
|
||||
const agent = this.ctx.agents.get(request.sessionId)
|
||||
if (agent !== undefined && hasApiSessionSubagentOwner(this.ctx, agent.session, agent)) {
|
||||
throw apiSessionSubagentOwnershipError(request.sessionId)
|
||||
}
|
||||
const jobs = this.ctx.jobs
|
||||
try {
|
||||
jobs.get(request.jobId, request.sessionId)
|
||||
|
||||
@@ -135,15 +135,15 @@ describe('JobController.kill', () => {
|
||||
expect(ctx.jobs.get(id, agent.id).status).toBe('running')
|
||||
})
|
||||
|
||||
it('rejects a subagent-owned live session with the ownership fence', async () => {
|
||||
it('kills a child session\'s own job from its list: the owner fence is the only access rule', async () => {
|
||||
const { ctx, controller } = await harness()
|
||||
const child = ctx.sessions.create(undefined, { meta: { origin: 'subagent' } })
|
||||
const childAgent = await registerAgent(ctx, child)
|
||||
const task = producer('child work')
|
||||
const id = ctx.jobs.start({ ...task.spec, owner: childAgent.id })
|
||||
|
||||
expect(failureCode(() => controller.kill({ sessionId: child.id, jobId: id })))
|
||||
.toBe('session/agent-busy')
|
||||
expect(ctx.jobs.get(id, childAgent.id).status).toBe('running')
|
||||
expect(controller.kill({ sessionId: child.id, jobId: id })).toEqual({ outcome: 'requested' })
|
||||
expect(task.cancels).toEqual(['cancelled by the user'])
|
||||
expect(ctx.jobs.get(id, childAgent.id).status).toBe('stopping')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
"references": [
|
||||
{ "path": "../../../vendor/cordis" },
|
||||
{ "path": "../../../vendor/schemastery" },
|
||||
{ "path": "../session-controller/tsconfig.host.json" },
|
||||
{ "path": "../../core/agent" },
|
||||
{ "path": "../../core/session" },
|
||||
{ "path": "../../jobs/jobs" },
|
||||
|
||||
@@ -63,7 +63,7 @@ import type {
|
||||
} from './types.ts'
|
||||
|
||||
export type * from './types.ts'
|
||||
export { ApiSessionNotFound, apiSessionSubagentOwnershipError, hasApiSessionSubagentOwner } from './agent.ts'
|
||||
export { ApiSessionNotFound } from './agent.ts'
|
||||
export { SessionFileReferences } from './file-references.ts'
|
||||
export { SessionSkillCatalog } from './skill-catalog.ts'
|
||||
|
||||
|
||||
@@ -1220,7 +1220,7 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
|
||||
},
|
||||
{
|
||||
signature: '@Remote(\'kill\') kill(request: JobKillRequest): JobKillValue',
|
||||
description: 'Kill one background job on a human\'s behalf. The request\'s session is the fenced read\'s caller, so the job must be one that session can see; the subagent ownership fence applies exactly as it does to `session.cancel`. The kill records `cancelled by the user` as its reason; it is not one the model requested, so the owning agent still receives the completion notice, and a shell tool waiting on that job reads the reason in its own result.',
|
||||
description: 'Kill one background job on a human\'s behalf. The request\'s session is the fenced read\'s caller, so the job must be one that session can see: the registry\'s owner fence is the only access rule, and a child session\'s own jobs are killable from its list like any other. The kill records `cancelled by the user` as its reason; it is not one the model requested, so the owning agent still receives the completion notice, and a shell tool waiting on that job reads the reason in its own result.',
|
||||
parameters: [{ name: 'request', description: 'Session whose job list carries the job, and the job id.' }],
|
||||
returns: 'the registry\'s admission of the kill request.',
|
||||
},
|
||||
|
||||
Generated
-3
@@ -990,9 +990,6 @@ importers:
|
||||
|
||||
packages/api/job-controller:
|
||||
dependencies:
|
||||
'@deepseek-ai/dsh-api-session-controller':
|
||||
specifier: workspace:^
|
||||
version: link:../session-controller
|
||||
'@deepseek-ai/dsh-typert-protocol':
|
||||
specifier: workspace:^
|
||||
version: link:../../typert/protocol
|
||||
|
||||
@@ -45,7 +45,6 @@ const DUPLICATE_SAFE_PACKAGES: readonly string[] = [
|
||||
* prominent heading in the pull request description.
|
||||
*/
|
||||
const SAFE_HOST_DEPENDENCY_EXPORTS = {
|
||||
'@deepseek-ai/dsh-api-session-controller': ['apiSessionSubagentOwnershipError', 'hasApiSessionSubagentOwner'],
|
||||
'@deepseek-ai/dsh-credentials': ['credentialKey'],
|
||||
'@deepseek-ai/dsh-deque': ['Deque'],
|
||||
'@deepseek-ai/dsh-llm': ['callConfigEquals'],
|
||||
|
||||
Reference in New Issue
Block a user