fix(plugins): correct install progress bounds, skill identity, and stage accuracy

Addresses review feedback on the marketplace installed-state change.

- Progress: byte-derived progress no longer has time-based drift layered on
  top, and fallback drift is clamped to the current stage range, so the bar
  cannot exceed the download band. 90/100 bytes at 40s elapsed used to report
  61% against a declared 5-45% band; it now reports 41%. Drift is measured
  from when the stage was entered rather than from task start.
- Skills: a marketplace skill is no longer reported installed from a bare
  skill name. The backend names skills from their own SKILL.md and records no
  publisher, so alice/review and bob/review install identically; matching the
  bare name marked every publisher's skill as installed. Skill cards now
  resolve to not-installed until the installed skill carries a publisher.
- Stages: "installing plugin dependencies" and "launching plugin" described
  work this task context cannot observe (installation persistence ran under
  the former; the runtime installs dependencies and starts the plugin inside
  apply_plugin_installation under the latter). They become "persisting the
  installation" and "installing or starting plugin", and the frontend maps
  that combined step to the dependency stage rather than the launch stage.
- Removed the per-dependency progress fields: the backend never populated
  them, so the UI could never have displayed them.

The stage mapping and progress maths move to install-progress.ts, and the
installed-state matching to a React-free marketplace-installed.ts, so both
are covered by executable tests (+9).

Verified: ruff, tsc --noEmit, prettier --check, eslint (0 errors), 98/98 unit
tests.
This commit is contained in:
TyperBody
2026-09-21 00:40:23 +08:00
parent f998e475e3
commit 163dac48b0
11 changed files with 494 additions and 249 deletions
+7 -2
View File
@@ -1770,7 +1770,7 @@ class PluginRuntimeConnector(ManagedRuntimeConnector):
artifact_digest = hashlib.sha256(file_bytes).hexdigest()
await self._store_artifact_package(execution_context, artifact_digest, file_bytes)
if task_context is not None:
task_context.set_current_action('installing plugin dependencies')
task_context.set_current_action('persisting the installation')
try:
binding, previous_digest, previous_was_durable = await self._persist_installation_package(
execution_context,
@@ -1789,7 +1789,12 @@ class PluginRuntimeConnector(ManagedRuntimeConnector):
plugin_name=plugin_name,
)
if task_context is not None:
task_context.set_current_action('launching plugin')
# The runtime installs the plugin's dependencies and starts it
# inside apply_plugin_installation. It does not stream
# per-dependency progress back to this task context, so this stage
# deliberately stays coarse instead of claiming a separate,
# unobservable "installing dependencies" step.
task_context.set_current_action('installing or starting plugin')
await self._apply_desired_state(
PluginInstallationDesiredState(binding=binding, enabled=True),
artifact_package=file_bytes,