rtrt-agent 0.1.7 → 0.2.0

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/README.ko.md CHANGED
@@ -4,23 +4,25 @@
4
4
  agent, worktree, invocation 및 permission broker 문맥을 보존하고 로컬 대시보드를
5
5
  백그라운드에서 사용할 수 있게 유지합니다.
6
6
 
7
- ## 설치
7
+ ## 설치 (v1 setup-managed wiring, v0.2.0)
8
+
9
+ 이 소스는 v0.2.0을 대상으로 합니다. 설치하려면 버전이 일치하는 게시된 npm release가 필요하며, 소스의 버전 표시는 게시 증명이 아닙니다. 이전 `rtrt-agent@0.1.7` release는 그대로이며 아래 native v2 진입점을 포함하지 않습니다.
10
+
11
+ 버전이 일치하는 npm release의 설치 형태는 다음과 같습니다.
8
12
 
9
13
  ```sh
10
- npm install rtrt-agent@0.1.7
14
+ npm install rtrt-agent@0.2.0
11
15
  ```
12
16
 
13
17
  `opencode.json`의 단수형 `plugin` 배열에 패키지를 등록합니다.
14
18
 
15
19
  ```json
16
20
  {
17
- "plugin": ["rtrt-agent@0.1.7"]
21
+ "plugin": ["rtrt-agent@0.2.0"]
18
22
  }
19
23
  ```
20
24
 
21
- OpenCode는 이름 있는 `RtrtProvenance` export를 로드합니다. 이 패키지는 의도적으로
22
- default export를 제공하지 않습니다. 선택 사항인 RTRT TUI statusline은 별도 소스
23
- 통합으로 유지되며 이 패키지에서 export하거나 pack하지 않습니다.
25
+ v1 패키지 root는 이름 있는 export `RtrtProvenance` 하나를 그대로 유지하며 v0.2.0에서도 이름은 바뀌지 않습니다. 선택 사항인 v1 TUI statusline은 별도 setup-managed 통합으로 남고 v1 패키지는 이를 export하거나 pack하지 않습니다. 새로 staged 된 native v2 소스는 아래에서 설명하며 `rtrt-agent@0.1.7`에는 게시되지 않습니다.
24
26
 
25
27
  ## 대시보드
26
28
 
@@ -44,20 +46,53 @@ state가 안전하지 않거나 없으면 시작만 fail-soft로 중단되고 Op
44
46
 
45
47
  `rtrt setup --agent opencode --apply`는 전체 RTRT 통합을 관리하지만 npm 설치는 수행하지
46
48
  않으며, 설정된 패키지는 OpenCode가 시작할 때 설치합니다. Setup은 먼저 정확한
47
- `rtrt-agent@0.1.7` 등록과 모든 대체 관리 asset을 기록합니다. 이 기록이 성공한 뒤에만
49
+ `rtrt-agent@0.2.0` 등록과 모든 대체 관리 asset을 기록합니다. 이 기록이 성공한 뒤에만
48
50
  인식된 legacy RTRT plugin을 제거하며, 이전 단계가 실패하면 legacy runtime을 보존합니다.
49
51
  Setup은 `rtrt-agent`의 정확히 일치하는 bare, pinned, ranged, tuple, object 형식만 소유하고
50
- 정확한 `"rtrt-agent@0.1.7"` string 하나로 정규화합니다. 이전의 미게시 `rtrt`와 초안
52
+ 정확한 `"rtrt-agent@0.2.0"` string 하나로 정규화합니다. 이전의 미게시 `rtrt`와 초안
51
53
  `rtrt-opencode` 패키지 spec은 외부 값이며 순서를 유지합니다. 위 직접 등록과
52
- `opencode plugin rtrt-agent@0.1.7 --global`은 계속 유효합니다.
54
+ `opencode plugin rtrt-agent@0.2.0 --global`은 계속 유효합니다.
53
55
 
54
56
  관리 agent 소유권 상태는 다음 순서의 첫 비어 있지 않은 root에서 읽습니다.
55
57
  `$OPENCODE_CONFIG_DIR`, `$XDG_CONFIG_HOME/opencode`, `~/.config/opencode`. 공존은 OMO
56
58
  4.19.4를 대상으로 CI에서 검사하고 OpenCode 1.18.29에서 직접 검증했으며, 어느 쪽도
57
59
  미래 버전 지원을 보장하지 않습니다. 통합 RTRT 릴리스 워크플로는 정확한 버전의
58
- 대시보드 플랫폼 패키지 5개를 `rtrt-agent@0.1.7`보다 먼저 게시한 뒤 일치하는 Rust
60
+ 대시보드 플랫폼 패키지 5개를 `rtrt-agent@0.2.0`보다 먼저 게시한 뒤 일치하는 Rust
59
61
  릴리스 artifact를 게시합니다.
60
62
 
