Skip to content

Use core v0.59.0: plugin files hold their configuration, one editor - #272

Merged
fylorn merged 9 commits into
devfrom
plugins-editor-int
Oct 3, 2026
Merged

fylorn merged 9 commits into
devfrom
plugins-editor-int

Conversation

@fylorn

@fylorn fylorn commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

Moves ThinkWatch Lite to core v0.59.0 (ef10a8b, protocol 35) and lands the plugin editor built against it.

What users get

Plugins: the file is the configuration

  • A plugin's settings, scope (with * patterns) and behavior on errors live in its own JS file. The app writes them into the manifest and leaves the rest of the file byte for byte; only Enabled stays an app switch.
  • One editor with a Settings tab and a Code tab and a single Save. The code is edited in place, and changes on either tab follow the other. Import from file… sits at the top right of the Code tab.
  • Add plugin opens the same editor on the Code tab with a small working template, a Plugin ID field (suggested from the file name or plugin name, checked for format, reserved words and duplicates) and an Install button.
  • Reorder by dragging, or with the arrows.
  • Fewer confirmations: only a plugin that can change tool calls asks in a system dialog, when it is installed, turned on, has its code changed, or has a change to its file approved. Editing config.yaml in the app or rolling back cannot do those: the config editor and the version history now show core's refusal inline, with a button that opens the Plugins page.
  • Default plugins are named in the UI language in core's messages, system dialogs and notifications.

Commits

  • plugins: the JS file is the source of truth, one editor, fewer confirmations
  • plugins: drag to reorder
  • plugins: build against core's final plugin contract (protocol 35)
  • config: keep core's plugin confirmation refusal in the editor and history
  • plugins: name default plugins in the UI language in core's messages
  • plugins: add a plugin in the same editor as editing
  • Use core v0.59.0: every core crate pinned to v0.59.0, Cargo.lock re-locked with cargo metadata only, so just the seven core crates move to ef10a8b. src/generated/tw-api.ts already matched the tag.
  • Merge of dev at b0ee70c (README badge).
  • shots: oracle answers from core v0.59.0

Upgrade notes for the next release

  • A remote core must be 0.59.0 (protocol 35). 2026.10.0 bundled 0.57.1 (protocol 31); no Lite release bundled 0.58.0.
  • Core rebuilds the request store (schema 25): the request history is cleared on first start.
  • security.hidden_text and security.output_limit are removed. A config.yaml that still has either key does not load, and the app starts in safe mode. Lite 2026.10.0 writes them only when one of these settings was changed from its factory value.

Checks

  • pnpm typecheck, pnpm test (744 tests), pnpm build
  • cargo fmt --check, cargo clippy --all-targets --locked -- -D warnings
  • cargo test --locked against the published twcore v0.59.0 from fetch-core.sh: 367 app tests, control_plane (9), msg_codes (5), ts_bindings (2), and the tw-adopt and tw-scan suites. UPDATE_TS=1 leaves src/generated/tw-api.ts unchanged.
  • scripts/shots/core/oracle.sh at v0.59.0: only api_version and version in the status answers change.
  • scripts/version.sh, scripts/release_notes_test.py
  • Upgrade check: a config written by twcore init 0.57.1 and a copy of a real 2026.10.0 config both pass twcore check 0.59.0; a config with security.hidden_text fails with the schema error, as the upgrade note says.

🤖 Generated with Claude Code

fylorn and others added 9 commits October 3, 2026 14:12
…mations

Plugins contract addendum 4 (core v0.59.0, protocol 35). On error, scope and
setting values live in the plugin's manifest; config keeps only the file,
its hash and the switch. Built against provisional types until core ships.

Editor
- Clicking a plugin opens one editor with two tabs and a single Save:
  - Settings: the enable switch (an app toggle, not in the code), on error
    (Segmented), scope and the plugin's settings form.
  - Code: the current code in an editable CodeMirror, with "Import from
    file…" at the top right.
- Form edits go through PluginRewrite into the code. Code edits go through
  PluginInspect (debounced) into the form. Syntax and manifest errors show
  inline under the line, with line and column, and lock the form until the
  code loads again. Late answers never overwrite newer edits; switching tabs
  and saving flush both directions first.
- Unsaved changes survive tab switches (both panes stay mounted, so the
  code keeps its undo history). Closing (Esc, ×, Cancel, outside click) or
  leaving for Review changes with unsaved changes asks once, in the app.
- Scope inputs take `*` patterns and suggest known clients, models and
  upstreams; a typed pattern lists the known names it matches.
- The header shows the SHA-256 of the code being edited (the native dialog
  shows the same) and marks permissions the edited code adds.
- The separate "Replace code" dialog and Settings dialog are gone. A plugin
  that fails to load, and a changed file that cannot load, offer "Edit
  code" instead (the latter edits the file on disk).

Confirmations (§3)
- A native dialog appears only when a plugin holding reply_tool_calls (in
  the old or new code) is installed, turned on, has its code changed or has
  an on-disk change approved. Data-only saves, other plugins, turning off,
  reordering never ask; Delete keeps its in-app confirmation.
- The webview decides from the permissions it has: certain cases go
  straight to the native command, the rest call the plain endpoint
  (CreatePlugin, SavePlugin, ApprovePluginFile) and fall back to the native
  command on 403 control.plugin.needs_confirmation.
- Install: the in-app review shows the code, permissions, the scope and on
  error written in the code, and the ID; one click on Install (plus the
  native dialog for a tool-call plugin). No second review.
- The row switch sends the approved code with the new state (SavePlugin).

