Skip to content
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
b83af63
feat(runtime): add extension lifecycle kernel
xxhZs Aug 13, 2026
b53bfd2
test(runtime): exercise extension lifecycle system flows
xxhZs Aug 13, 2026
05fafe2
feat(runtime): add extension tool contributions
xxhZs Aug 14, 2026
ee5897c
feat(runtime-host): wire extension tool runtime
xxhZs Aug 14, 2026
08cfa84
feat(runtime-host): add trusted extension control plane
xxhZs Aug 14, 2026
ca96ed2
test(runtime-host): exercise trusted extension end to end
xxhZs Aug 14, 2026
ea89b10
feat(runtime-host): add installable tool packages
xxhZs Aug 14, 2026
004fca5
fix(runtime-host): harden tool package model integration
xxhZs Aug 14, 2026
40e4890
feat(runtime-host): let subagents author tool candidates
xxhZs Aug 14, 2026
82fa50b
fix(runtime-host): harden agent-authored tool retries
xxhZs Aug 14, 2026
05b6da9
fix(runtime-host): align extension identity validation
xxhZs Aug 14, 2026
2d32d6d
feat(extension): add lifecycle-managed UI contributions
xxhZs Aug 14, 2026
6ccbd09
feat(extension): close UI import and host bridge loop
xxhZs Aug 14, 2026
c4f99d7
fix(extension): make imported UI executable
xxhZs Aug 14, 2026
7eb0ae9
feat(extension): publish tool results to ui state
xxhZs Aug 14, 2026
522db5f
feat(extension): add native UI sidecar panels
xxhZs Aug 14, 2026
a3f8706
feat(extensions): compose UI and tools as one revision
xxhZs Aug 14, 2026
bc504c0
fix(extensions): exclude git metadata from revisions
xxhZs Aug 14, 2026
c77cec6
feat(extensions): replace the complete desktop root
xxhZs Aug 14, 2026
d80ccd2
feat(graph): add bounded UI author routing
xxhZs Aug 15, 2026
8a06021
feat(extensions): add composable UI slots
xxhZs Aug 15, 2026
f007f70
feat(extensions): complete unified plugin platform
xxhZs Aug 15, 2026
3998a28
feat(runtime): add trusted pre-tool-use hooks
xxhZs Aug 12, 2026
73e60bb
test(runtime): cover configured hook denial end to end
xxhZs Aug 12, 2026
4906c30
fix(runtime): enforce hook lifecycle bounds
xxhZs Aug 13, 2026
63889e3
feat(extensions): add typed runtime hook contributions
xxhZs Aug 15, 2026
589c064
feat(extensions): close unified package authoring loop
xxhZs Aug 16, 2026
23dd1cc
fix(extensions): isolate UI package previews
xxhZs Aug 16, 2026
9d31f8c
fix(extensions): preserve hook lifecycle across updates
xxhZs Aug 16, 2026
11a3af9
feat(extensions): add generic event listener contributions
xxhZs Aug 17, 2026
628f5ba
feat(extensions): add composable serverless runtime
xxhZs Aug 17, 2026
09b3710
fix(extensions): harden dispatch and timer execution
xxhZs Aug 17, 2026
2384d42
refactor(extensions): run trusted plugins in process
xxhZs Aug 17, 2026
7b16610
refactor(extensions): adopt hierarchical runtime contexts
xxhZs Aug 18, 2026
adf3b9d
fix(extensions): close lifecycle consistency gaps
xxhZs Aug 18, 2026
a8cc6f6
refactor(extensions): unify plugin composition runtime
xxhZs Aug 18, 2026
2b31619
refactor(extensions): internalize plugin runtime kernel
xxhZs Aug 18, 2026
a69078f
refactor extension composition through entry tree operations
xxhZs Aug 18, 2026
538634f
route extension mutations through composition apply
xxhZs Aug 18, 2026
80293cf
remove last-good extension revision state
xxhZs Aug 18, 2026
f08365b
align event management with entry tree semantics
xxhZs Aug 18, 2026
675d7b9
refactor(extensions): fold configuration into entry state
xxhZs Aug 18, 2026
fa73009
refactor(extensions): project catalog reads from composition
xxhZs Aug 18, 2026
6ca8938
refactor(extensions): converge composition protocol and runtime consu…
xxhZs Aug 18, 2026
27dd2d4
docs(extensions): define canonical package control surface
xxhZs Aug 18, 2026
c1baee0
refactor(runtime): route extension projections through services
xxhZs Aug 18, 2026
23bd59e
refactor(extensions): remove legacy contribution management
xxhZs Aug 18, 2026
67cb43a
refactor(extensions): complete entry and fiber tree architecture
xxhZs Aug 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/desktop/src/renderer/locales/settings-subagents-copy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ const SETTINGS_SUBAGENTS_COPY_BY_LOCALE = {
local_read: { label: '代码阅读', description: '只读访问当前工作区,适合搜索、理解和总结代码。' },
web_research: { label: '网络研究', description: '只使用联网搜索,适合查找外部资料和最新信息。' },
implementation: { label: '实现代码', description: '可以读写文件并执行命令,在隔离 worktree 中完成改动。' },
tool_author: { label: '工具作者', description: '通过正式接口创建、安装并在沙箱中测试候选 Tool,不修改工作区源码。' },
},
thinking: {
off: '关闭',
Expand Down Expand Up @@ -231,6 +232,7 @@ const SETTINGS_SUBAGENTS_COPY_BY_LOCALE = {
local_read: { label: 'Code reading', description: 'Read-only access to the current workspace for search, understanding, and summaries.' },
web_research: { label: 'Web research', description: 'Web search only, for external sources and current information.' },
implementation: { label: 'Implementation', description: 'Read and write files and run commands in an isolated worktree.' },
tool_author: { label: 'Tool author', description: 'Create, install, and sandbox-test Tool candidates through the formal authoring interface without editing workspace source.' },
},
thinking: {
off: 'Off',
Expand Down
68 changes: 68 additions & 0 deletions docs/architecture/extension-lifecycle-kernel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Extension Lifecycle Kernel (Phase 1)

This module implements the product-independent lifecycle and revision semantics from issue #2973. It deliberately does not register Tools, UI, hooks, or execute dynamic scripts.

## Authority and objects

- An **Extension** is a stable identity.
- A **Revision** is immutable after `install`. Installing a revision never executes extension code.
- A **Binding** selects one exact revision for one scope. Only one binding for an extension may exist in a scope.
- A **Candidate** owns everything allocated by `prepare`, `healthCheck`, and `activate` until it either becomes current or is rolled back.
- A **Current Activation** owns its exposed dependency value and every registered effect.
- A **Composition Snapshot** is an immutable, deterministic view of the active revisions and contribution descriptors in a scope.

## Lifecycle

```text
install revision (no effects)
-> enable binding
-> wait for same-scope dependencies
-> prepare candidate
-> health check
-> activate
-> commit current
-> stop / remove binding / uninstall revision
```

Updates use current/candidate semantics:

```text
current remains active
-> prepare + health-check candidate
-> stop active dependents
-> activate candidate
-> commit candidate as current
-> dispose old current
-> reactivate dependents against the new dependency value
```

If preparation, health checking, or activation fails, the candidate is disposed in reverse effect-registration order and current remains committed. If candidate activation had already stopped dependents, they are reconciled back against the old current.

## Invariants

- All mutations are serialized by the kernel.
- Dependencies resolve only inside the binding's scope.
- Missing dependencies produce `waiting`; activating the provider reconciles waiting consumers.
- Stopping a provider stops transitive dependents before the provider.
- Dependency cycles fail without executing extension code.
- Effects are disposed in reverse registration order; failed disposers remain inspectable and retryable.
- A revision cannot be uninstalled while any binding, current activation, or candidate references it.
- Previously returned composition snapshots never mutate.

## Adapter boundary

Future contribution adapters register their reversible work through `ExtensionActivationContext.ownEffect`. A Tool adapter, for example, will own the Tool registry entry's disposer. The kernel does not bypass Maka's existing Runtime, permission, sandbox, or Run-composition authorities; those integrations belong to later phases.

## System verification

`extension-lifecycle-kernel.system.test.ts` treats the exported kernel as the test boundary. It does not replace lifecycle methods or assert mock call counts. The scenarios exercise:

- a real TCP server and persistent client through health check, dependency injection, provider stop, automatic consumer restart, scope disposal, and port release;
- real `EventEmitter` listeners and timers to detect resource leaks across stop, restart, and binding removal;
- a diamond dependency graph moving from one provider revision to another, including transitive stop and reactivation order;
- candidate, dependent, binding-removal, and scope-disposal cleanup failures with retained ownership and retry;
- invalid definitions, candidates, effect registration, dependency reads, binding conflicts, and missing objects through public error codes;
- deterministic revision/composition ordering and stable composition digests across generation changes;
- 2,000 seeded public lifecycle operations across four scopes, three dependent extensions, and two revisions while continuously checking public-state/composition agreement and exact live-resource counts.

The focused fault-matrix tests remain alongside these system scenarios. Coverage is collected from the compiled JavaScript with Node's test runner so the result measures the implementation that actually executes.
82 changes: 82 additions & 0 deletions docs/architecture/extension-tool-contributions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Extension Tool Contributions (Phase 2)

This vertical slice connects the Extension lifecycle authority to Maka's real Tool execution
path. It supports trusted static Tool revisions; it does not load arbitrary packages or scripts.

## Completion contract

Phase 2 is complete when all of these statements are true:

- installing a revision remains effect-free;
- activating a binding publishes its declared Tools into that binding's scope;
- the next Backend `send()` sees Core and active Extension Tools through one catalog;
- an Extension Tool call settles through the existing `ToolRuntime` rather than a parallel
executor;
- Tool availability, argument validation, permissions, sandbox boundaries, durable settlement,
and product routing remain authoritative;
- updating a binding switches the complete Tool surface transactionally, and a failed candidate
restores the prior surface;
- stopping, removing, or disposing the binding retracts every Tool registration;
- Extension Tools cannot shadow Core Tools, Runtime protocol names, other extensions, or claim a
provider-native protocol;
- an empty Extension registry produces the same Core Tool surface as before.

## Registration model

`ExtensionToolContributionRegistry` is a typed resource registry. A trusted Tool revision publishes
through `contributeExtensionTool`, which immediately hands the matching unregister function to
`ExtensionActivationContext.ownEffect`.

Candidate replacement is transactional. A candidate from the same binding may temporarily replace
its current Tool name while activating. Candidate cleanup restores the old entry; after commit, old
activation cleanup retires the replaced entry so a later stop cannot resurrect it.

Core Tool names should be supplied through `protectedToolNames` so conflicts fail during activation.
`compose` checks again when producing the execution snapshot, which catches changes in the Core
catalog between activation and admission.

## Runtime boundary

`AiSdkBackendInput.resolveTools` is an optional trusted reader for the full Core + Extension Tool
catalog. The Backend snapshots it once at the beginning of each `send()` and then routes that
snapshot through the same apply-patch projection, `ToolAvailabilityRuntime`, provider schema,
repair path, and `ToolRuntime` dispatch used by Core Tools.

This phase intentionally chooses the existing `send()`/Turn boundary. Refreshing at every physical
model request, pinning a larger Run composition, draining in-flight calls, and isolated
agent-authored code remain later decisions.

## Runtime Host ownership

`HostExtensionRuntime` is the in-process authority owned by the execution Runtime Host. It owns the
lifecycle kernel and Tool registry together and composes Session-scoped Extension Tools into both
the model Backend and the Host's available-Tool catalog.

The exact Tool snapshot selected at the beginning of `send()` is also written into the durable Run
Composition record. During Host drain, new Extension mutations are rejected while read-only Tool
resolution remains available to admitted work. The Extension authority closes after execution
domains, disposes every tracked Scope, and only then uninstalls its in-memory revisions.

## Trusted control plane and restart recovery

The Runtime Host composition may register a bounded catalog of trusted static Tool revisions.
`StaticTrustedToolExtensionLoader` resolves only those definitions; it never imports a workspace
path or executes user-authored code. The local owner can use `extension.catalog.query` and
`extension.catalog.mutate` to list, enable, disable, update, and remove bindings. These operations
are deliberately absent from the remote-owner grant list.

`HostExtensionController` persists desired bindings, enabled state, last-good revision, and the
latest diagnostic in the root-private Host control directory. Desired state is committed before
enable, update, and disable convergence so a process crash can resume the command. At startup it:

1. loads every available last-good and desired revision without activating code during install;
2. restores last-good bindings first;
3. attempts the desired upgrade through the lifecycle candidate transaction;
4. retains last-good Tools and records a diagnostic when loading, health check, or activation
fails;
5. leaves the rest of Runtime Host available when Extension state cannot be recovered.

This completes the minimum trusted-static product slice: a Host integrator registers Tool
revisions, a local administrator controls them through the Runtime Host protocol, and enabled
bindings survive process restart. Manifest/npm discovery, third-party code isolation, UI, an Agent
authoring Tools, and model-call-level hot swapping remain outside this trust boundary.
7 changes: 6 additions & 1 deletion packages/core/src/subagent-settings.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { isThinkingLevel, type ThinkingLevel } from './model-thinking.js';

export const SUBAGENT_PROFILES = ['local_read', 'web_research', 'implementation'] as const;
export const SUBAGENT_PROFILES = [
'local_read',
'web_research',
'implementation',
'tool_author',
] as const;
export type SubagentProfile = (typeof SUBAGENT_PROFILES)[number];

export const MAX_SUBAGENT_PRESETS = 64;
Expand Down
Loading
Loading