mirror of
https://github.com/NeuralNomadsAI/CodeNomad.git
synced 2026-11-01 19:40:35 +00:00
5 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a5efdefc8d
|
feat(missions): resume recurring Missions after OpenCode restarts with native schedules (#1259)
Closes #926. ## What changes Recurring Missions now survive OpenCode restarts (update, crash and relaunch, reboot) without a manual **Resume**, and the scheduler offers more than "once a day at a fixed time". - **Native schedules.** OpenCode's own scheduler (`experimental.schedules`, OpenCode 2.2.3+) owns the calendar; Missions decides whether a passage may run (CAS admission, no duplicates, Pause/Stop, errors and uncertain states stay manual). A dedicated technical conversation with a local no-op model carries the wake, so wakes, titles and compaction make no provider HTTP requests. - **Richer scheduling**, in one collapsed **Scheduling** section of the Create/Edit editor (default: once, immediately, no condition): date, delay, interval (≥15 min), daily, chosen weekdays, up to eight times a day, day of month, native cron; optional end date/duration and a maximum number of launched passages; five event conditions (another schedule succeeds, a one-time Mission completes, new local commits, a new message in a followed conversation, explicit files change). Up to five upcoming dates are previewed in the saved IANA zone. - **Created paused, started with Play.** Everything is editable before the first launch; after it, edits follow the approved matrix (future parameters between passages, objective/notes journaled during a passage). - **Keep OpenCode running.** A Preferences setting supervises the shared OpenCode service through the OS (Windows Task Scheduler with a periodic recovery task, macOS LaunchAgent, Linux systemd user unit with lingering, WSL from Windows). ON by default at the first Play unless OFF was saved; OFF removes supervision without stopping OpenCode; a live service is never killed or duplicated. - **Existing daily schedules** keep their path and controls; no migration. - Help (English and nine translations) and dev-docs are updated. ## Validation - Typechecks (4 workspaces); server Missions/host-lifetime suites; UI unit tests 1945/1945; help 31/31 and build. - Real isolated OpenCode services (2.2.5, minimum 2.2.3): resting schedule resumes after service restart with no client and no Resume; 32 real passages with history/quota/phase preserved; concurrent wakes admit once; DST and catch-up for every frequency type; forced local auto-compaction and restart during maintenance with zero provider HTTP; real upgrade 2.2.3 → 2.2.5. - OS supervision on Windows, WSL, Ubuntu VM (two real reboots, healthy before login; OFF without a session) and macOS VM (Aqua session, crashes, OFF/adoption). On the final bundle: two real periodic Windows recovery relaunches; WSL OFF/Retry on a live HTTP 500 service keeps the same process. - Windows Electron and Tauri packages run in private profiles: paused creation, nine frequencies, preview. - Full browser suite: 956/957 in the last tier; the failure is `directory-browser.test.ts` (folder picker timeout), unchanged by this branch, and that file passes 20/20 on rerun. Earlier dev-baseline runs on the same machine also lost module loads to `net::ERR_NO_BUFFER_SPACE`. ## Known limits (not qualified in this PR) - macOS reboot followed by a real login, and a Windows host reboot/logon (needs a disposable bench). - Real screen-reader output (keyboard, ARIA and RTL checks pass). - Linux/macOS desktop packages. - OpenCode's background *pre*-compaction never applies to the technical wake conversations: their local model reports zero tokens, below the native minimum. Their automatic local compaction is qualified. ## Follow-up Integration with native `/goal` comes in a separate PR. |
||
|
|
794ac20306
|
feat(integrations): recognise OpenCode integrations from the reviewed catalog and read their state (#1243)
## Summary Slice C2 of the OpenCode integrations plan: CodeNomad recognises configured OpenCode integrations from the reviewed catalog and reads their state. This is the base for attaching an open application to a conversation (C4) and for the Bundles extension (C3). - **Catalog** (`opencode/integrations/catalog.ts`): newest `catalog-v<n>` release of `NeuralNomadsAI/opencode-integrations` (GitHub releases API, never "latest"), `catalog.json` asset through the API origin, 256 KiB / 10 s bounds, zod validation, 30 s shared cache with conditional list requests, last valid catalog persisted and served as `stale`, `unavailable` with an empty list otherwise. No catalog release exists yet: the real repository reads as `unavailable` today (no release was created). - **Recognition** (`recognition.ts`): registry package (aliases resolved), plugin ID or exact `-<plugin>` rule (disabled plugins), `sources.git` prefix; a source designating two integrations or loaded under another plugin ID is non-conforming. - **State route** `GET /api/workspaces/:id/integrations?directory=` (`status.ts`, `integrations.ts`, `routes/integrations.ts`): absent / inactive / failed / unsupported / non-conforming / unknown / ok; fixed `status` method under the catalog's RPC ID only, 4 in flight, 8 s each; full output validation, marker mismatch and path-like `appVersion` refused; ownership rechecked with `assertCurrent` after the calls; catalog internals never returned. - **Plugin add** (AC-P7, D18): recognised `native` integrations are Global-only (`global-only`, 10 locales); registry and Git specs of one integration are duplicates. - **UI store** `stores/integrations-store.ts`: demand reads, 300 ms coalescing of `rpc.neuralnomads.<app>.*`, `plugin.updated`, `config.updated`, no polling. Mounted by C4. - Docs: `dev-docs/OPENCODE_INTEGRATIONS.md`, `PLUGIN_CONTROLS.md`; help `integrations.md` in English and the nine translations. ## Validation - `node --import tsx --test packages/server/src/opencode/integrations/catalog.test.ts` (11 pass, local release server) - `node --import tsx --test packages/server/src/opencode/integrations/recognition.test.ts` (8 pass) - `node --import tsx --test packages/server/src/server/routes/integrations.test.ts` (4 pass) - `node --import tsx --test packages/server/src/opencode/plugin-sources.test.ts` (11 pass) - `node --import tsx --test packages/ui/src/stores/integrations-store.test.ts` (4 pass) - `node --import tsx scripts/test-integrations-recognition-native.mjs <opencode 2.2.5 cli>`: PASS (added to the runtime matrix) - Server and UI typecheck; `npm run test:help` pass. File sizes: `http-server.ts` ~2500 lines (+7), `index.ts` ~790 (+2), `pr-build.yml` ~770. |
||
|
|
5313fbf5ad
|
feat(integrations): add the OpenCode integration contract and its test fixtures (#1238)
## Summary
Slices **F0** (integration contract) and **T0** (CodeNomad test
fixtures) of the approved integrations SCR (§4.2 basis; AC-F1, AC-F2,
AC-T1, AC-T2, AC-T3, AC-T5, AC-T6). No user-visible change: this is the
base the recognition, attachment and API 4 slices build on.
### F0: contract
- `packages/server/src/opencode/integrations/contract.ts`: zod schemas
of the marker, the reviewed catalog and every RPC definition of contract
1 (`integrationRpcSchemas`, keyed like `rpc.schema.json`:
`<method>.input|output`, `<event>.event`), plus the draft constants.
- Vendored JSON Schemas of
`NeuralNomadsAI/opencode-integrations@68eb8aa` (`marker`, `catalog`,
`rpc`, `host`; byte-identical to spec 0.1 at `dac13a74`) with
`VENDORED.json` (revision + Git blob IDs, checked by the test).
- **`readIntegrationRpcOutput(name, value)`**: the one way to read an
RPC output. Integrations register their definitions without `pattern`
(OpenCode 2.2.5 refuses it; spec/rpc-contract.md §4), so OpenCode passes
outputs the spec forbids; the full zod schema (patterns and bounds)
decides, and a non-conforming output is an integration error (`{ ok:
false, error: "non-conforming" }`) that never echoes the value. Tested
against every sample the pattern-free definitions accept but the spec
refuses.
- **Parity test** (`contract.test.ts`): a corpus of valid and invalid
samples (including the conformance package's cases) through zod **and**
Ajv 2020-12; fails on any disagreement and on any definition missing on
either side. Lengths count code points like JSON Schema (astral
characters agree). Four seeded mutations of `contract.ts` are each
caught.
- Outputs are reader schemas (unknown fields dropped); inputs strict;
`integrationCatalogSchema` adds the uniqueness JSON Schema cannot
express (`integrationCatalogDocumentSchema` stays exact).
- Types in the new `packages/server/src/integration-types.ts` (not
`api-types.ts`).
- `ajv2020` = `npm:ajv@^8.20.0` dev alias, so the hoisted Ajv 6 used by
existing mission tests keeps resolving (a plain `ajv@8` dev dependency
shadowed it and broke their types).
### T0: fixtures
- `scripts/fixtures/fake-npm-registry.mjs`: the loopback fake registry,
now shared by `test-plugin-sources-native.mjs` (scoped names, 404 for
unknown names, integrity/shasum).
- `scripts/fixtures/fake-integration/`: installable fake integration
package (`@neuralnomads/opencode-<id>`): marker,
`status`/`installHost`/`hosts`/`attachHost`/`detachHost`/`advertise`/`withdraw`
and payload-free events, offer registry on an **injectable clock** (15 s
lease, renewal without hello, replacement only after a new hello, 16
offers, `busy`/`gone`/`refused`), **marker-mismatch** mode,
`hostPart`/`installOutcome` options, a loopback **fake host with
sentinel secrets** (and a `leaky` mode),
`findSentinels`/`assertNoSentinels`.
- **Python discovery isolation**: `python-host.py` runs the vendored
reference client (`Service.discover`, `advertise_all`/`withdraw_all`)
under `python -I -B` and an audit hook; the native test asserts every
file and socket access stays inside the fixture and reaches only the
fixture daemon. `writeFixtureServiceRegistration` registers the fixture
daemon under the fixture's `XDG_STATE_HOME`; `withProductRuntime` now
passes its `env` to `run`.
- Docs: new `dev-docs/OPENCODE_INTEGRATIONS.md` (indexed in `INDEX.md`
and the `AGENTS.md` area list), native fixtures section in
`BROWSER_TESTING.md`, `PLUGIN_CONTROLS.md` reference.
## Finding to note
OpenCode 2.2.5 refuses `pattern` in RPC definitions: Effect's JSON
Schema import throws "Patterns may block validation and are disabled",
so a call whose declared output has a pattern fails with
`rpc.invalid_output`. The fake package registers its definitions without
`pattern` and validates its own input; CodeNomad revalidates outputs
with `contract.ts`. The Blender pilot hit the same refusal;
opencode-integrations `68eb8aa` now registers without patterns and says
readers validate outputs, which `readIntegrationRpcOutput` does.
## Out of scope
- AC-T4 (Blender 5.2 in the integrations CI, manual protocol) belongs to
`opencode-integrations`.
- AC-T3's §4.4 breaker clock: the breaker arrives with the API 4 slice;
the convention (every lease/expiry/rate window takes `now`) is
documented.
## Tests
- `node --import tsx --test
packages/server/src/opencode/integrations/contract.test.ts`: 24 pass
- `node --import tsx --test
scripts/fixtures/fake-integration/fake-integration.test.mjs`: 15 pass
- `node --import tsx scripts/test-integration-fixtures-native.mjs
<opencode 2.2.5>`: PASS (fake registry install, contract outputs, marker
mismatch, Python isolation + advertise/attach/detach/withdraw, no
sentinel in outputs or daemon log)
- `node --import tsx scripts/test-plugin-sources-native.mjs <opencode
2.2.5>`: PASS with the shared registry
- `npm run typecheck --workspace @neuralnomads/codenomad`: clean
- Full server suite: 2966 tests, 2953 pass, 2 fail under load
(`plugin-sources.test.ts`, `media-blob.test.ts`; both pass alone, as
does the `index.test.ts` load timeout)
- `node --test scripts/native-fixture-guards.test.mjs
"scripts/fixtures/wsl-*.test.mjs"`: 11 pass
- CI: unit fixtures in the server test job; the native test in the
runtime-contract job (minimum and latest).
|
||
|
|
66a85afa1c
|
feat(plugins): add, remove and show the version of any OpenCode plugin (#1232)
## Summary
Status → Plugins can now **add**, **remove** and **show the version** of
any OpenCode plugin (SCR §4.1, slices C1/U1/D1/Q1, AC-P1…AC-P9).
- **Add**: a `+` icon beside Refresh opens a small `.window-*` popover:
package spec (`--surface-field`), scope (Global default; Project shows
the warning), trust sentence. Registry and remote Git specs only.
- **Remove**: each row's menu gets **Remove from Global/Project** for
declarations in that scope's own write target. `id`/`-id` rules stay;
the package cache is never touched.
- **States**: a just-added source shows **Installing** until
`plugin.list` reports its exact target (then an ordinary active/failed
row). After `PLUGIN_SOURCE_PENDING_TIMEOUT_MS` (120 s) it shows **Not
confirmed** with Refresh.
- **Tooltips**: version, **Update available**, failure + native `ref`,
GUI notice when a `features.gui` package is declared outside Global.
## Implementation
- Server: new `opencode/plugin-sources.ts` + `routes/plugin-sources.ts`
(`POST`/`DELETE /api/workspaces/:id/plugin-sources`), built on
`PluginControls.editConfigDocument` (ownership, scope resolution,
deletion fence, connection fence, lock, atomic replace, JSONC/CRLF/BOM,
WSL). Pure helpers in `plugin-source-document.ts` (npm-package-arg
validation, package identity, verified removal). Types in
`plugin-source-types.ts`. `plugin-controls.ts` only gains `pluginId` on
sources and the `gui` passthrough.
- Errors carry a stable `code`: `invalid-spec`, `local-path`,
`plugin-id` (422), `duplicate`, `stale` (409).
- UI: `stores/plugin-sources.ts`, `components/plugin-source-add.tsx`,
`components/plugin-row-actions.tsx` (replaces
`plugin-package-action.tsx`, memoized item descriptors), i18n part file
in the 10 locales, help in 10 languages, `dev-docs/PLUGIN_CONTROLS.md`
rewritten.
## Deviations to note
- `disabled` state for unloaded sources: `plugin.list` omits disabled
plugins and exposes no package→ID link, so an unloaded configured source
is shown as **Not confirmed** ("may be disabled, or installation not
confirmed"). A disabled package with an exact `-id` rule therefore shows
its ID row (switches) and its unconfirmed source row (Remove items).
Documented in `PLUGIN_CONTROLS.md`.
- AC-P7 "a GUI package is added Global-only, Project refused":
`features.gui` is known only once OpenCode has loaded the package, so
pre-install refusal is not possible. This PR keeps Global as default,
warns for Project, and flags a Project-declared GUI package in its
tooltip afterwards. Native-family Global-only enforcement (D18) waits
for the integration catalogue (later slice).
## Tests
- `node --import tsx --test
packages/server/src/opencode/plugin-source-document.test.ts
packages/server/src/opencode/plugin-sources.test.ts` (13 pass)
- Full server suite: 2931 pass, 0 fail (one load-timeout in
`index.test.ts`, passes alone)
- `CODENOMAD_TEST_WSL_PLUGIN_CONTROLS=Ubuntu …
plugin-control-document-wsl.test.ts` (9 pass, incl. new add/remove)
- `node --import tsx scripts/test-plugin-sources-native.mjs <opencode
2.2.5>`: PASS (loopback fake registry, 404 → failed with `ref`); added
to CI runtime contracts
- UI store tests (45 pass), browser
`plugin-sources`/`plugin-controls`/`plugin-packages` (14 pass), `npm run
typecheck`, `npm run test:help` (31 pass)
|
||
|
|
7a1fc90310
|
docs(agents): keep AGENTS.md to universal rules and agent docs to the current state (#1082)
AGENTS.md loaded about 33,000 tokens into every agent session, subagent and reviewer, mostly per-feature contracts, and the agent documentation around it had accumulated duplicates, dated logs and history of removed designs. This PR makes AGENTS.md a short set of rules that apply everywhere plus an index of area documents, and leaves AGENTS.md, dev-docs and the repository skills describing only the current state. Three commits, reviewable separately: 1. **Move (lossless).** Every per-area rule moved verbatim from AGENTS.md into its area document (existing ones, or six new ones: UI_CONVENTIONS, UI_SHELL, COMPOSER, TRANSCRIPT, SETTINGS, PROJECTS). A script split the old file into 208 units, required one destination for each and checked all 199 content units verbatim at their destination. AGENTS.md went from 134,000 to 16,000 characters. 2. **Trim.** A per-area ponytail review removed rules stated two or three times (keeping the more precise copy), dated validation logs, per-version qualification sections below the current minimum, abandoned designs and self-references; seven documents whose surviving rules live elsewhere were deleted. It also fixed statements the code contradicts (locale registration, the Mission play/recovery condition, plugin package updates, cleanup progress UI, timeline index persistence, automation restart/targets, stale pins and minimums). 3. **Current state only.** The new AGENTS.md rule: no history (removed features and their names, former behavior, provenance, dated logs) and no denials; correct a contradicted statement at its source. DEVELOPER_MODE.md is merged into BROWSER_AUTOMATION.md and every Developer Mode mention is gone (including the README and the browser help page in ten locales). Names that only made sense against older designs are renamed with every reference updated, MISSIONS_ENVIRONMENT_ADMISSION is folded into SESSION_ENVIRONMENT, and INDEX lists every document. The server npm README now states the real OpenCode minimum (2.1.2). AGENTS.md is now about 14,000 characters (roughly 3,500 tokens); AGENTS.md, dev-docs and the vendor README went from about 11,200 lines to under 4,700. Validation: every relative Markdown link and every cited dev-docs path resolves; no reference to a renamed or deleted document remains; `npm run test:help` passes for all ten locales. Review: three independent GPT-6.1 Sol (high) reviews by area found binding rules lost in the trim and current-state passes; 25 were restored as present-tense facts or prohibitions and four contradicting statements corrected, each checked against code. A Fable 5.1 gatekeeper review of the whole PR passed; its follow-up routes session recovery from the index, points to dev-docs/INDEX.md and documents the family identity read. |