Rust
- plugin_install_confirmed, plugin_save_confirmed and
  plugin_approve_confirmed replace plugin_install, plugin_replace_source,
  plugin_approve and plugin_update_confirmed. Each re-inspects through core,
  shows one native dialog and calls the matching …Confirmed endpoint. The
  save dialog lists what changes: turning on, code (old → new SHA-256), or
  each data change when a rewrite of the approved code reproduces the save.
- CreatePlugin, SavePlugin, ApprovePluginFile and PluginRewrite join both
  webview allow-lists; the three …Confirmed endpoints stay out (tested).

Provisional
- src/plugins/api.provisional.ts and src-tauri/src/plugins/wire.rs hold
  the addendum-4 types and endpoints, with the swap steps for integration.
  The control-plane test for tool-call plugins is ignored until v0.59.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The reorder dialog takes drag and drop as well as the up and down buttons.
A row is dragged by its grip and name (the buttons stay outside the drag
area), the dragged row dims and an insertion line shows where it lands. It
reuses the pointer-event hook of the route and group dialogs
(routing/useReorder), so there is no new dependency and Tauri's file-drop
handling does not interfere.

The buttons stay for the keyboard. When a move takes a plugin to the top or
the bottom, the button that just became disabled hands focus to the other
one, so the keyboard can keep going. Saving still writes one config
version (ReorderPlugins).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Swap the provisional addendum-4 types for the ones core main generates
(550e6fd, future v0.59.0, CONTROL_API_VERSION 35).

- src/generated/tw-api.ts is regenerated. src/plugins/api.provisional.ts
  and src-tauri/src/plugins/wire.rs are gone: the webview endpoint list
  and the three native confirmations use tw_api::ep and tw_api's
  PluginView, PluginInspection, ManifestView, SettingSpecView,
  PluginCreate, PluginSave and PluginRewriteRequest directly.
- Differences from the provisional guesses: PluginRewrite answers
  PluginSource (there is no PluginRewritten), its request type is
  PluginRewriteRequest, and its settings carry only the keys to change.
  The editor and the native save dialog keep sending every declared
  setting, which core accepts.
- core.zh.json: gw.plugin.manifest_not_data and
  gw.plugin.manifest_not_data_at added, config.plugin.blank_pattern and
  config.plugin.setting_type removed, control.plugin.needs_confirmation
  rewritten for core's new sentence (install, turn on, change the code or
  approve a change to the file).
- The control-plane test for tool-call plugins runs again.

The tw-* pin stays at v0.58.0 until core tags v0.59.0, so until then this
branch builds only with a local [patch] of the core crates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tory

Core now refuses PUT /config, PATCH /config and a rollback with 403
control.plugin.needs_confirmation when the write would turn on, add or
re-approve a plugin that can change the tool calls in replies, and those
paths have no confirmed variant. A toast disappeared after a few seconds
and did not say where the change can be made.

The config file editor and the version history now show core's sentence
in an inline notice ("This change has to be confirmed on the Plugins
page", plus whether the file was saved or the version restored) with a
button that opens the Plugins page. Other failures still use the toast.
PatchConfig is only used for retention, failover and client probes, so it
cannot hit this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Core puts a plugin's manifest name into its messages ({plugin} in
gw.plugin.* and control.plugin.needs_confirmation, plugin_name in
plugin_failed). For the default plugins that name is English, so the
Chinese UI said "Convert WSL and Windows paths" where the Plugins page
says 「WSL 路径转换」.

One helper on each side now names a plugin everywhere: pluginName (TS)
and plugins::defaults::name_in (Rust), both reading
src/i18n/plugin-defaults.json. With an id, a plugin is matched by id and
manifest name as before; with only a name, as in a message's {plugin},
by the exact English manifest name. Descriptions and setting labels
still need the id.

- zhOf / core_text::zh render {plugin} through it, so toasts, notices,
  inline errors (such as the config file's 403), the request drawer's
  plugin runs, the stats tooltip and notifications all say the same
  name. Callers that know the plugin pass its id (coreText, errorText).
- The history search sends the codes with {plugin} when the query
  matches a localized default plugin name, since the stored sentences
  are English.
- Shared cases in core.zh.cases.json keep both implementations in step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Add plugin dialog still had the old flow: a Choose a file / Paste
code switch, a drop zone, Next and a separate review step. Adding now
opens the plugin editor in a "new plugin" mode, so it works exactly like
editing:

- It opens on the Code tab with a short starter template that loads: a
  pure-data manifest written the way core rewrites it (the system
  permission, one note setting, on_error) and an onRequest hook that
  does nothing until the note is filled in. zh and en templates differ
  only in their text.
- The code is edited in place, can be pasted, and "Import from file…"
  stays at the top right. The header (name, ID, SHA-256, permission
  chips) and the Settings tab follow the code through PluginInspect, with
  inline errors, as when editing.
- The Settings tab adds a Plugin ID field. It follows the imported file
  name or the code's name until edited, and is checked for format,
  core's reserved words and existing IDs; suggestId now avoids reserved
  words the way core does. The enable switch starts off.
- One primary button, Install. A plugin whose manifest holds
  reply_tool_calls goes straight to plugin_install_confirmed (one native
  confirmation, CreatePluginConfirmed); any other installs with
  CreatePlugin. There is no review step.
- Closing with changes asks once, saying the plugin will not be
  installed.

SourceDialog, its strings, the drop zone, the segmented switch and the
review step are gone. Editor.test.tsx covers the new mode and the
template; model tests cover the ID checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@fylorn
fylorn merged commit 08dd771 into dev Oct 3, 2026
4 checks passed
@fylorn
fylorn deleted the plugins-editor-int branch October 3, 2026 10:42
@fylorn fylorn mentioned this pull request Oct 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant