56 Commits
Author SHA1 Message Date
esengine be5c98c744 refactor(agent): remove missing-tool-call-reasoning detection and recovery
The loop no longer classifies a tool-call turn that arrived without
reasoning, retries it, or keeps a file-backed cooldown for it. Removes the
watch and the cooldown state, the deferred stream sink they needed, the
provider policy interfaces and their implementations, the descriptor field on
the extension wire (schema and SDK regenerated), the state-dir path, the
run-metrics counters and the protocol-recovery kinds.

The protocol-recovery audit channel itself stays for now.
2026-11-02 03:26:03 -08:00
esengine 336cbc3c22 chore(extension): regenerate the protocol artifacts for the prompt refusal identity 2026-10-18 04:01:28 -07:00
esengine f28360eadc feat(extension): a mod reads the session and queues a prompt, and settings bound what it may add 2026-10-18 04:01:28 -07:00
esengine f26e1c1359 feat(extension): a block at tool.before may carry an answer that stands in for the tool's output 2026-10-18 04:01:28 -07:00
esengine ec49147dee feat(permission): a tighten-only extension's ask is a fresh human approval of the one call 2026-10-18 00:35:39 -07:00
esengine 3d624951db feat(ext): dynamic tools and kill-on-timeout for single-threaded runtimes; the mod host serves tools, commands and the band 2026-10-18 00:35:27 -07:00
xiexian 3a47847772 docs(sdk): verify starter extension with the offline provider 2026-10-06 10:39:44 +08:00
YHH 42455e61fb Merge pull request #11844 from KHG420/codex/studio-sdk-tool-call-identifiers
fix(sdk): validate provider tool call identifiers
2026-10-04 13:12:36 -07:00
YHH 255001cead Merge pull request #11843 from KHG420/codex/studio-sdk-usage-counts
fix(sdk): reject negative provider token usage
2026-10-04 12:30:56 -07:00
xiexian 9d5f2a7dd9 fix(sdk): validate provider tool call identifiers 2026-10-05 02:28:31 +08:00
xiexian 63754d56d2 fix(sdk): reject negative provider token usage 2026-10-05 02:25:05 +08:00
xiexian 0e06829846 fix(sdk): reject unregistered provider chunk types 2026-10-05 02:22:10 +08:00
YHH 03147a71cd Merge pull request #11839 from KHG420/codex/studio-sdk-echo-provider
feat(sdk): add an installable offline provider example
2026-10-04 11:01:31 -07:00
yhh fdba7c3210 fix(sdk): drop the import the merged intercept-budget change left unused 2026-10-04 10:36:19 -07:00
YHH 254140a866 Merge pull request #11795 from KHG420/codex/studio-sdk-provider-start-cancel
fix(sdk): cancel provider streams while opening
2026-10-04 10:20:50 -07:00
YHH 66bfceb85b Merge pull request #11789 from KHG420/codex/studio-sdk-externalized-intercept-budget
fix(sdk): include externalized reads in the intercept budget
2026-10-04 10:10:08 -07:00
xiexian 39fe0acdc5 fix(sdk): cancel provider streams while opening 2026-10-05 01:04:37 +08:00
YHH 0a66bd417d Merge pull request #11791 from KHG420/codex/studio-sdk-stream-context-lifetime
fix(sdk): cancel provider contexts when streams finish
2026-10-04 09:25:48 -07:00
xiexian 024b50c173 docs(sdk): explain local tests and custom host shutdown 2026-10-04 01:18:05 +08:00
YHH 70b380471e Merge pull request #11556 from KHG420/codex/studio-fullsidecar-demo-guide
docs(sdk): align fullsidecar demo guidance
2026-10-03 07:16:37 -07:00
xiexian df5b824df9 fix(sdk): require explicit UI result acknowledgements 2026-10-03 21:05:36 +08:00
YHH b7682ae672 Merge pull request #11835 from KHG420/codex/studio-sdk-response-format
fix(sdk): validate provider response format types
2026-10-03 04:36:21 -07:00
YHH fca92a5675 Merge pull request #11826 from KHG420/codex/studio-fullsidecar-null-arguments
fix(sdk): handle null arguments in fullsidecar demo
2026-10-03 04:24:13 -07:00
YHH 4fdaa75397 Merge pull request #11828 from KHG420/codex/studio-sdk-tool-argument-presence
fix(sdk): require tool argument presence
2026-10-03 04:15:13 -07:00
YHH 474ba205d1 Merge pull request #11802 from KHG420/codex/studio-sdk-cancelled-host-calls
fix(sdk): stop already-cancelled host requests before writing
2026-10-03 04:12:43 -07:00
YHH 5717f930f0 Merge pull request #11798 from KHG420/codex/studio-sdk-shutdown-callback-recovery
fix(sdk): recover shutdown callback panics
2026-10-03 04:09:32 -07:00
xiexian 2e9045a300 fix(sdk): include externalized reads in the intercept budget 2026-10-03 18:20:33 +08:00
xiexian cf490285b0 fix(sdk): cancel provider contexts when streams finish 2026-10-03 18:02:43 +08:00
xiexian 54c8e7ab68 feat(sdk): add installable offline provider example 2026-10-03 15:16:42 +08:00
xiexian 74b6705484 fix(sdk): validate provider response format types 2026-10-03 14:37:10 +08:00
xiexian e741ffe34b fix(sdk): require tool argument presence 2026-10-03 13:38:53 +08:00
xiexian 3b4a2a7619 fix(sdk): handle null arguments in fullsidecar demo 2026-10-03 13:27:39 +08:00
xiexian cf3460a48f fix(sdk): stop already-cancelled host requests before writing 2026-10-03 08:47:02 +08:00
xiexian 92c6944d9b fix(sdk): recover shutdown callback panics 2026-10-03 08:07:59 +08:00
xiexian 431dd159a8 fix(sdk): synchronize notification queue shutdown 2026-10-03 07:05:17 +08:00
YHH 4f8f8a5bd5 Merge pull request #11553 from KHG420/codex/studio-sdk-tool-example
docs(sdk): add an installable tool extension example
2026-10-02 13:25:12 -07:00
YHH 323b1857f9 Merge pull request #11503 from KHG420/codex/studio-starter-sidecar-lifecycle
fix(sdk): make starter interceptor work on composed inputs
2026-10-02 13:21:33 -07:00
xiexian 9aa53444c7 fix(sdk): declare only callable tool handlers 2026-10-02 23:45:31 +08:00
xiexian 552209fa46 docs(sdk): add an installable tool extension example 2026-10-01 16:16:56 +08:00
xiexian 8fdfb89127 docs(sdk): align fullsidecar demo guidance 2026-10-01 16:11:25 +08:00
xiexian b5f2f0c025 test(extensions): verify installed fullsidecar host effects 2026-10-01 15:14:53 +08:00
xiexian 86f5ea0c5a fix(sdk): make starter interceptor work on composed inputs 2026-10-01 11:36:41 +08:00
yhh 394da0a313 refactor: the kernel's packages live in the layer they belong to
internal/ was 141 packages side by side, so nothing but a reader's memory
said which were foundations and which assembled the rest. Each now sits in a
layer directory, and the directory is the declaration:

  base      L0  foundation with no domain in it
  contract  L1  types and interfaces the layers above agree on
  model, tools, safety, ext, state, platform
            L2  subsystems, peers of one another
  runtime   L3  the turn engine
  session   L4  the controller
  assembly  L5  the composition root
  frontend  L6  cli, serve, acp and the host adapters

Only paths change: package names, code and behaviour do not. Imports,
repolint and coverage baselines, sensitive paths, CI package lists and docs
follow the new paths; release notes keep the paths they were written with,
and sample paths inside tests and fixtures keep theirs.

repolint gains layer-order: a package may import its own layer and those
below it, read from where it lives. The 47 imports that point upward today
are recorded as that rule's opening debt (-allow-widen, a new rule's first
baseline) and can only go down. Most of it is config, which still reaches
into eight subsystems and is the next thing to take apart; the rest is
event, history and a few tools reaching into runtime for types that belong
in contract.
2026-09-23 18:28:21 -07:00
yhh d318d24050 feat(extension): a plugin runtime can give the agent new tools
A runtime could only replace a tool by name, and that slot was never routed
to it, so a plugin that wanted to add one had to ship an MCP server. A
runtime now declares tools in its manifest, under runtime.tools with the
tools capability: a name, a description and an object input schema, known
before the process starts and shown in the install plan and the package
list. At initialize the runtime names the declared tools it serves; one the
manifest does not describe fails the handshake as capability_not_declared.

The model sees each as ext__<plugin>__<tool> and reaches it through the
capability catalog the way it reaches MCP tools, so installing or reloading
a plugin never moves the cached prompt prefix, and every call passes the
permission check under that name. A call is the new host request
extension/tool/call, bounded at five minutes; isError carries a failure the
tool reports about its own work, apart from a call that could not run.
A runtime that failed to start contributes no tools.

The Go SDK serves them from Options.Tools and names them in the handshake.
An effect test drives a call through the real assembly to a fake runtime and
back to the model, and fails with the registration removed.
2026-09-23 17:43:26 -07:00
esengine 68b031ae5d docs: delete stale documents, untranslated copies and main-v2 release remnants
Deletes what the documentation standard (6b6a41d36) classes as dead on
the studio line, as reviewed and approved item by item.

Deleted:
- Stale or unreferenced: production_checklist (a "v5.9.9 stable" that
  never existed), the 4-line WINDOWS_LTSC_2019 stub,
  SESSION_REFERENCE_ARCHITECTURE, docs/superpowers (the retired
  autoresearch plans), docs/research, RELEASING.md (main-v2's three-tag
  line), SIGNPATH_WINDOWS_ADMIN_SOP.md (desktop line only) and
  CHANGELOG.md (main-v2's Go line).
- Chinese copies: 30 zh-CN documents with an English original, plus
  three with none (COLLABORATION_MODES, DESKTOP_HOOKS, GOAL_ENFORCEMENT).
  Only README, GUIDE and CLI keep a Chinese page.
- main-v2 release pipeline: the prepare-release-notes, release-notes,
  release-verify-issues and pages workflows; their release-notes,
  generate, event, stable-validation and public-contract scripts; and
  release-notes/releases.json.

What had to move with it:
- releases.json was embedded, so the docs tool served main-v2's
  1.17-1.24 history inside Studio 2.17, where "latest release" answered
  1.24.1. productdocs drops the release catalog: release_notes.go, the
  virtual changelog paths, the version boost and Manifest.ReleaseNotes.
  docs-manifest --verify-source now compares docs/ only. The /docs
  example no longer promises a changelog.
- spec-parity held SPEC.zh-CN.md to SPEC.md. With one language it has
  nothing to compare, so the rule and its tests are removed.
- The spatiotemporal golden fixtures lived under docs/superpowers but
  are test data. They move to internal/extension/testdata.
- Links: onboarding pages point at the English documents; English
  documents lose their switch to a copy that is gone. GUIDE drops the
  pointer to the Chinese-only hooks page. The theme provenance generator
  stops writing THEME_ASSETS.zh-CN.md. ci.yml stops linting and running
  what was deleted. CODEOWNERS and the standard lose the two dead paths.

Checked: no broken relative .md link remains; actionlint passes;
go test ./... passes; make lint is clean.

Baseline only goes down:
- doc-language 33 -> 0
- doc-owner 91 -> 44
- doc-prose 2755 -> 2375
- spec-parity removed

site/ is also main-v2's and still reads releases.json. It was not on the
approved list and is left for a separate decision.
2026-09-16 21:49:47 -07:00
esengine e9ea2dbaaf feat(studio): a card an extension may redraw, and an interpreter the pane can change
Two things the workbench could describe but not act on.

An extension that publishes a view against a tool call now draws inside that
call's card. The takeover replaces the body only: the name, the status, the cost
and the failure line stay the host's, so a redrawn card is still recognisably
this call and still visibly the extension's work. The surface travels as a
first-class DTO through eventwire and uihub rather than as an opaque blob.

The shell tool's interpreter becomes a setting. It was already auto-detected —
real bash first, Git for Windows next, PowerShell only when a Windows host has
no bash — but nothing in the new UI said which one was chosen, and nothing could
change it. sandbox now exposes what auto-detection picks from (DetectShells) and
whether a chosen one actually runs (VerifyShell); the settings pane offers only
interpreters this machine really has, names what "自动" resolves to here, and
says on the row itself that Windows PowerShell 5.1 will not parse '&&'. Choosing
one persists to [tools.shell] and rebuilds the runtime, because boot binds the
interpreter into the tool while assembling it — the same path a role assignment
takes, and refused mid-turn for the same reason.

A shell card spends its mono tag on the interpreter instead of on the tool id:
the name above already says Bash, and on a host without one the command was
handed to PowerShell, which is the difference between a command that works and
the same text failing on '&&'.
2026-08-15 08:45:57 -07:00
esengine 2d006ec456 feat(studio): plugin packages, extension view slots, and theme tokens
Working snapshot of the studio branch: installable plugin packages (plan
ticket, archive, export, install-source permission subject), the extension
protocol's view/slot rendering path, desktop theme tokens, and the file-drop
surface on the next shell.

Grouped as one commit because the pieces share the generated protocol schema
and the desktop port they land through.
2026-08-15 07:37:38 -07:00
esengine 9363add18f feat(extension): let an extension hold a place in the rail, not just scroll by
An extension could publish a card, a form, a notification, a status line —
every one of them a thing that happens and then scrolls away. What it could not
do is describe something that is still true: a watcher running, a sync at 40%,
a connection holding. That state belongs in the side rail, and there was no
surface kind for it.

UISurfacePanel is that kind, and it is a kind rather than a new RPC on purpose.
The method registry is frozen; growing it per feature is what turns version
compatibility into every extension author's problem. A new surface travels the
publish path that already exists, so an extension built against an older host
degrades to an unknown kind instead of a broken call.

UIPanelPayload carries no Markdown, unlike UICardPayload. The rail is a narrow
column and a rendered document there costs the layout more than it tells the
reader; an extension with a long story to tell publishes a card into the
transcript, which is what the transcript is for.

Registering the kind is what actually admits it: protocol.enumTypes is the one
table the strict decoder, the JSON Schema, and the generated SDK all read. The
generator's own guard caught the rest — a surface kind with no matching
UI<Kind>Payload in the SDK fails generation loudly, which is how it should
have gone. Regenerated artifacts follow, schema hash included.

The Extension* types move out of event.go and eventwire/wire.go into their own
files while passing through: both were already over the size ceiling, a new
surface kind touches every one of those structs, and the two conversion blocks
are the only thing in eventwire that is not a one-liner.
2026-08-15 03:39:28 -07:00
SivanCola 25040554cc Test the full sidecar package end to end
Problem:
The reference fullsidecar binary activated strategies, providers, and UI that its manifest did not declare, while conformance tests bypassed the real manifest and installed-package layout. The host also omitted Manifest v2 dependency identities from initialize requests.

Root cause:
The fixture built pluginpkg.Package values by hand, and the sidecar handshake projected only runtime intercept/replacement/capability strings.

Fix:
Declare the complete fullsidecar capability surface, project requires/provides and dependency schema versions into the handshake, echo activated provides from the SDK example, and drive conformance through a temporary installed v2 package including enable, disable, remove, and kernel contribution checks.

Verification:
- go test ./... -count=1
- go test -race ./internal/extension/sidecar ./internal/extension/conformance -count=1
- cd desktop && go test ./... -count=1
- cd sdk/go && go test ./... -count=1
- go run ./cmd/extension-protocol-gen -check -root .
- git diff --check
2026-08-08 10:31:50 +08:00
SivanCola 500d21a283 Fix runtime ownership and v2 manifest boundaries
Problem:
Runtime publish gates, receipts, file priors, message deduplication, and stream cancellation were process-global, so rebuilding one session could drain or overwrite another session's lifecycle evidence. The v2 manifest path also still exposed migration behavior for versioned manifests.

Root cause:
Lifecycle state was wired through package globals instead of the controller/session lineage, and migration/parser compatibility retained assumptions from the unpublished manifest v1 design. Drain cancellation also allowed callbacks to register after a generation had already expired.

Fix:
Introduce an explicit RuntimeOwner carried through boot, controller, provider, UI, host streams, receipts, and file-write compensation. Reuse it only within a rebuild lineage, defer replacement publication until commit, reject draining turns, make late drain registrations cancel immediately, and enforce v2-only versioned manifests while limiting explicit migration to pre-extension manifests without apiVersion.

Verification:
- go test -race ./internal/agent ./internal/boot ./internal/control ./internal/extension ./internal/extension/providerext ./internal/extension/uihub ./internal/tool/builtin -count=1
- go test ./internal/extension ./internal/pluginpkg ./internal/recovery ./internal/agent ./internal/control ./internal/boot -count=1
- go test ./... -count=1 (sdk/go)
- focused Desktop plugin lifecycle tests
2026-08-08 02:29:40 +08:00