63
+ ## Native OpenCode 2.0.20 진입점
64
+
65
+ v0.2.0 source manifest의 pack 목록에는 native v2 server와 TUI 파일이 추가되지만, npm registry의 `rtrt-agent@0.1.7`에는 **포함되지 않습니다**. 게시된 `rtrt-agent@0.1.7`은 native v2 진입점을 설치·등록하지 않으며, 과거 `0.1.7` 패키지 계약은 게시된 그대로 유지됩니다. v0.2.0 native 소스의 검증 대상은 정확히 `@opencode/cli@2.0.20`과 `@opencode/plugin@2.0.20`이며, 1.x SDK의 v2 preview가 아닙니다. 패키지 root는 v1 이름 있는 export `RtrtProvenance`를 유지합니다 (이름만, v0.2.0에서도 변경 없음).
66
+
67
+ v0.2.0 source pack에 staged 된 항목:
68
+
69
+ - `./server`의 native `Plugin.Definition` default export. 이름 있는 `createNativeServer` 팩토리가 `{ id: "rtrt-agent", setup }` 형태로 생성합니다. v1 `RtrtProvenance` 경로는 그대로 별도입니다.
70
+ - `package.json`의 `exports`에서 `./server`와 `./tui`에 사용하는 single-element 배열. OpenCode 1.18.33 legacy resolver는 배열을 무시하고 `main`인 `index.js`로 fallback 하며, native 2.0.20은 정상적으로 해석합니다.
71
+ - v1 TUI statusline은 여전히 별도의 `app_bottom` 등록입니다. v2 native `rtrt-statusline.tsx`는 `tui/v2/` 아래에 있고 2.x에서만 동작하며 v1 연결은 그대로입니다.
72
+ - Native `prompt.footer.status` 등록과 server·TUI hook의 cleanup.
73
+
74
+ Tool-arg provenance, 대시보드 자동 시작, 명시적 `rtrt_dashboard_open`은 모두 native server 뒤에 있습니다. Permission 정책은 fail-closed이며 명시적 `deny`는 v1 broker와 v2 evaluator 양쪽에서 그대로 보존됩니다. 외부 Claude CLI permission broker 지원과 per-call shell identity / provider-limit recovery는 native v2 build에서 **지원되지 않습니다**. 이는 2.0.20 public API에 해당 표면이 없기 때문이며 어떤 workaround로도 자동 승인되지 않습니다. 기본 deny 정책은 v1 broker에서와 동일합니다.
75
+
76
+ ### 로컬 소스 checkout (npm 설치 없음)
77
+
78
+ 로컬 checkout으로 개발할 때는 OpenCode의 native `plugins` config가 디렉터리 참조를 받습니다. 정확한 경로는 checkout에 따라 다르므로 형식만 보여 줍니다.
79
+
80
+ ```json
81
+ {
82
+ "plugins": [
83
+ "/absolute/path/to/rtrt/plugins/opencode"
84
+ ]
85
+ }
86
+ ```
87
+
88
+ 이 디렉터리 형식은 격리된 HOME/XDG 환경에서 실제 `@opencode/cli@2.0.20` host로 로컬 검증했습니다. 그 정확한 버전에서 `server.js`가 active이고 native TUI 탐색 결과는 `features.tui: true`이며 RTRT stdio MCP가 연결됩니다. TUI strict typecheck, 번들 빌드, 120열·40열 headless footer 렌더링도 통과합니다. 검증은 전체 interactive OpenCode TUI를 다루지 않으며 다른 OpenCode 버전을 보장하지 않습니다. 검증 대상은 정확히 테스트한 `@opencode/cli@2.0.20` host에 한정됩니다.
89
+
90
+ ### Native MCP 형식 (정보 제공, OpenCode 2 공식 schema)
91
+
92
+ Native local MCP 형식은 `mcp.servers.rtrt.type: "local"`과 `["/absolute/path/to/rtrt-mcp", "--transport", "stdio"]` 같은 `command` 배열을 사용합니다. 옵션 이름은 `disabled`와 `codemode`이며 sandbox source config에서는 둘 다 `false`입니다. Release에 고정된 v1 등록은 `rtrt-agent@0.2.0`입니다. `rtrt setup --agent opencode --apply`는 여전히 v1 설정을 기록하며 native v2 설치가 아닙니다. Setup 자체는 npm 패키지를 설치하지 않고, OpenCode가 시작할 때 설정된 패키지를 해석합니다.
93
+
94
+ 게시된 `rtrt-agent@0.1.7`은 npm registry에서 그대로입니다. Sandbox의 기본 v2 설정은 MCP-only이며 staged native plugin·footer·forwarding adapter는 그 경로에서 활성화되지 않습니다. 이 소스 작업은 Docker 배포, `rtrt collector` 서비스, 운영자 측 `forward` 배선 또는 다른 운영 서비스를 promote, 활성화, 연결하지 않습니다 — 이 표면은 분리되어 있고 v0.2.0 소스 변경으로 켜지지 않습니다.
95
+
61
96
  ## 라이선스
62
97
 
63
98
  MIT
