agent-device runs as an optional module on workers
メンテナーはふだん 1 日以内に返信
評価
- 難易度
- 5/5
- 見積もり時間
- 1週間以上
- 初心者へのやさしさ
- 8/100
- issue の種類
- リファクタリング
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- typescript
調査の方向性
Start with src/daemon/main.ts, where agent-device is wired by static import on the worker path (lines 329-645), and src/core/config.ts, which holds its config type, validator and gateway warning. The change moves this into a new src/agent-device/definition.ts and worker.ts behind the module host from #595. Done means a worker with the module off never calls loadWorker, the old agentDevice key is rejected as unknown, and the listed tests pass under the new capabilities key.
索引モデルが issue の本文から書いたものです。
説明
Part of #591.
Scope
Moves agent-device's worker side onto the module host from #595. After
this PR, agent-device is the registry's first module:
- It is turned on with
capabilities.agent-device.enabled. The top-level
agentDevicekey goes. - A worker with it turned off never loads its code.
src/daemon/main.tsno longer wires it on a worker.
Its behaviour is otherwise unchanged, with three exceptions:
- With agent-device off, simlock no longer stops an agent-device daemon
left by a crash (ADR 0030 §2). - On
daemon stop, agent-device's two stop steps are now bounded by the
Hook deadline;
today they have no limit. Its slowest stop is about 22 s: a running
--versioncheck or reap (ps5 s, SIGTERM 10 s, SIGKILL 2 s,
F18). Past 30 s the drivers are disposed with agent-device still
stopping, and the warning names it. - A gateway's warning for
commanduses the generic text (below).
The gateway side stays hand-wired in main.ts until #598. In this PR it
loads agent-device with a dynamic import(), only when it is turned on.
That way no daemon loads agent-device's code through a static import.
Test fixtures that build a whole Config lose their agentDevice line.
That edit is mechanical and is not counted in this task's size. Setup
lines that turn agent-device on (edge.test.ts, gateway-front.test.ts,
main.test.ts, worker-capabilities.e2e.test.ts,
lease-requires.e2e.test.ts) move to capabilities["agent-device"]
instead. The slow-lane files are rewritten (F14).
In short
An operator turns agent-device on under capabilities:
{ "capabilities": { "agent-device": { "enabled": true, "command": "agent-device", "leaseTtlCapMs": 60000 } } }
A config that still says { "agentDevice": { "enabled": true } } gets
Unknown config key: "agentDevice", and agent-device stays off. The
integration is unreleased, so nothing migrates the old key.
flowchart LR
R["module-registry: agent-device *"] --> H[module host]
H -->|on| L["loadWorker() → createWorker *"]
H -->|off| O["agent-device: off<br/>no code loaded, no reap *"]
If this goes wrong: agent-device does not start on a worker that turns it
on, or simlock daemon stop leaves its process behind.
Technical spec
Facts
| # | Fact | Source |
|---|---|---|
| F1 | main.ts statically imports 15 names from ../agent-device/index.js. |
src/daemon/main.ts:5-21 |
| F2 | On a worker, main.ts builds agent-device's supervisor, edge parts, lease-end participant, capability source (with an off stub when disabled), relay target, HTTP front, stop steps and start. |
src/daemon/main.ts:329-357, :372, :484-488, :522-527, :548-551, :571-575, :592-596, :630-645 |
| F3 | agentDevice.stop() runs in dispose before the drivers, because the Android driver stops the adb server agent-device uses. |
src/daemon/main.ts:548-551 |
| F4 | stopAuxiliary disposes the edge and cancels waiting allocates through leasing.cancelPending. |
src/daemon/main.ts:571-575 |
| F5 | onSocketClaimed starts the supervisor, not awaited, unless a test passed agentDeviceUpstream. |
src/daemon/main.ts:592-596 |
| F6 | With config.enabled false, the supervisor starts in off; start() reaps a recorded daemon, then stays off. |
src/agent-device/host/agent-device-supervisor.ts:163, :255-286 |
| F7 | Two supervisor tests pin the off path. | src/agent-device/host/agent-device-supervisor.test.ts:346, :1161 |
| F8 | The agent-device config is { enabled, command, leaseTtlCapMs? }, default { enabled: false, command: "agent-device" }, leaseTtlCapMs from 5000 to lease.maxTtlMs; command is worker-only. The core holds its type, the cap check and its call, its gateway key, its gateway warning, its default and its validator. The validators come from src/core/validation.ts, which src/core/index.ts does not export; invalidValue words an error Invalid config value for "<path>": expected <what>. |
src/core/config.ts:253-257, :410, :452-459, :485, :494, :522-530, :763, :916-920; src/core/validation.ts:17-19, :17-118 |
| F9 | Config tests for agent-device: one describe block of 8 tests, plus a gateway test. | src/core/config.test.ts:1961-2036, :1776 |
| F10 | configSchema names agentDevice. |
src/contract/schemas.ts:902-906 |
| F11 | Tests pass agentDeviceUpstream or agentDeviceHealth to startDaemon. |
src/agent-device/edge.test.ts:165; src/agent-device/gateway-front.test.ts:270; src/daemon/main.test.ts:2430; src/daemon/worker-capabilities.e2e.test.ts:66; src/daemon/lease-requires.e2e.test.ts:89 |
| F12 | Five assertions name the old key: three in those suites, two in the supervisor's tests. | src/agent-device/gateway-front.test.ts:703, :709-711; src/agent-device/edge.test.ts:2098-2105; src/agent-device/host/agent-device-supervisor.test.ts:421, :480 |
| F13 | The supervisor's remedies tell the user to set agentDevice.command. |
src/agent-device/host/agent-device-supervisor.ts:228-230 |
| F14 | The slow-lane tests write and delete agentDevice in a config. |
e2e/slow-agent-device.test.ts; e2e/slow-agent-device-gateway.test.ts:160, :173, :264, :280 |
| F15 | docs/CONFIGURATION.md documents the agentDevice.* keys, the gateway slice, and "A daemon a killed Simlock left behind is stopped at the next start". |
docs/CONFIGURATION.md:38-40, :162-169 |
| F16 | The gateway path reads config.agentDevice for enabled and leaseTtlCapMs. |
src/daemon/main.ts:827, :958-960 |
| F17 | dependency-cruiser records import() as dynamic-import, import type as type-only, and import("x").T as type-import. |
node_modules/dependency-cruiser/src/extract/tsc/extract-typescript-deps.mjs:82, :424, :436 (18.4.0) |
| F18 | The supervisor's waits: ps 5 s, --version 10 s, SIGTERM grace 10 s, SIGKILL grace 2 s. |
src/agent-device/host/agent-device-supervisor.ts:76-80 |
| F19 | agent-device's front imports src/http's helpers and types. |
src/agent-device/front/front.ts:1; src/agent-device/front/allocate.ts:4-6; src/agent-device/host/host.ts:3 |
| F20 | Today a gateway warns Ignoring "agentDevice.command": it names the program a worker runs, and this daemon runs in gateway mode. |
src/core/config.ts:528 |
Modules touched
flowchart LR
main["daemon/main.ts *"] --> host["modules host"]
main --> reg["module-registry *"]
reg --> def["agent-device/definition *new*"]
def -. loadWorker .-> w["agent-device/worker *new*"]
main -. "dynamic import (gateway, until #598)" .-> idx["agent-device/index *"]
w --> sup["agent-device supervisor *"]
cfg["core/config.ts, core/index.ts *"]
contract["contract/schemas.ts, protocol.ts *"]
Surface and extension points
src/agent-device/definition.ts exports agentDeviceModule, an
OptionalModuleDefinition:
name: "agent-device".config:- F8's validator without
enabled, which the core owns (#595), built
from validators thatsrc/core/index.tsnow exports
(objectValidator,nonEmptyString,integerInRange,
invalidValue); - F8's defaults without
enabled; gatewayKeys: ["leaseTtlCapMs"];- a
checkthatleaseTtlCapMsis at mostlease.maxTtlMs, failing
withinvalidValue's text,
Invalid config value for "capabilities.agent-device.leaseTtlCapMs": expected at most lease.maxTtlMs.
- F8's validator without
- Every agent-device piece F8 lists leaves
src/core/config.ts. loadWorker: () => import("./worker.js"). NoloadGatewayuntil #598.
src/agent-device/worker.ts exports createWorker(ctx). It builds the
supervisor and the edge parts from ctx (F2), and returns:
capability, from today's capability source;relayTarget, from today's relay target;mount, the HTTP front's handler (the front keeps usingsrc/http's
helpers, F19; ADR 0030 §6), withctx.config.leaseTtlCapMsand
maxRunMs = ctx.daemonConfig.lease.maxTtlMs;findings, today'sagentDeviceFindingsreturning
{ message, remedy }; the host now stampsmodule(#596's line moves
here);hooks.afterSocketClaimed:void supervisor.start()(F5);hooks.beforeDeviceReclaim: the lease-end participant;hooks.beforeDrain: dispose the edge, then cancel waiting allocates
throughctx.cancelPending(F4);hooks.beforeDriversDispose:supervisor.stop()(F3).
src/agent-device/testing.ts exports
agentDeviceModuleWith({ upstream?, health? }), a definition whose
createWorker uses the given fakes. With upstream, the supervisor is
never started, as F5 does today.
Other changes:
src/agent-device/index.tskeeps only what the gateway path uses
(F16).main.ts's gateway path loads it withawait import(...)only
whencapabilities.agent-device.enabledistrue. It reads the section
through the typedefinition.tsexports, and a missing section counts
as off. Every other name F1 lists leavesmain.ts.StartDaemonOptionsdropsagentDeviceUpstreamandagentDeviceHealth.- The supervisor drops its
enabledbranch (F6). It is built only for a
turned-on module. - The supervisor's remedies say
capabilities.agent-device.command(F13). - A gateway warns
Ignoring "capabilities.agent-device.command": it configures a worker, and this daemon runs in gateway mode.,
#595's generic text, in place of F20's. - New dependency-cruiser rule
main-loads-modules-lazily:main.ts
importssrc/agent-deviceonly asdynamic-import,type-onlyor
type-import(F17). #598 tightens it to no import at all.
Contract and event changes
configSchema drops agentDevice (F10); agent-device's section travels in
capabilities. This PR bumps the protocol by one. No event changes.
Other code on the same state
- The gateway path in
main.ts(F16) reads agent-device's config. It
switches toconfig.capabilities["agent-device"], with the same
behaviour. - #595's host tests pin agent-device's hand-wired entry first in the
capability list, and its stop steps before the hooks. With the hand
wiring gone, those tests assert the registry order only, with
agent-device as a registered module. - The core doctor gets only
host.findings()atmain.ts:329. - A leftover agent-device daemon while off: nothing acts on it any
more. The next start with agent-device on reaps it, as today (F6).
Rules in play
architecture.mdrules 18 and 21:main.tsnames no module on the
worker path.safety.md: the reap's ownership proof is unchanged.testing.md: delete only F7's two tests, whose path is gone. F9's tests
move, they are not deleted.documentation.md: F15, plusCLI.md,HTTP-API.mdandCLIENT.md
wherever they writeagentDevice.
Tests
Seam:
startDaemon({ optionalModules: [agentDeviceModuleWith({ upstream: fake })], configOverrides: { capabilities: { "agent-device": … } } })
in place of agentDeviceUpstream in the suites F11 names, modelled on
src/agent-device/edge.test.ts. Those suites still pass, with their setup
changed and F12's three assertions renamed to the new key.
- a worker with no
capabilities["agent-device"]section answers status.get with{ name: agent-device, state: off }, and so does one withenabled: false - a worker with agent-device off never calls agent-device's
loadWorker(a spying definition that wrapsagentDeviceModule) - a gateway with agent-device off, or with no section for it, mounts no
/v1/agent-device(as today) - a worker whose config has only the old
agentDevicekey warnsUnknown config key: "agentDevice"and reports agent-deviceoff - a worker with agent-device off starts no supervisor: no
psruns and no signal is sent, even with a recorded daemon on disk - F9's tests move to
src/agent-device/definition.test.ts, undercapabilities.agent-device(as today):- the defaults, and an explicit section kept as written;
commandrefused when empty, and when a number;leaseTtlCapMsaccepted at 5000 and atlease.maxTtlMs, refused as a fraction, refused below 5000, and refused abovelease.maxTtlMswithinvalidValue's text namingcapabilities.agent-device.leaseTtlCapMs;- a loaded config parsed through
configSchemakeeps the section; - on a gateway,
enabledandleaseTtlCapMsread without a warning, andcommandwarned about with the generic text
startDaemonwith agent-device on stops the agent-device child before the drivers are disposed (as today,main.test.ts:2449)- with agent-device on, a release still waits for agent-device's lease end before the device is reclaimed (as today)
daemon stopcancels an agent-device allocate waiting in the queue before leasing settles (as today)doctor.runreports{ kind: "module-unavailable", module: "agent-device" }, with the module stamped by the host- the supervisor's not-found and not-agent-device remedies name
capabilities.agent-device.command(F12's two supervisor assertions) config.getshows agent-device's section undercapabilities["agent-device"]and noagentDevicekey- the conformance suite runs over agent-device as a registered module
- dependency-cruiser fails a value import of
src/agent-devicefrommain.ts(a lint-harness case);pnpm lintpassing on this PR'smain.ts, with its dynamic and type imports, shows the allowed kinds pass - the protocol version is one above the base branch's
Done when
- Every Tests line passes, and every other test in the suites F11 names
passes with only its setup changed and F12's assertions renamed. - F7's two supervisor tests are deleted. No other agent-device test is
deleted. docs/CONFIGURATION.mddocumentscapabilities.agent-device.*. Its
leftover-daemon line says it applies with
capabilities.agent-device.enabledtrue.- F14's config writes and deletes use
capabilities["agent-device"]. On a
Mac with an iOS runtime:scripts/slow-e2e.sh e2e/slow-agent-device.test.ts
andscripts/slow-e2e.sh e2e/slow-agent-device-gateway.test.tspass.
Out of scope
- The gateway side moving onto the host (#598).
- The doctor finding's shape (#596).
Depends on
- #595
Written by an agent.
- 主要言語
- TypeScript
- スター
- 19
- フォーク
- 1
- 平均マージ
- 7時間 57分
- マージ済み PR(30日)
- 213
環境構築
このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
callstackincubator/simlock のほかの issue
-
Lint does not enforce index.ts boundaries for cleanup/, routing/, warm-pool/ and contract/ helpers対応中かも このイシューにリンクされたプルリクエストがオープン中、またはマージ済みです。 オープンbug:new
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
callstackincubator/simlock#445 ·
メンテナーはふだん 1 日以内に返信
-
bug:new
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
callstackincubator/simlock#424 ·
メンテナーはふだん 1 日以内に返信
-
daemon-lifecycle e2e: the it.fails status test passes for the wrong reason対応中かも @V3RON が 7 日前に担当しました。 オープンbug:triage
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
callstackincubator/simlock#422 · コメント 1 件 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
-
feature:spec
難易度 5/5 1週間以上 初心者へのやさしさ 8/100
callstackincubator/simlock#602 ·
メンテナーはふだん 1 日以内に返信
-
agent-device's gateway side runs as an optional module対応中かも @V3RON が 1 日前に担当しました。 オープンtask:ready
callstackincubator/simlock#598 · コメント 2 件 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
callstackincubator/simlock の issue をすべて見る
似ている issue
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 80/100
aws/agentcore-cli#2606 ·
メンテナーはふだん 1 日以内に返信
-
ai-driven-qa bug Cosmetic
難易度 2/5 1〜3時間 初心者へのやさしさ 74/100
メンテナーはふだん 1 日以内に返信
-
ai-driven-qa bug claude Minor
難易度 2/5 1〜3時間 初心者へのやさしさ 64/100
linagora/twake-mail-admin#301 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
メンテナーはふだん 1 日以内に返信
-
reproduced
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
anomalyco/opencode#56038 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信