package/README.md CHANGED
@@ -4,23 +4,25 @@
4
4
  worktree, invocation, and permission-broker context across supported hooks and keeps
5
5
  the local dashboard available in the background.
6
6
 
7
- ## Install
7
+ ## Install (v1 setup-managed wiring, v0.2.0)
8
+
9
+ This source targets v0.2.0. Installation requires the matching published npm release; a source version marker is not proof of publication. The older `rtrt-agent@0.1.7` release remains unchanged and does not include the native v2 entries described below.
10
+
11
+ For the matching npm release:
8
12
 
9
13
  ```sh
10
- npm install rtrt-agent@0.1.7
14
+ npm install rtrt-agent@0.2.0
11
15
  ```
12
16
 
13
17
  Register the package in the singular `plugin` array in `opencode.json`:
14
18
 
15
19
  ```json
16
20
  {
17
- "plugin": ["rtrt-agent@0.1.7"]
21
+ "plugin": ["rtrt-agent@0.2.0"]
18
22
  }
19
23
  ```
20
24
 
21
- OpenCode loads the named `RtrtProvenance` export. The package intentionally has no
22
- default export. The optional RTRT TUI statusline remains a separate source integration
23
- and is not exported or packed by this package.
25
+ The v1 package root remains the named export `RtrtProvenance` only — its name is unchanged across the v0.2.0 source. The optional v1 TUI statusline stays a separate setup-managed integration and is not exported or packed by the v1 package. The newly staged native v2 source is described below and is not published in `rtrt-agent@0.1.7`.
24
26
 
25
27
  ## Dashboard
26
28
 
@@ -43,21 +45,54 @@ Existing `~/.rtrt` data is preserved.
43
45
 
44
46
  `rtrt setup --agent opencode --apply` manages the full RTRT integration but performs no
45
47
  npm installation; OpenCode installs the configured package at startup. Setup first writes
46
- the exact `rtrt-agent@0.1.7` registration and every replacement managed asset. Only after
48
+ the exact `rtrt-agent@0.2.0` registration and every replacement managed asset. Only after
47
49
  those writes succeed does it remove a recognized legacy RTRT plugin; an earlier failure
48
50
  preserves the legacy runtime. Setup owns exact bare, pinned, ranged, tuple, and object forms
49
- of `rtrt-agent` only and normalizes them to one exact `"rtrt-agent@0.1.7"` string. Old
51
+ of `rtrt-agent` only and normalizes them to one exact `"rtrt-agent@0.2.0"` string. Old
50
52
  unpublished `rtrt` and draft `rtrt-opencode` package specs are foreign and retain their
51
- order. The direct registration above and `opencode plugin rtrt-agent@0.1.7 --global`
53
+ order. The direct registration above and `opencode plugin rtrt-agent@0.2.0 --global`
52
54
  remain valid.
53
55
 
54
56
  Managed-agent ownership state is read from the first nonempty root in this order:
55
57
  `$OPENCODE_CONFIG_DIR`, `$XDG_CONFIG_HOME/opencode`, then `~/.config/opencode`.
56
58
  Coexistence is CI-gated against OMO 4.19.4 and was verified on OpenCode 1.18.29; neither
57
59
  is a future-version guarantee. The unified RTRT release workflow publishes the exact
58
- five exact-version dashboard platform packages before `rtrt-agent@0.1.7`, then publishes
60
+ five exact-version dashboard platform packages before `rtrt-agent@0.2.0`, then publishes
59
61
  the matching Rust release artifacts.
60
62
 
63
+ ## Native OpenCode 2.0.20 Entries
64
+
65
+ The v0.2.0 manifest includes native v2 server and TUI files in the pack inventory. The older `rtrt-agent@0.1.7` release does not include or register those entries and remains unchanged. The native source targets exactly `@opencode/cli@2.0.20` and `@opencode/plugin@2.0.20`, not the 1.x SDK's v2 preview. The package root remains the v1 named export `RtrtProvenance` (name-only, unchanged across v0.2.0).
66
+
67
+ What is staged in the v0.2.0 source pack:
68
+
69
+ - A native `Plugin.Definition` default export at `./server`, constructed by the named `createNativeServer` factory and exposing `{ id: "rtrt-agent", setup }`. The v1 `RtrtProvenance` path stays separate.
70
+ - Single-element arrays for `./server` and `./tui` in `package.json` `exports`. OpenCode 1.18.33's legacy resolver ignores those arrays and falls back to `main` `index.js`; native 2.0.20 resolves them normally.
71
+ - A v1 TUI statusline that remains a separate `app_bottom` registration. The v2 native `rtrt-statusline.tsx` lives under `tui/v2/` and only runs in 2.x; v1 wiring is unchanged.
72
+ - A native `prompt.footer.status` registration and cleanup for both server and TUI hooks.
73
+
74
+ Tool-arg provenance, dashboard auto-startup, and an explicit `rtrt_dashboard_open` all live behind the native server. Permission policies are fail-closed: explicit `deny` is preserved across the v1 broker and the v2 evaluator. External Claude CLI permission broker support and per-call shell identity / provider-limit recovery are **unsupported** in the native v2 build because those surfaces are not in the 2.0.20 public API; they are not auto-approved via any workaround. The deny-by-default policy is unchanged from the v1 broker.
75
+
76
+ ### Local source checkout (no npm install)
77
+
78
+ For development against a local checkout, OpenCode's native `plugins` config accepts a directory reference. Exact paths depend on your checkout, so only the shape is shown:
79
+
80
+ ```json
81
+ {
82
+ "plugins": [
83
+ "/absolute/path/to/rtrt/plugins/opencode"
84
+ ]
85
+ }
86
+ ```
87
+
88
+ This directory form was verified locally with the actual `@opencode/cli@2.0.20` host in an isolated HOME/XDG environment. With that exact version: `server.js` is active, native TUI discovery reports `features.tui: true`, RTRT stdio MCP connects, strict TUI typechecking and bundling pass, and headless footer rendering at 120 and 40 columns passes. The verification does not cover the full interactive OpenCode TUI and is not a guarantee for other OpenCode versions; it is specific to the exact `@opencode/cli@2.0.20` host that was tested.
89
+
90
+ ### Native MCP shape (informational, OpenCode 2 official schema)
91
+
92
+ The native local MCP shape uses `mcp.servers.rtrt.type: "local"` and a `command` array such as `["/absolute/path/to/rtrt-mcp", "--transport", "stdio"]`; the option names are `disabled` and `codemode`, both `false` in the sandbox source config. The release-pinned v1 registration is `rtrt-agent@0.2.0`. `rtrt setup --agent opencode --apply` still writes v1 configuration, not native v2 installation; setup itself does not install npm packages, and OpenCode resolves the configured package at startup.
93
+
94
+ Published `rtrt-agent@0.1.7` remains unchanged on the npm registry. The sandbox's default v2 configuration is MCP-only; the staged native plugin, footer, and forwarding adapter are not activated there. This source work does not promote, activate, or wire up a Docker deployment, the `rtrt collector` service, the operator-side `forward` plumbing, or any other operational services — those remain separate surfaces and are not enabled by the v0.2.0 source change.
95
+
61
96
  ## License
62
97
 
63
98
  MIT
@@ -1,10 +1,11 @@
1
1
  # Version-pinned distribution notices
2
2
 
3
3
  These are unmodified LICENSE files extracted from the specified public npm and
4
- crates.io archives. `LICENSE` at the distribution root remains RTRT's own MIT
5
- license. This directory accompanies the source, the five GitHub binary archives,
6
- the five dashboard platform npm packages, and `rtrt-agent`. It is a targeted
7
- notice set, not an exhaustive dependency inventory or a legal certification.
4
+ crates.io archives, plus the complete unmodified `option-ext@0.2.0` source crate.
5
+ `LICENSE` at the distribution root remains RTRT's own MIT license. This directory
6
+ accompanies the source, the five GitHub binary archives, the five dashboard
7
+ platform npm packages, and `rtrt-agent`. It is a targeted notice set, not an
8
+ exhaustive dependency inventory or a legal certification.
8
9
 
9
10
  The five MIT JS bundles `cytoscape-fcose`, `cytoscape-cola`, `cose-base`,
10
11
  `layout-base`, and `webcola` are embedded in the dashboard without their upstream
@@ -32,9 +33,11 @@ the matching `https://registry.npmjs.org/<name>/<version>` `dist.integrity`
32
33
  | webpki-roots@1.0.7 | https://static.crates.io/crates/webpki-roots/webpki-roots-1.0.7.crate | `52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d` |
33
34
  | option-ext@0.2.0 | https://static.crates.io/crates/option-ext/option-ext-0.2.0.crate | `04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d` |
34
35
 
35
- Paths below are relative to this directory; SHA-256 is of the extracted file,
36
- not the archive. For npm, the upstream path is `package/LICENSE`. For crates,
37
- the upstream path is `<name>-<version>/<path below>`.
36
+ Paths below are relative to this directory; SHA-256 is of the distributed local
37
+ file. LICENSE rows hash extracted files, not their upstream archives. For npm,
38
+ the upstream path is `package/LICENSE`. For crates, the upstream license path
39
+ is `<name>-<version>/<path below>`. The `SOURCE.crate` row instead hashes the
40
+ unmodified complete upstream archive bytes (also listed in the archive table).
38
41
 
39
42
  | Extracted file | SHA-256 |
40
43
  |---|---|
@@ -54,10 +57,20 @@ the upstream path is `<name>-<version>/<path below>`.
54
57
  | `webpki-roots@0.26.11/LICENSE` | `e271993808fec50ab29350b39539cdec611a9103f827e0aa26d61da70e2d33f8` |
55
58
  | `webpki-roots@1.0.7/LICENSE` | `e271993808fec50ab29350b39539cdec611a9103f827e0aa26d61da70e2d33f8` |
56
59
  | `option-ext@0.2.0/LICENSE.txt` | `66a3107d5ad6a058aab753eaac2047ccb2ed0e39465dd0fe5844da3e300d5172` |
60
+ | `option-ext@0.2.0/SOURCE.crate` | `04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d` |
57
61
 
58
62
  `ring`'s top-level LICENSE explains its ISC/Apache-2.0 BoringSSL split and
59
63
  points to the included once_cell subpart texts. The included fiat subpart
60
- license is copied from the same crate. `option-ext` is MPL-2.0: this notice
61
- alone is not a substitute for assessment of source-code availability under
62
- MPL-2.0 section 3.2; the versioned source is available from the crate URL
63
- above, but counsel should assess whether additional delivery is required.
64
+ license is copied from the same crate. `option-ext@0.2.0` is MPL-2.0: its
65
+ complete, unchanged upstream source archive is bundled at
66
+ `THIRD_PARTY_NOTICES/option-ext@0.2.0/SOURCE.crate` inside each of the five
67
+ GitHub binary archives, five dashboard platform npm packages, and the
68
+ `rtrt-agent` npm package. It is the same version and SHA-256 as `Cargo.lock`;
69
+ recipients need no network download. From the distribution root, run
70
+ `mkdir option-ext-source && tar -xzf THIRD_PARTY_NOTICES/option-ext@0.2.0/SOURCE.crate -C option-ext-source`
71
+ to read the original `option-ext-0.2.0/src/`, `Cargo.toml`, `Cargo.toml.orig`,
72
+ `README.md`, and `LICENSE.txt` beneath `option-ext-source/`. The URL above is
73
+ an additional upstream retrieval option, not the only source offer. This
74
+ source-code copy applies only to the MPL-covered `option-ext` component; it
75
+ does not relicense RTRT. Qualified counsel should assess any remaining
76
+ distribution obligations, including MPL-2.0 section 3.2.
package/package.json CHANGED
@@ -1,11 +1,17 @@
1
1
  {
2
2
  "name": "rtrt-agent",
3
- "version": "0.1.7",
3
+ "version": "0.2.0",
4
4
  "description": "RTRT provenance, permission-broker, and local dashboard plugin for OpenCode",
5
5
  "type": "module",
6
6
  "main": "./index.js",
7
7
  "exports": {
8
- ".": "./index.js"
8
+ ".": "./index.js",
9
+ "./server": [
10
+ "./server.js"
11
+ ],
12
+ "./tui": [
13
+ "./tui/v2/rtrt-statusline.tsx"
14
+ ]
9
15
  },
10
16
  "bin": {
11
17
  "rtrt-dashboard-open": "./bin/rtrt-dashboard-open.js"
@@ -13,12 +19,20 @@
13
19
  "files": [
14
20
  "index.js",
15
21
  "rtrt-provenance.js",
22
+ "server.js",
23
+ "v2/permissions.js",
16
24
  "bin/rtrt-dashboard-open.js",
17
25
  "runtime/dashboard-binary.js",
18
26
  "runtime/dashboard-files.js",
19
27
  "runtime/dashboard-plugin.js",
20
28
  "runtime/dashboard-process.js",
21
29
  "runtime/dashboard-supervisor.js",
30
+ "tui/index.js",
31
+ "tui/rtrt-statusline-core.mjs",
32
+ "tui/rtrt-statusline-core.d.mts",
33
+ "tui/v2/native-runtime.mjs",
34
+ "tui/v2/native-state.mjs",
35
+ "tui/v2/rtrt-statusline.tsx",
22
36
  "README.md",
23
37
  "README.ko.md",
24
38
  "LICENSE",
@@ -38,12 +52,9 @@
38
52
  "THIRD_PARTY_NOTICES/subtle@2.6.1/LICENSE",
39
53
  "THIRD_PARTY_NOTICES/webpki-roots@0.26.11/LICENSE",
40
54
  "THIRD_PARTY_NOTICES/webpki-roots@1.0.7/LICENSE",
41
- "THIRD_PARTY_NOTICES/option-ext@0.2.0/LICENSE.txt"
55
+ "THIRD_PARTY_NOTICES/option-ext@0.2.0/LICENSE.txt",
56
+ "THIRD_PARTY_NOTICES/option-ext@0.2.0/SOURCE.crate"
42
57
  ],
43
- "scripts": {
44
- "test": "node --test rtrt-provenance.test.mjs dashboard-binary.test.mjs dashboard-open.test.mjs dashboard-supervisor.test.mjs package.test.mjs notices-package.test.mjs platform-package.test.mjs platform-publish.test.mjs tui/rtrt-statusline.test.mjs",
45
- "sync:platforms": "node ../../packaging/npm/agent-contract.mjs --sync"
46
- },
47
58
  "engines": {
48
59
  "node": ">=20"
49
60
  },
@@ -67,11 +78,31 @@
67
78
  "dependencies": {
68
79
  "@opencode-ai/sdk": "1.15.13"
69
80
  },
81
+ "peerDependencies": {
82
+ "@opencode/plugin": ">=1.18.33 <3",
83
+ "@opentui/core": ">=0.4.5",
84
+ "@opentui/solid": ">=0.4.5",
85
+ "solid-js": ">=1.9.0"
86
+ },
87
+ "peerDependenciesMeta": {
88
+ "@opencode/plugin": {
89
+ "optional": true
90
+ },
91
+ "@opentui/core": {
92
+ "optional": true
93
+ },
94
+ "@opentui/solid": {
95
+ "optional": true
96
+ },
97
+ "solid-js": {
98
+ "optional": true
99
+ }
100
+ },
70
101
  "optionalDependencies": {
71
- "rtrt-dashboard-linux-x64": "0.1.7",
72
- "rtrt-dashboard-linux-arm64": "0.1.7",
73
- "rtrt-dashboard-darwin-x64": "0.1.7",
74
- "rtrt-dashboard-darwin-arm64": "0.1.7",
75
- "rtrt-dashboard-win32-x64": "0.1.7"
102
+ "rtrt-dashboard-linux-x64": "0.2.0",
103
+ "rtrt-dashboard-linux-arm64": "0.2.0",
104
+ "rtrt-dashboard-darwin-x64": "0.2.0",
105
+ "rtrt-dashboard-darwin-arm64": "0.2.0",
106
+ "rtrt-dashboard-win32-x64": "0.2.0"
76
107
  }
77
108
  }
package/server.js ADDED
@@ -0,0 +1,98 @@
1
+ import { randomUUID } from "node:crypto"
2
+ import path from "node:path"
3
+
4
+ import { createDashboardSupervisor } from "./runtime/dashboard-supervisor.js"
5
+ import { createPermissionEvaluator } from "./v2/permissions.js"
6
+
7
+ const RTRT_TOOLS = new Set(["rtrt_agent_call", "rtrt_agent_route"])
8
+ const DASHBOARD_INPUT = { type: "object", properties: {}, additionalProperties: false }
9
+
10
+ function scheduleStartup(start) {
11
+ const timer = setTimeout(() => { void start() }, 0)
12
+ timer.unref?.()
13
+ return { cancel: () => clearTimeout(timer) }
14
+ }
15
+
16
+ // The native server contract is @opencode/plugin 2.0.20. The v1 root remains
17
+ // separate: v2 has no permission.create or shell call identity, so it cannot
18
+ // provide the legacy Claude permission broker or per-call shell provenance.
19
+ export function createNativeServer({ supervisor, schedule = scheduleStartup, managedAgentStatePath, report = (message) => process.emitWarning(message, "RTRT_NATIVE_PLUGIN") } = {}) {
20
+ return {
21
+ id: "rtrt-agent",
22
+ async setup(ctx) {
23
+ const dashboard = supervisor ?? createDashboardSupervisor()
24
+ const invocations = new Map()
25
+ const registrations = []
26
+ let disposed = false
27
+ let cleanupPromise
28
+ const notify = (message) => {
29
+ try { report(message) } catch {
30
+ process.emitWarning("RTRT native diagnostic unavailable.", "RTRT_NATIVE_PLUGIN")
31
+ }
32
+ }
33
+ const startup = schedule(() => Promise.resolve()
34
+ .then(() => { if (!disposed) return dashboard.ensure() })
35
+ .catch(() => { if (!disposed) notify("RTRT dashboard unavailable.") }))
36
+ const dispose = () => {
37
+ if (cleanupPromise) return cleanupPromise
38
+ disposed = true
39
+ invocations.clear()
40
+ cleanupPromise = (async () => {
41
+ const failures = []
42
+ try { startup.cancel() } catch { failures.push(new Error("RTRT startup cancellation failed.")) }
43
+ const outcomes = await Promise.allSettled(registrations.reverse().map((registration) =>
44
+ Promise.resolve().then(() => registration.dispose())))
45
+ for (const [index, outcome] of outcomes.entries()) {
46
+ if (outcome.status === "rejected") failures.push(new Error(`RTRT registration ${index} disposal failed.`))
47
+ }
48
+ if (failures.length) throw new AggregateError(failures, "RTRT native cleanup failed.")
49
+ })()
50
+ return cleanupPromise
51
+ }
52
+ try {
53
+ registrations.push(await ctx.tool.hook("execute.before", (event) => {
54
+ if (disposed || !RTRT_TOOLS.has(event.tool) || !event.input || typeof event.input !== "object" || Array.isArray(event.input)) return
55
+ const key = `${event.sessionID}\0${event.id}`
56
+ let invocationID = invocations.get(key)
57
+ if (!invocationID) {
58
+ invocationID = randomUUID()
59
+ invocations.set(key, invocationID)
60
+ }
61
+ Object.assign(event.input, {
62
+ invocation_id: invocationID,
63
+ parent_project: path.basename(ctx.location.project.canonical),
64
+ parent_session_id: event.sessionID,
65
+ parent_call_id: event.id,
66
+ caller_agent: event.agent,
67
+ parent_cwd: ctx.location.directory,
68
+ parent_worktree: ctx.location.project.canonical,
69
+ })
70
+ }))
71
+ registrations.push(await ctx.tool.hook("execute.after", (event) => {
72
+ if (!disposed && RTRT_TOOLS.has(event.tool)) invocations.delete(`${event.sessionID}\0${event.id}`)
73
+ }))
74
+ registrations.push(await ctx.tool.transform((editor) => {
75
+ if (disposed) return
76
+ editor.add({
77
+ name: "rtrt_dashboard_open",
78
+ description: "Open the RTRT dashboard in the operator's browser when explicitly requested.",
79
+ input: DASHBOARD_INPUT,
80
+ async execute() {
81
+ if (disposed) return { content: "Dashboard unavailable." }
82
+ const status = await dashboard.open().catch(() => "Dashboard unavailable.")
83
+ return { content: status === "Dashboard open requested." ? status : "Dashboard unavailable." }
84
+ },
85
+ })
86
+ }))
87
+ const evaluate = await createPermissionEvaluator(ctx, managedAgentStatePath, () => !disposed)
88
+ registrations.push(await ctx.permission.hook("evaluate", evaluate))
89
+ return dispose
90
+ } catch (error) {
91
+ try { await dispose() } catch { notify("RTRT native cleanup failed.") }
92
+ throw error
93
+ }
94
+ },
95
+ }
96
+ }
97
+
98
+ export default createNativeServer()
package/tui/index.js ADDED
@@ -0,0 +1 @@
1
+ export { default } from "./v2/rtrt-statusline.tsx"
@@ -0,0 +1,205 @@
1
+ export type StatuslineTone =
2
+ | "accent"
3
+ | "good"
4
+ | "warn"
5
+ | "bad"
6
+ | "muted"
7
+
8
+ export type StatuslineSegment = {
9
+ id: string
10
+ text: string
11
+ compact: string | null
12
+ tone: StatuslineTone
13
+ priority: number
14
+ }
15
+
16
+ export type StatuslinePayload = {
17
+ version: 1
18
+ stale: boolean
19
+ degraded: string[]
20
+ segments: StatuslineSegment[]
21
+ }
22
+ export type StatuslineViewRole = "badge" | "label" | "value" | "separator"
23
+ export type StatuslineViewPart = {
24
+ id: string
25
+ text: string
26
+ role: StatuslineViewRole
27
+ tone: StatuslineTone
28
+ }
29
+ export type StatuslineViewRow = { id: string; parts: StatuslineViewPart[] }
30
+ export type StatuslineViewModel = {
31
+ layout: "wide" | "medium" | "narrow"
32
+ width: number
33
+ rows: StatuslineViewRow[]
34
+ }
35
+ export type StatuslineEconomicsValue = {
36
+ text: string
37
+ tone: StatuslineTone
38
+ used?: number
39
+ limit?: number
40
+ percent?: number
41
+ headroom?: number
42
+ }
43
+ export type StatuslineEconomics = {
44
+ cost: StatuslineEconomicsValue
45
+ context: StatuslineEconomicsValue
46
+ model: StatuslineEconomicsValue
47
+ status: StatuslineEconomicsValue
48
+ week: StatuslineEconomicsValue
49
+ modelRef?: string
50
+ }
51
+ export type StatuslineViewOptions = {
52
+ width?: number
53
+ economics?: StatuslineEconomics
54
+ promptRight?: boolean
55
+ }
56
+ export type StatuslineState = {
57
+ payload: StatuslinePayload
58
+ lastGood: StatuslinePayload | undefined
59
+ stale: boolean
60
+ unavailable: boolean
61
+ }
62
+ export type StatuslineContext = { cwd: string; session: string; width: number; model?: string }
63
+ export type StatuslineRefreshEvent =
64
+ | "server.connected"
65
+ | "project.updated"
66
+ | "session.created"
67
+ | "session.updated"
68
+ | "session.compacted"
69
+ | "session.deleted"
70
+ | "session.diff"
71
+ | "session.error"
72
+ | "session.idle"
73
+ | "session.status"
74
+ export type StatuslineRefreshTarget = { app: boolean; sessionID?: string }
75
+
76
+ export const STATUSLINE_VERSION: 1
77
+ export const STATUSLINE_SEPARATOR: string
78
+ export const STATUSLINE_FALLBACK: string
79
+ export const STATUSLINE_GROUP_SEPARATOR: string
80
+ export const STATUSLINE_WIDE_WIDTH: number
81
+ export const STATUSLINE_MEDIUM_WIDTH: number
82
+ export const DEFAULT_DEBOUNCE_MS: number
83
+ export const DEFAULT_INTERVAL_MS: number
84
+ export const DEFAULT_TIMEOUT_MS: number
85
+ export const STATUSLINE_REFRESH_EVENTS: readonly StatuslineRefreshEvent[]
86
+
87
+ export function parseStatuslineJson(raw: string): StatuslinePayload | undefined
88
+ export function statuslineRefreshTarget(event: unknown, activeSessionID?: string): StatuslineRefreshTarget
89
+ export function statuslineCommandArgs(context: StatuslineContext): string[]
90
+ export function statuslineBinaryCandidates(bin?: unknown, env?: Record<string, string | undefined>): string[]
91
+ export function runStatusline(input: StatuslineContext & {
92
+ bin?: unknown
93
+ env?: Record<string, string | undefined>
94
+ spawnImpl?: (...args: any[]) => any
95
+ timeoutMs?: number
96
+ signal?: AbortSignal
97
+ nowFn?: () => number
98
+ setTimeoutFn?: typeof setTimeout
99
+ clearTimeoutFn?: typeof clearTimeout
100
+ }): Promise<StatuslinePayload | undefined>
101
+ export function createSpawnLimiter(input?: { concurrency?: number }): {
102
+ run<Value>(
103
+ task: (signal: AbortSignal) => Value | Promise<Value>,
104
+ options?: { signal?: AbortSignal; priority?: number },
105
+ ): Promise<Value | undefined>
106
+ activeCount(): number
107
+ queuedCount(): number
108
+ dispose(): void
109
+ }
110
+ export function cellWidth(text: string): number
111
+ export function deriveSessionEconomics(input?: {
112
+ session?: {
113
+ cost?: number
114
+ model?: { id: string; providerID: string }
115
+ }
116
+ messages?: ReadonlyArray<{
117
+ role: string
118
+ providerID?: string
119
+ modelID?: string
120
+ time?: { completed?: number }
121
+ error?: unknown
122
+ tokens?: {
123
+ input?: number
124
+ output?: number
125
+ reasoning?: number
126
+ cache?: { read?: number; write?: number }
127
+ }
128
+ }>
129
+ providers?: ReadonlyArray<{
130
+ id: string
131
+ models: Readonly<Record<string, {
132
+ id: string
133
+ providerID?: string
134
+ name?: string
135
+ limit?: { context?: number }
136
+ }>>
137
+ }>
138
+ status?: { type?: string }
139
+ }): StatuslineEconomics
140
+ export function selectSegments(
141
+ payload: StatuslinePayload,
142
+ options?: { width?: number; compact?: boolean },
143
+ ): StatuslineSegment[]
144
+ export function initialStatuslineState(): StatuslineState
145
+ export function nextStatuslineState(previous: StatuslineState, payload?: StatuslinePayload): StatuslineState
146
+ export function renderSegments(
147
+ state: StatuslineState,
148
+ options?: { width?: number; compact?: boolean },
149
+ ): StatuslineSegment[]
150
+ export function renderStatuslineText(
151
+ state: StatuslineState,
152
+ options?: StatuslineViewOptions & { compact?: boolean },
153
+ ): string
154
+ export function createStatuslineViewModel(
155
+ state: StatuslineState,
156
+ options?: StatuslineViewOptions,
157
+ ): StatuslineViewModel
158
+ export function renderStatuslineViewText(
159
+ state: StatuslineState,
160
+ options?: StatuslineViewOptions,
161
+ ): string
162
+
163
+ export function createRefreshScheduler(input: {
164
+ run: () => void | Promise<void>
165
+ debounceMs?: number
166
+ intervalMs?: number
167
+ setTimeoutFn?: typeof setTimeout
168
+ clearTimeoutFn?: typeof clearTimeout
169
+ setIntervalFn?: typeof setInterval
170
+ clearIntervalFn?: typeof clearInterval
171
+ }): {
172
+ start(): void
173
+ request(immediate?: boolean): void
174
+ flush(): Promise<void>
175
+ dispose(): void
176
+ }
177
+
178
+ export function createStatuslineController(input: {
179
+ load: (
180
+ context: StatuslineContext,
181
+ signal: AbortSignal,
182
+ ) => StatuslinePayload | undefined | Promise<StatuslinePayload | undefined>
183
+ onState?: (state: StatuslineState) => void
184
+ debounceMs?: number
185
+ intervalMs?: number
186
+ timers?: Record<string, unknown>
187
+ }): {
188
+ start(): void
189
+ request(immediate?: boolean): void
190
+ refreshNow(): Promise<void>
191
+ dispose(): void
192
+ setContext(context: Partial<StatuslineContext>): void
193
+ getState(): StatuslineState
194
+ getContext(): StatuslineContext
195
+ }
196
+
197
+ export function createKeyedControllerPool<Key, Value extends { dispose(): void }>(input: {
198
+ create: (key: Key) => Value
199
+ }): {
200
+ acquire(key: Key): { value: Value; release(): void }
201
+ get(key: Key): Value | undefined
202
+ forEach(callback: (value: Value, key: Key) => void): void
203
+ size(): number
204
+ dispose(): void
205
+ }