mirror of
https://github.com/stablyai/orca.git
synced 2026-11-13 09:27:29 +00:00
687b0ee252a6e8d8e5431e00f1eba11f2b810e96
1185
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
345c326a91 |
feat(cli): rename a chat with orca chat rename (#38719)
Fixes #38556 |
||
|
|
a9ba59c944 |
Nest workspaces under parents on other machines (#38740)
* feat(lineage): store cross-store parent links beside same-store lineage (inert)
A workspace's parent can live in another Orca store. Save that link in the
child's store under a new top-level key, crossStoreLineageByChildKey, so older
builds' same-store cleanup never reads it. Nothing writes records yet.
- Types, load normalization (malformed entries dropped one by one), section label.
- Store get/set/remove by child; a child keeps at most one parent record.
- Every maintenance path acts by the child only: deletion and missing-worktree
pruning, repo removal and deregistered-repo residue, identity migration,
profile project transfer, WSL move projection and UNC drive conversion, and
the orcad migration dormant payload (export, validation, import, subtraction).
* fix(lineage): gate cross-store links on host capability; one parent per child
- Migration stage/commit carrying crossStoreLineage requires the new
orcad.migration-cross-store-lineage.v1 capability; older hosts are refused
before staging instead of silently dropping the links. Empty arrays are omitted.
- Payload validation and destination prepare reject a child that has both a
cross-store and a same-store parent, through one shared conflict check.
- Saved entries stay on disk as written; the map is Record<string, unknown> and
readers go through validCrossStoreLineage. Re-keying moves unreadable entries unchanged.
- Tests: literal expectations; drop touched mock-call and work-count assertions.
* test(lineage): drop clone and save work-count assertions from project group removal test
* feat(lineage): resolve cross-store parents into the sidebar parent map (inert)
- One resolver: a saved record nests its child under the single visible
row whose store reports the parent's installationId and whose instance
matches; copied installs, unknown stores and stale instances stay standalone.
- getSidebarLineageParentsByIdentity merges resolved cross-store parents with
same-store edges before cycle breaking; a valid same-store edge wins.
- Workspace delete collects descendants only from the parent's own store.
- Store identity: paired stores from their verified status hostDescriptor;
this store from the published descriptor, else host-installation.json.
- Nothing passes a view or writes records yet, so the sidebar is unchanged.
* fix(lineage): import WorkspaceKey from workspace-scope after main move
* fix(lineage): resolve sidebar parents once over complete rows; keep unowned rows' delete rule
- resolveSidebarLineageParentIdentities resolves same-store edges and cross-store
records once over every visible store's unfiltered rows, then drops cycles.
Ancestor injection and nesting reuse that map, so a filtered-out twin still
makes a parent ambiguous.
- Removed, retired or refusing paired servers leave the view: their records stay
saved and their children standalone. Offline servers keep their last-known id.
- A row with no owner is unknown, not ours: it never resolves, and delete only
excludes descendants whose store is known and differs from the parent's.
* fix(lineage): refuse UNC rekey collisions and transfers giving a child two parents
The UNC drive conversion rekeys cross-store child keys through the shared
collision merge, so a UNC key and its drive key with different records throw
instead of one silently overwriting the other. Profile project transfer runs the
shared child-key conflict check before writing the target.
* feat(lineage): write, carry and nest cross-machine parents
- Capability worktree.lineage.crossMachine on the child's store; the CLI
refuses before any request when the server lacks it.
- worktree.set / worktree.create accept crossStoreParent
{ installationId, workspaceKey, instanceId } and write only
crossStoreLineageByChildKey (refused when it names this store, or the
child has no identity). Remove Parent clears both.
- worktree.crossStoreParentRef: the parent's own store names it with its
installationId; reparent refuses a parent whose chain already reaches
another store. Same-store Set Parent refuses the same.
- worktree.ps rows and lineage lists carry the records; same-store lineage
fields stay null so older readers show the child standalone.
- The desktop sidebar resolves parents over its own and paired stores'
records (PR 2's resolver) at the visible-worktree and row-plan call sites.
- CLI: active/current or an inferred terminal parent against a server is
sent as a cross-store parent.
* fix(lineage): read worktree rows through the record selector; mark the test runtime cast
* fix(lineage): hydrate every store, fence refreshes, honest CLI fallback
- Each host's catalog hydration fetches its lineage snapshot, so this
desktop's identity and records load whichever store is focused.
- Cross-store records join the per-child request-start fence: a stale
lineage list can't restore a removed record or override a newer one.
- A failed inferred cross-store parent sends noParent instead of
forwarding the selector a server could match to a same-store twin.
- Inferred cross-store creates record env-workspace/inferred capture.
- The writer states its trust contract; the chain rule runs at the mint.
* feat(lineage): cross-machine parents on the phone, pickers, chat resume and labels
- Phone: the list nests a child under a parent on another machine, from the
records rows already carry and the store identities its host verified
(status.get descriptor; relayed servers' installationId on the host list).
- Set Parent popover, context menu and the composer's parent picker offer
parents in other visible stores. The parent's own store names it
(worktree.crossStoreParentRef); the window checks it against that store's
verified installationId and the child store's capability before any write.
Remove Parent and Open parent cover cross-machine links.
- Native chat resume ancestors follow resolved cross-machine parents.
- Host sections keep a nested child under its parent and keep its own
machine label.
- Main sidebar feed resolves cross-store parents (rows carry records; desktop
facts and paired-host facts carry identities); paired web windows read
their host's installationId; reveal without an occurrence plan resolves
them; `worktree create --agent` names a parent on another machine.
* test(lineage): type the mint test's parent row through unknown
* test(lineage): give the cross-machine tests' partial-row casts their SAFETY reasons
* fix(cli): worktree set names a parent on another machine
* feat(lineage): name the machine of parent candidates on another machine
* fix(lineage): Set Parent and Remove Parent act on the clicked row's own host
* fix(lineage): phone resolves parents over every row before the repo filter
* fix(lineage): chat resume nests by host-qualified identity across machines
* fix(lineage): local create refuses a same-install parent before checkout and shares the RPC recorder
* fix(lineage): name parent machines without a focus read; keep create's probes synchronous
* fix(lineage): chat resume keys offers by their own machine; only cross-machine parents leave a section
* fix(lineage): name an older parent server before asking it for a cross-machine ref
* fix(lineage): an app-picked parent on another machine records as manual
* fix(lineage): chat resume keeps base grouping; only resolved cross-store parents move a chat
* revert(lineage): drop chat resume from cross-machine parents; it groups by machine as before
* refactor(lineage): CLI builds cross-machine parents; explicit only
- Drop worktree.crossStoreParentRef and its reparent chain walk: the CLI
builds the ref from this desktop's installationId (worktree.lineageList
now reports it) and the parent row it reads. Cross-machine chains are
allowed; same-store cycle validation and same-install refusal stay.
- No inferred cross-machine parent: remove crossStoreParentInferred and
the fallback states. Against a server, this machine's terminal, cwd and
env context is never sent; the CLI prints how to nest explicitly.
* refactor(lineage): build cross-machine parent refs from verified ids; pickers hide only loops; one desktop install id source
* refactor(lineage): refuse moving a linked workspace instead of carrying the link
Orcad migration and profile project transfer now refuse, before any staging or
write, when a workspace in the move has a cross-store parent record (raw entries,
readable or not). Remove the carry machinery: the dormant crossStoreLineage
field, its runtime capability and destination gate, import/subtract/claim code,
transfer carry and the two-parent conflict checker. Also drop the unused
removeCrossStoreWorkspaceLineage.
* docs(lineage): describe cross-store parent refs without the removed mint RPC
* test(lineage): drop the RPC call-list assertion from the cross-store ref test
* fix(lineage): refuse linked moves before any write and for shared-repo children
- Migration refusal passes the child's own host stamp to the scope matcher and
refuses an unstamped key whose shared repo id the move includes, so such a
child can't move while its link stays behind.
- Profile transfer checks the read-only source snapshot before pending-move
recovery or storage migration; the payload check stays as the final guard.
* fix(lineage): refuse a linked profile move before the profile lookup can restore the index
* fix(lineage): startup hydration of other stores loads only cross-machine links
* fix(lineage): list the child's own machine first in the parent picker
* fix(lineage): keep profile argument errors ahead of the linked-move preflight
* fix(worktrees): let the first listing replace a restored server workspace stub
A restored active server workspace starts as a session stub with no head.
If git status fills in its branch before the first listing lands, the
listing merge took that as a concurrent branch switch and kept the stub,
dropping the catalog row (instance id, lineage, provenance). The workspace
then showed standalone until the next listing.
* fix(worktrees): accept a headless row's listing only when it confirms git status
A stale listing could otherwise roll back a branch switch git status saw on
an empty-head row (e.g. an SSH fallback row) during the first connected listing.
* feat(lineage): CLI nests a create on another machine under the caller workspace
- No parent flag and no --no-parent against a server: the caller's workspace
on this machine (env, else cwd) is sent as an inferred cross-store parent,
the way --parent-worktree active builds it. The host records it as
inferred from environment or cwd.
- A server without worktree.lineage.crossMachine, or a caller workspace that
can't be linked, still creates unnested and prints one note.
- An inferred parent falls away for folder targets, as same-machine folder
creates record none.
* fix(lineage): CLI cross-machine inference reads the caller terminal and suppresses fallbacks
- The caller terminal's workspace joins env and cwd as an inferred parent,
with same-machine precedence: env and terminal must agree, cwd only alone.
- A fallback (old server, unlinkable or unresolved caller, ambiguous cwd)
prints one note and sends noParent, so the server's own create-time
inference can't nest what the CLI reported as not nested.
* feat(sidebar): drag a workspace onto one on another machine to nest it
Dropping a workspace on a workspace from another machine now sets the
parent through the same cross-machine path as Set Parent: same eligibility
(loops, older servers), same refusal messages. Drop targets carry host
identity so the child's own copy is the one written.
* refactor(lineage): this machine's Orca resolves the caller parent for a create on another machine
- New read-only worktree.resolveCallerParent runs the same-machine create
resolver over the caller's env, terminal and cwd context, so stale
contexts are skipped and only validated parents can conflict, and returns
the cross-store ref or a reason.
- The CLI's own precedence and terminal lookup are removed; it keeps the
capability gate, one note with noParent on any fallback, and silence with
no Orca context.
* feat(orchestration): remote worker-start --worktree new-child nests under the coordinator's workspace
worker-start --on <server> --worktree new-child creates the server's worktree
under the coordinator's workspace on this machine, using the same verified
cross-machine parent ref as the CLI's caller auto-nest. A server without
cross-machine parents still starts the worker, top-level, with a warning in
the receipt. Guide, CLI help and docs describe it.
* fix(lineage): resolveCallerParent reads this store's identity without creating it
- Uses the published host descriptor's installationId, else the existing
host-installation.json; never creates or repairs it. Missing identity
returns a reason, so the CLI creates unnested with its note.
- Drops the read-only wording: parent resolution does the same catalog
upkeep a same-machine create does.
* fix(sidebar): validate every cross-machine parent before a multi-select drop writes
A drop of several workspaces builds every cross-machine parent ref first, so an
older server's refusal leaves the whole selection unchanged. Drops the touched
preview-timing test cases.
* refactor(orchestration): build the remote new-child parent ref with the shared caller resolver
Orchestration still picks the caller workspace; resolveCallerParentForAnotherStore
verifies it and builds the ref, and both read the installationId the same way.
Drops the copy and mock-call tests.
* fix(lineage): narrow the inferred source with a guard instead of casts
* fix(lineage): a composer parent picked on another machine nests normally once the destination is that machine
The retained host-qualified pick is classified against the current destination:
same host gives the ordinary parent fields, another store the cross-machine ref.
Also corrects two stale comments.
* perf(lineage): read only the requested child's cross-store entry
* perf(lineage): request each store's lineage list once at startup
* perf(lineage): cap optional caller-parent inference at 3 s and skip a timed-out local Orca
* perf(lineage): return cached sidebar parents when selector inputs are unchanged
* fix(lineage): snapshot selector inputs without a type assertion
* Route startup lineage and the sidebar lineage memo through their owners
|
||
|
|
ad17492205 |
Update README downloads badge (#38752)
Co-authored-by: buf0-bot[bot] <252831055+buf0-bot[bot]@users.noreply.github.com> |
||
|
|
4c82aab266 |
README: refresh hero and feature animations (#38726)
New hero still: a coordinator running two workers with the mobile app replying. The Features table is rebuilt from the website's product animations (13 rows led by orchestration, chat or terminal, run anywhere and the mobile app), Also in the box lists the rest of the feature wall, the tagline matches the website, and the contributors image shows up to 500 people. |
||
|
|
4fbb1ccd97 |
feat(native-chat): color chat links with the theme's link color (#38676)
Chat links used the body text color, so a link only showed as a faint underline and a theme's textLink.foreground had no effect. They now paint --chat-link: the theme's link color, else the built-in note blue for the chat's own scheme, mixed toward the chat foreground like the other chat hues. Code and bold link labels follow the link color. When the chat paints a terminal palette the app theme doesn't match, the app theme's link color is cleared, as its bubble already is. The theme contrast check now measures the chat link on the theme's user bubble and drops only the bubble when it fails. |
||
|
|
9deff9dffd |
Update README downloads badge (#31749)
Co-authored-by: buf0-bot[bot] <252831055+buf0-bot[bot]@users.noreply.github.com> |
||
|
|
f660d8c47d |
feat(plugins): add permissioned desktop appearance publication (#38524)
* feat(plugins): add permissioned desktop appearance publication * docs(plugins): add one-line API comments for appearance provider * chore(plugins): regenerate declarative testing kit for appearance API --------- Co-authored-by: Matthew Wilkinson <wilkinson.m@outlook.com> |
||
|
|
6bb2e13c35 |
refactor(tooling): organize remaining scripts by domain owners (#38407)
* refactor(tooling): group remaining scripts by concrete domain owners * fix(tooling): preserve relocated launcher resource roots |
||
|
|
9bd6272fdf |
refactor(tooling): organize build, CI, and test owners (#38130)
* refactor(tooling): organize test runtime and maintenance owners * test(tooling): preserve relocated runtime fixture dependency edges * refactor(tooling): preserve xterm routing owner checks * ci(tooling): include routing owner in sparse checkout * test(tooling): preserve copied workflow dependency closures * fix(tooling): normalize hashed JSON assets in release parity * fix(tooling): distinguish asset aliases from literal references * fix(tooling): frame normalized asset reference payloads * fix(tooling): normalize decoded JSON asset references * refactor(tooling): organize build, CI and release owners * fix(tooling): align moved generator and workflow path contracts * fix(tooling): update generated plugin bundle owner banners * test(tooling): create relocated mobile verifier fixture directories * test(tooling): copy headless detector dependency closure * test(tooling): align native batch policy fixtures with owner path * fix(tooling): link moved reliability command to historical evidence * fix(tooling): preserve nested sandbox owners and canonical guide source * fix(tooling): target release version owners in checkout fixture |
||
|
|
9658ec48ad |
fix(automations): accept Pi model and thinking extra arguments (#38020)
* fix(automations): accept Pi model and thinking extra arguments * fix(automations): keep Pi provider pins and refuse unpinned inline keys * fix(automations): match Pi value options to its argument parser * fix(automations): refuse Pi model overrides beside unscoped inline keys * test(automations): cover Pi's last-provider-wins key pin * fix(automations): refuse any Pi custom Command text with --api-key * fix(automations): drop the Pi inline-key pin guard Pi already sends an unpinned inline key to the chosen model's provider and warns; the user typed both the key and the model, so Orca does not refuse it. |
||
|
|
ee6a248ec9 | Remove the New divider from Chat UI (#38044) | ||
|
|
542a2ec78b |
docs(agents): default to no new tests and tighten the anti-pattern list (#37993)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> |
||
|
|
9c7fe9793e |
fix(deps): backport minimatch, glob, and deepmerge security fixes (#37981)
Backport upstream matching and abort fixes plus safe deepmerge property handling across the independent workspaces. Includes installed-package regression tests and audit-tool fixture repair. Agent-reviewed at 83ca381d519e6247d7c1b3f0a52ce2907a9f9a19; CI aggregate passed. Version-based scanner alerts remain open pending documented disposition or compatible upstream releases. |
||
|
|
d5f2a17cd2 | refactor: group agent launch contracts under concrete owners (#37915) | ||
|
|
d2f874dbb5 | fix(terminal): rename link popover's "Open in this group" to "Open in new tab" (#37920) | ||
|
|
7f885c19a6 |
fix(skills): rename computer use to orca-computer-use (#37659)
* fix(skills): rename computer use to orca-computer-use * test(skills): resolve rename fixture after main cleanup |
||
|
|
0a1e420212 |
feat(editor): add Insert Final Newline on save (#37786)
* feat(editor): insert final newline on save Based on the community contribution in #33567 by @johnie. Co-authored-by: Johnie Hjelm <864992+johnie@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: credit community final-newline contribution Co-authored-by: Johnie Hjelm <864992+johnie@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(editor): exclude CSV previews from clean saves Restore the final-newline setting imports after the upstream settings refactor. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(editor): skip unnecessary clean saves Only run the final-newline clean-save fallback when text needs a newline, while preserving empty and terminated drafts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(editor): preserve pending edits on keyboard save Use current tab state and retain loaded fallback text when pending edits have not produced a draft yet. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Johnie Hjelm <864992+johnie@users.noreply.github.com> |
||
|
|
3a714906a0 |
plugins: keep status bar plugins' workers running while enabled (#37630)
A plugin with a worker and status bar items is now started when enabled and kept warm, like a plugin with hooks, through one shared resident-worker predicate and warmer. |
||
|
|
36f17a04a7 |
plugins: Box layout container for components panels (catalog 4) (#37420)
- Box (ui.catalog 4, since 1.4.248): direction, gap/padding/paddingX/ paddingY/margin on the gap scale, align, justify, wrap, grow, shrink, width/height/min/max (px up to 4096 or whole percents), columns, scroll, border (true or a tone), radius, background (theme surfaces or tone tints). Strict props: hex colours, CSS lengths and unknown props are dropped with a warning like other components. - Renderer maps props to theme tokens through data attributes; sizes reach inline style only as validated px or percents. - Typings, docs, skill references, gallery Layout tab; orca/testing finds Boxes and summarizes danger or warning Boxes. |
||
|
|
cb031c4517 |
fix(plugins): merge disk writes into a custom editor's unsaved edits (#37343)
* fix(plugins): merge disk writes into a custom editor's unsaved edits A write to the file on disk while a custom editor tab had unsaved edits never reached the editor: the buffer kept the edits, Orca showed its Reload/Overwrite banner, and the session's merge never ran. Now a frame whose document session has merge announces it (editor.merges), and Orca sends it the new disk text with the text the edits started from (editor.document with ancestor). The session merges, posts the result, and the tab stays unsaved with it; the banner is skipped. Without merge the banner decides as before. * test(editor): an explicit save under the changed-on-disk banner keeps the edits * fix(plugins): make the outside merge exact, and narrow it to where it is - The host settles a merge only on the session's edit made against the merge's own document version, and always commits that result to the buffer (even when it equals the disk text); silence past the 1 s flush deadline, a remount or a throwing merge leave the banner and the suspended autosave in place. - Orca stamps exactly the disk snapshot the acknowledged merge used and records it as the next ancestor; a write that landed meanwhile puts the conflict back for its own merge. - The ancestor is the tab's disk text recorded while clean and advanced on acknowledged merges, and must match the tab's disk baseline. - Only a local file in one merging editor view, with no other tab on its buffer, takes this path; anything else gets the banner. - Frame events keep reaching their handlers after the merge capability. * fix(plugins): move nothing on a failed disk merge; advance the saved text on success A merge that throws now leaves the session's document, baseline and version untouched, so the user's next edit goes out against the old version: Orca refuses it and keeps the banner instead of taking it as the merge's acknowledgement. After an acknowledged merge the tab's saved text moves to the disk text it used, so typing back to the old ancestor no longer looks clean. * chore(plugins): regenerate the testing bundle on main |
||
|
|
5846caf2ba |
plugins: editor.save.before hook (format on save, or refuse the save) (#37347)
* plugins: editor.save.before hook (format on save, or refuse the save) Explicit saves of text files from an editor tab run plugins' editor.save.before in main before the write (local, SSH, WSL and folder workspaces all save through that one renderer path). A plugin can return changed text or deny with a reason, shown in the save error. The buffer takes the hook's text only if it is unchanged since the hook started; edits made meanwhile stay dirty. Autosave, restart saves and bytes documents are not hooked, and with no live plugin listing the hook the save path adds no await. Co-Authored-By: Claude <noreply@anthropic.com> * test(plugins): editor.save.before text reaches the visible Monaco model The hook writes its text into the tab's draft, which reaches the model through the same content-sync bridge as Reload from disk. A model-level test covers the formatted text showing, the next keystroke editing it, and edits typed during the hook staying in the model. Co-Authored-By: Claude <noreply@anthropic.com> * plugins: editor.save.before review fixes: flush held typing, update the surviving view, keep the denial - After the hook answers, pending view text (rich Markdown's serialize timer) is flushed before deciding whether the buffer still holds the hook's input. - The hook's text goes to whichever view of the buffer record is still open, so closing the saving view keeps a sibling view in step with disk. - The save-request bridge passes Errors through whole, so the toast shows the plugin and its reason instead of a generic failure. Co-Authored-By: Claude <noreply@anthropic.com> * chore(plugins): regenerate after rebasing the save hook on main --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
c8112e643f | docs(plugins): regenerate the manifest reference and schema for the priority wording (#37351) | ||
|
|
999160b3db |
feat(plugins): commands[].replaces lets a mod take over a built-in action (#37316)
* plugins: commands[].replaces lets a worker command take over a built-in action A worker command with replaces: <action> (the declarative action list, engines >=1.4.248, needs main) runs in place of that built-in from its shortcut, plugin aliases and the sidebar menus; a failed or silent (2 s) worker falls back to Orca's own. The install screen names each replacement, the plugin page has a per-action switch (settings.pluginReplacementsOff), and when two plugins replace one action the one installed first wins and the other's log says so. Update review counts replaces as new access. * fix(plugins): narrow replaced action ids before looking up their titles * plugins: harden command replaces after review - Consent includes the replaced actions, so adding replaces to a dev folder needs a new approval; CLI approval and update review name each takeover. - Shortcuts main intercepts (Tasks, Workspace Board, history), search with a selection and floating maximize now go through the replacement too. - A worker that only starts after the 2 s fallback no longer runs the command as well: main refuses to start it past the deadline. - A workspace-only command does not replace an action without a workspace. - An update keeps the plugin's first install time, so it keeps its takeover. - Drop the commands.run promise until Orca offers it. * plugins: commands.run always runs the built-in, never a replacement A5b's commands.run dispatches with the 'plugin-host' source; the app dispatcher now builds handlers that run Orca's own action for it, so a plugin that replaces an action and calls commands.run cannot loop. Docs and schema describe wrapping the built-in with commands.run; regenerated. |
||
|
|
2fb3a0975e |
feat(plugins): workers can open files and run built-in actions (#37323)
* feat(plugins): add editor.open and commands.run for full-access workers Both ride the existing plugin view request channel (the one plugin open and plugin shot use) to the one main window's renderer; headless serve and a missing window answer unavailable. editor.open takes a path that stays inside the workspace (Git worktrees and folders) and opens it as a tab without switching what the person sees. commands.run takes only the built-in contributes.commands action list and dispatches Orca's own handler, never a plugin replacement. Both need workspace:write. workspace.list now also lists project-group folder workspaces. * fix(plugins): open folder workspaces and host-naming ids through Orca's window workspace.open of a project-group folder asks the window to show it (setActiveFolderWorkspace), since the runtime activates Git worktrees only. editor.open resolves the identity: ids workspace.list now returns. commands.run documents that it must call the raw built-in, never a replacement wrapper. * plugins: regenerate the testing bundle and skill guides on the rebased host actions Generated files only (lint-staged's oxlint refuses a commit with no lintable files). Co-Authored-By: Claude <noreply@anthropic.com> * chore(plugins): regenerate the testing bundle on main --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
544577fe02 |
feat(plugins): workers can list and open workspaces, create and read terminals, launch agents (#37312)
* feat(plugins): let full-access workers act on workspaces, terminals and agents Host API minor 3 adds worker-only rows: workspace.list (workspace:read), workspace.open (workspace:write), terminal.create (terminal:send), terminal.read (terminal:read, at most 2,000 lines) and agent.launch (agent:launch). Each dispatches the runtime RPC method the orca CLI calls, in process with the CLI's identity, so it runs on the host that owns the workspace (local, WSL or SSH) and covers folder workspaces. The new capabilities arrive in 1.4.248; typings, docs and skill references are generated from the table. * fix(plugins): make workspace host actions safe to revoke, cancel and target - Each runtime effect re-checks the plugin's approval right before the runtime acts (a dispatcher beforeInvoke hook, after its own waits), and a worker's cancel or exit now aborts the call's signal through to the runtime. - agent.launch starts the agent's CLI in a background terminal and returns its handle at once; a prompt is delivered afterwards through the runtime's own reuse-terminal path, so the host call never waits for the composer. - workspace.list adds project-group folder workspaces and returns ids that name their host (identity: keys), so twins on two hosts are never ambiguous. - terminal.create passes presentation background; terminal.read counts a runtime-limited read as truncated. workspace.open refuses folders as unavailable until a window-side open lands. * plugins: keep both API extensions and host-call tracking under the line limits after rebasing on the hooks Worker-only status bar and workspace methods spread into PLUGIN_HOST_API through one module, the in-flight host call map moves into PluginWorkerHostCalls, and bindPluginRuntime builds the runtime RPC so main-process-plugins.ts keeps its size; regenerated docs and typings. Co-Authored-By: Claude <noreply@anthropic.com> * chore(plugins): regenerate after rebasing on the editor recipe and shots * chore(plugins): regenerate after rebasing on the plugin shot recipe --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
06b8f7dc37 |
Keep Windows and Linux chat switching separate from worktree shortcuts (#37199)
* fix(shortcuts): give Windows and Linux chat switching a working default * test(shortcuts): verify the worktree and chat chords independently * fix(shortcuts): preserve agent newline keys while switching chat views * fix(shortcuts): use Ctrl+Shift+F11 for Windows and Linux chat switching The Ctrl+Alt+Shift+J default could not match non-Latin layouts or IME input, because Ctrl+Alt chords skip the physical-key fallback that protects AltGr characters. A function-key primary matches on every layout; Ctrl+Shift+J stays as the alias where Switch worktree doesn't claim it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(shortcuts): keep the chat toggle's own default alias when its shortcuts are edited Saving any change to Switch between Chat UI and terminal on Windows and Linux kept its untouched Ctrl+Shift+J alias, which the conflict check then refused against the worktree palette's default, so replacing or removing the primary failed. The check now exempts a chord per binding when it is the action's own default and every other owner also holds it as a default, so non-default explicit claims are still refused. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(shortcuts): expect the new Windows/Linux chat switch label The context menus show the toggle's primary binding, which is now Ctrl+Shift+F11 on Windows and Linux. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
f908d2f40a |
feat(plugins): plugin shot --editor to screenshot and drive custom editors (#37302)
* plugins: orca plugin shot --editor, and plugin open --editor opens the file's folder plugin open --editor now opens the file in the workspace that holds it, matching its real path too (/tmp vs /private/tmp), and otherwise adds the file's folder as a folder workspace instead of failing with no_workspace. plugin shot <dir|key> --editor <file> [--theme] [--width --height] [--act] loads the editor afresh in a hidden tab, waits for it to get its document and ask for it (a new editor.subscribed frame event), captures the editor's guest with the panel shot's capture path, and sends --act clicks, drags and keys as real input via sendInputEvent. It exits 1 with the frame's errors when the editor throws, is blocked by its content policy, or never asks for its document within 5s; theme and size are put back. New RPC method plugins.shotEditor so older hosts fail cleanly. * plugins: shot --editor never changes the author's file With --act the shot copies the file into a temporary folder, shoots the copy in a temporary folder workspace, then closes its tab and removes the workspace and folder; --in-place acts on the file itself. Without --act it opens the real file, leaves its buffer alone and closes the tab if the shot opened it. Afterwards Orca shows the workspace it showed before. Regenerates the skill's CLI reference. * plugins: fix editor shot typecheck (refusal next step, RPC test mock) * plugins: editor shot targets its own guest, flushes before reload, scopes cleanup per capture - Main captures the exact guest WebContents the shot's tab mounted, not the first with its URL. - An open editor's pending edits are flushed before the shot reloads it; the shot fails if not. - --act --in-place keeps the acted tab and its unsaved edits. - end-capture undoes only its own capture (per-capture id), so overlapping shots don't clash. - The file's workspace lookup includes local folder workspaces. * plugins: shot --status-bar/--window, key acts focus the editor guest, panel shot selects a workspace - Editor --act keys first give the guest's <webview> DOM focus and refocus its target, so a canvas that keeps focus no longer drops later keys; never webContents.focus(), which focuses the window. - orca plugin shot <dir|key> --status-bar | --window [--theme]: captures Orca's status bar or the whole hidden window (plugins.shotChrome) with the existing capture helpers. - With no workspace showing, a panel shot selects this machine's most recent workspace in the hidden window instead of failing. * plugins: a shot's end restores only its own capture overrides * plugins: regenerate the testing runtime with the editor subscribed signal * plugins: editor capture refusals go through one CLI-message helper * fix(plugins): give the chrome shot refusal a next step |
||
|
|
fa7c3f95fe |
feat(plugins): check custom editors and test them with mockEditorHost (#37293)
* feat(plugins): check custom editors statically and add mockEditorHost orca plugin check now follows each custom editor entry (local script src, inline module scripts, relative imports), transpiled and resolved as the frame loader does, and fails at file:line:column on syntax errors, missing files, refused imports, string eval/Function and files over their size cap, with no running Orca. The check ships in the test hooks bundle. orca/testing gains mockEditorHost: it runs the frame shell's real window.orca (document API and session) over an in-memory text or bytes file, with edits, replace, conflictNext, flush, setTheme and dispose. Typings, docs and the skill guide cover both. * feat(plugins): warn on editor eval and check editors in dev watch editor_eval comes from a lexical scan that can misread rare code, so it is now a warning that does not fail orca plugin check. dev watch --check runs the editor check beside the panel check and reports it under editors. * fix(plugins): check editors by the frame protocol's rules; mock blocks lost saves The static editor check now reads files through the loader's contained reader (symlinks out of the plugin are refused), runs only JavaScript script types, honors the page's import map for native modules, classifies files by the protocol's content types (JSON imports, extensionless HTML entries), decodes escaped script URLs, and uses the protocol's caps: 10 MiB entry, 1 MiB transpiled module, 16 MiB checked dependency. mockEditorHost refuses an oversized edit before reading it and then rejects flush() until a later edit lands, as Orca blocks the save. * test(plugins): type mockEditorHost's frame API instead of reflecting on it * fix(plugins): keep main's case-spelling check and hook typings through the rebase The rebase onto main resolved plugin-artifact-validation.ts and orca-testing.d.ts to B4's side; this puts back main's exact-path-spelling check and hooks typings beside B4's contained reader and mockEditorHost types, and regenerates the testing bundle. |
||
|
|
7989c05ad3 |
feat(plugins): agent tool-call hooks (Claude Code can block or rewrite) (#37303)
* plugins: agent.tool.before and agent.tool.after hooks; Claude Code PreToolUse blocks
The hook server runs plugins' tool hooks after recording status. Claude Code's
POSIX hook script now asks for an answer on PreToolUse (X-Orca-Hook-Decision),
waits at most 8 s (Orca answers within 6 s) and prints the decision: a deny
with the plugin's reason, or updatedInput without approving it. Timeouts,
errors and an unreachable Orca print {}, so the call proceeds. Other events
still print {} before posting. Every other agent, Windows scripts and SSH/WSL
relays only observe or are not hooked yet (per-agent table in the
agent-hooks README).
Co-Authored-By: Claude <noreply@anthropic.com>
* plugins: verify PreToolUse answers against the real Claude CLI; 204 at once when no tool hook listens
A real-CLI test (ORCA_REAL_CLAUDE_CLI_TEST=1) shows Claude Code 2.1.316 runs
updatedInput sent without permissionDecision, still asks for approval of the
rewritten call, and refuses a deny. With no plugin listing a tool hook, the
server answers 204 before any await and runs no chain.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(claude): type the real-CLI PreToolUse answer
* plugins: harden agent tool hooks (partial replies, fenced posts, OpenClaude, Amp/Copilot/Cursor)
- Drop a failed curl's partial body and print only a complete PreToolUse decision.
- Run tool plugins only for posts status ingestion accepted; name the owner's terminal.
- Enable PreToolUse decision mode only for Claude Code, not CLIs reusing its script.
- Decode Amp tool.call/tool.result, Copilot envelope/implied events and Cursor shell/MCP calls.
Co-Authored-By: Claude <noreply@anthropic.com>
* plugins: regenerate the testing bundle on the rebased tool hooks
Generated file only (lint-staged's oxlint refuses a commit with no lintable files).
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
|
||
|
|
60d262ebee |
feat(plugins): hooks for agent launches and prompts, plus plugin dev hook (#37295)
* plugins: agent.launch.before and agent.prompt.before hooks; orca plugin dev hook agent.launch.before runs at the PTY spawn every agent terminal goes through (window tabs, runtime/CLI agent launch, automations, orchestration), before dispatch to its host: change the command line, add env (Orca's own ORCA_* variables and the agent's environment stay hidden), or deny. Restores and resumes are not hooked. agent.prompt.before runs in sendTerminalAgentPrompt (launch prompts, orchestration dispatch, terminal send to an agent) with the agent named: rewrite or deny. The terminal hooks move into one runtime layer. orca plugin dev hook runs a hook through one dev plugin and prints the decision. Co-Authored-By: Claude <noreply@anthropic.com> * plugins: recheck rewritten launch commands, forward hook env to WSL, keep prompt delivery outside the hook order - A command agent.launch.before rewrote passes the AI Vault resume check again on both spawn paths. - Variables a hook adds are named in WSLENV on Windows so WSL agents get them. - agent.prompt.before holds the terminal's order only while it decides; a delivery waiting on a permission answer no longer blocks later input. Co-Authored-By: Claude <noreply@anthropic.com> * plugins: plugin dev hook honours onError: deny Co-Authored-By: Claude <noreply@anthropic.com> * plugins: regenerate the testing bundle and skill guides on the rebased hooks Generated files only (lint-staged's oxlint refuses a commit with no lintable files). Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
ac8c1125f0 |
feat(plugins): hooks that can change or block terminal sends and workspace creation (#37287)
* plugins: contributes.hooks runtime with terminal.send and workspace.create hooks
Workers register orca.hooks.on(hook, (event, next) => ...). Orca runs listed
plugins in order before it acts: next(event) continues with allowed fields
changed, { deny } stops, nothing passes. A plugin past its 2 s deadline, or
one that throws, crashes or is disabled, counts as passing and is logged with
plugin, hook and duration. First hooks: terminal.send (runtime sendTerminal),
workspace.create.before (IPC and runtime creates, folders included) and
observe-only workspace.create.after. orca/testing mountWorker gains
hooks.fire. Needs main + trusted-worker and engines.orca >=1.4.248.
Co-Authored-By: Claude <noreply@anthropic.com>
* plugins: keep hook plugins' workers started; bound start-up separately from the hook deadline
Workers of enabled plugins that list hooks start as soon as they are approved
and are never idle-reaped; crash restarts follow the manager's policy. A hook
waits up to the worker ready timeout for start-up, then gives the handler its
full 2 s, so a cold worker no longer lets the first hooked action through.
Co-Authored-By: Claude <noreply@anthropic.com>
* plugins: give hook workers their own soft cap instead of the active-worker cap
Always-running hook workers no longer hold slots of the shared 5-worker cap, so
many hook plugins cannot starve the rest. They have a soft cap of 16; past it a
hook worker waits for another to stop, logged once.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(plugins): type the hook warmer's ensureWorker mock
* plugins: address hook review: ordering, human input, overflow, revalidation, folder creates
- Hook workers past the soft cap start as overflow (logged once) instead of
waiting, so reconciliation, disable and uninstall never hang.
- Writes to one terminal stay in order while terminal.send decides, so a later
Enter cannot submit the previous line.
- terminal.send runs only for programmatic sends (main, plugins, the CLI);
desktop, mobile and legacy client input is a person's typing.
- Text a hook rewrote passes the AI Vault resume check again.
- Project-group folder workspace creates (RPC, agent launch, window) run the
workspace.create hooks through one boundary.
- A decision from a plugin disabled or changed while it ran is discarded.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(plugins): drive hook start-up timing with fake-timer deferreds, no real sleeps
Co-Authored-By: Claude <noreply@anthropic.com>
* plugins: only the hook decision holds a terminal's write order
A delivery that waits (a permission prompt, the launch composer) no longer
blocks later writes, and a person's input, interrupts and query replies never
wait behind a pending decision.
Co-Authored-By: Claude <noreply@anthropic.com>
* plugins: hook DX: onError deny, denied_by_plugin, a Hooks reference, --template hook
- contributes.hooks[].onError: "deny" makes a throw, timeout, crash or bad
answer refuse the action, naming the plugin; the default still passes.
orca/testing mountWorker honours it, and mockHost answers fire-and-forget
methods (result { ok: true }) with success by default.
- A plugin denial reaches RPC clients as denied_by_plugin with the plugin and
reason, and the CLI no longer suggests a retry for it.
- The skill's host-api reference gains a generated Hooks section (event, what
next may change, stop result); the recipe passes sends through unchanged and
warns against awaiting other calls before deciding.
- orca plugin create --template hook: a worker-only terminal.send guard that
fails closed, with a test.
Co-Authored-By: Claude <noreply@anthropic.com>
* plugins: regenerate hook docs on main; bind the plugin runtime and its hook runner in one call
Keeps main-process-plugins.ts under 300 lines now that status bar items landed.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
|
||
|
|
a9b1041ecc |
feat(plugins): status bar items plugins can update live (#37291)
* plugins: contributes.statusBarItems with worker updates (statusBar.update)
A plugin declares status bar items { id, text?, icon?, tone?, tooltip?,
command? } (declarative profile, engines >=1.4.247); pressing one runs
its own command. A trusted worker changes them with the new worker-only
host method statusBar.update; main keeps the session's changes and
pushes snapshots to desktop windows over plugins:statusBarChanged.
Regenerates schemas, references, typings and the authoring skill.
* plugins: statusBarItems ship in 1.4.248, beside contributes.hooks
* plugins: clear a plugin's status bar changes when it is disabled, removed or updated
* plugins: list status bar items in the plugin page's Adds section
* plugins: count statusBarItems' since in the host's newest readable version
* i18n: add the plugin page's status bar row label to en.json
* plugins: status bar items respect workspace commands, dev reloads and catalog summaries
- An item whose command needs a workspace is disabled with no open workspace.
- A dev code edit (new devRevision) drops the old worker's status bar changes.
- Catalog summaries list status bar items, and marketplace details show them.
* plugins: authoring skill lists status bar items as buildable; approval lists them
The skill no longer calls status bar items reserved and gains a row for them. The
dev-load/inspect approval already lists them via the contribution summary; a test pins
that and the fingerprint binding.
* chore(plugins): regenerate the testing bundle after rebasing on main
|
||
|
|
fbe891ce8b |
fix(plugins): skip nested packages in dev folders and summarize blocked frame requests (#37300)
* fix(plugins): skip nested dev packages and count blocked frame requests Dev folders and their watcher now skip .git, .orca and node_modules at any depth, so a nested build/node_modules no longer trips the 2,000-file limit or re-reviews the plugin; installs still hash everything but the root .git. Requests a plugin frame makes off its own files (session-cancelled or refused by its content policy) are counted per kind and origin and logged every 5 s and on teardown, outside the 20-lines-per-10-s console budget: [frame] blocked font-src https://unpkg.com (x48): plugin frames have no network * fix(plugins): read current CSP refusal wording and bound blocked-request logging - Electron 43 prints "Loading the font '…' violates …" (and Connecting to / Framing / Fetching content from); read it alongside the older "Refused to …" wording, name the unset directive from the fallback note, and treat fetch's repeat as an echo. An Electron test captures the bundled runtime's real messages. - Keep at most 32 kind-and-origin counts and log at most 8 lines per flush, folding the rest into one overflow count line. |
||
|
|
4a0ee07bd2 |
feat(plugins): orca.document.session() for custom editors (#37285)
* feat(plugins): add orca.document.session() for custom editors One call replaces the document sync every editor rebuilds: decode/encode (plain text by default), optional merge, debounce. One edit in flight with later changes coalesced; the baseline moves only on accept; a refused edit keeps the draft and rejects flush(); own accepted edits never reach onReplace; a conflict merges once against the last accepted value and resubmits, or keeps the draft without merge. The session answers editor.flush itself and resolves after hello and the first document. The --template editor now uses it. Docs and typings state the session and the undo model (the editor owns undo). * fix(plugins): let the outside document win a session conflict without merge Agents write files while people edit them, so keeping the draft lost the agent's write on the next keystroke. Without merge the session now drops the draft, fires onReplace with the outside value and rejects flush() with conflict; editors that want both sides pass merge. * fix(plugins): serialize session edits through saves and settle before answering - One edit in flight at all times, saves included; editor.flush is answered only once every change is accepted or refused with nothing newer to send. - The editor host commits a held edit when a save starts and admits edits during the save at once, so waiting fits the 1 s deadline. - A change generation replaces identity checks, and sent bytes are snapshotted, so a reused buffer is never mistaken for an accepted edit. - A waiting flush keeps its conflict rejection when the outside document wins, and nothing merges or sends after dispose. * test(plugins): read session test edits through typed helpers * fix(plugins): drain session flushes, reject dropped drafts, snapshot the baseline - flush() keeps going until no change made during it is left unsent. - A waiting flush rejects with conflict whenever the outside document replaced a draft, newer ones included. - The accepted baseline is decoded from the sent snapshot, so a reused value buffer cannot change merge's base. * test(plugins): name the session test option and message types |
||
|
|
51f302167e |
feat(plugins): let a custom editor require the desktop app's isolated frame (#37298)
* feat(plugins): let a custom editor require isolated frames customEditors[].frame: "isolated" | "any" (default any, since 1.4.248). An isolated-only editor never runs in the srcdoc fallback: main serves its fallback shell without the entry, and when this window has no isolated guest (browser clients, or desktop with isolated frames off) the file opens in Source with "This editor needs the Orca desktop app". Picking the editor shows the same notice with an Open Source button. * i18n: add the needs-desktop editor notice strings * plugins: refresh frame isolation when its setting changes; browser clients open Source - The learned isolated-frames answer is tied to the setting it was learned under, so turning isolated frames back on lets isolated-only editors open by default again. - A browser client's plugin bridge has no list, so the plugin store settles on no plugins instead of erroring and retrying; files open in Source. |
||
|
|
54b496cef1 |
fix(cli): open ordinary directories as folder projects (#37232)
* fix(cli): open ordinary directories as folder projects * fix(cli): explain the non-Git layout in the repo add next step |
||
|
|
29479ddd3e |
Chat UI roll out implementation (#37222)
* Adjust Chat UI announcement rollout and show a nonblocking card - Assign new profiles to an 80/20 control and treatment experiment, with treatment split between all users and eligible harnesses. - Show terminal-default users a corner card and record targeting assignments to measure outcomes. * Refine the Chat UI announcement card layout and copy * Add live switching demo to Chat UI launch tip experiment * fix test * Shorten Chat UI announcement telemetry documentation * Add a swapped-button cohort to the Chat UI launch tip - Assign 10% of new enrollments to the new variant while preserving existing assignments. - Track live-switching link exposure and simplify the demo copy. * Adjust Chat UI launch tip rollout and switching options - Add a centered dialog cohort and independently vary button emphasis. - Target users who have not switched and allow switching back to terminal. - Record live-switching exposure only when the link is visible. * Validate persisted chat tip emphasis before initializing telemetry * Target chat UI launch tips using the current default surface Freeze eligibility at enrollment while preserving the historical audience. |
||
|
|
923be35090 |
docs(settings): point the Chat or CLI choice at Settings → Agents (#36871)
* docs(settings): point the Chat or CLI choice at Settings → Agents The Settings → Chat → Providers page was removed; the default and the per-agent choice now live in Settings → Agents. Tracked in STA-13195. * docs(settings): say the Chat | CLI choice is for supported agents on this computer |
||
|
|
ac99f1cb39 |
fix(emulator): list only the worktree's attached emulators, iOS and Android (#37127)
* fix(emulator): scope emulator list to the worktree's sessions emulator list ignored --worktree and returned serve-sim's global iOS helper list. It now lists the session registry's attached sessions (iOS and Android) for the selected worktree, or every worktree's for --worktree all. * fix(emulator): drop stopped devices before listing sessions * fix(emulator): point the no-active hint at emulator devices * test(emulator): assert list results, not serve-sim calls * refactor(emulator): prune stopped devices inside the registry listing |
||
|
|
af6dabd70b |
feat(plugins): orca plugin shot --scenario replays a check scenario; dev watch shot pendingRequests (#37090)
- shot <key|dir> --scenario <name> renders the panel in that check scenario through the check runner (a separate Node process on a mock host and mounted worker), sends the validated drawn tree to a new plugins.shotReplay, and captures it in a throwaway preview surface: no guest, no live host, no real storage or settings, no real worker, images as placeholders. Window hidden or minimized only; --act, --environment and bridged shells refused; an older host answers method_not_found (runtime_unsupported) rather than doing a live shot. - dev watch's shot event carries pendingRequests. |
||
|
|
5467bf3d25 |
fix: preview remote HTML outside registered workspaces (#36791)
* fix: preview remote HTML outside registered workspaces * fix(terminal): keep HTML from hosts without re-grantable files in the editor * fix(doc-preview): check outside-workspace reads by host-canonical path and page large workspace assets * fix(doc-preview): check approved folders for workspace assets of outside documents * test(e2e): read the preview guest without a type assertion * test(e2e): explain the outside-workspace preview timeout * test(doc-preview): type the pinned grant target in the share test --------- Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com> |
||
|
|
dfbaa2fc29 |
Plugin shots wait for the worker and restore after --act; terminal approval prompt (#37066)
* feat(plugins): dev watch asks for first-load approval in a terminal; acronym-aware names
- In an interactive terminal without --json, a first dev watch load that needs approval shows
the grants (as plugin inspect prints them) and asks Approve and load? [y/N]; yes reloads with
the host's fingerprint and keeps watching. --json and non-TTY runs keep the exit-3 flow.
- create keeps known acronyms uppercase when it derives a name from the id (pr-queue → PR Queue).
* fix(plugins): a declined dev watch approval prints only the re-run command
The person just read the grants in the prompt, so the exit-3 error no longer repeats them:
Not approved. To approve later, run: <command>.
* feat(plugins): shots wait for the worker; --act restores the panel; Table and Tree acts
- A shot settles once the panel has no plugin.request or host call in flight and no new tree for
a quiet spell (capped at 3 s), so a cold worker's skeleton is not captured; pendingRequests
reports what was still pending.
- After --act and the capture, the guest restores every plugin-written atom it had before (new
ui.snapshot/ui.restore; persisted atoms re-persist) and Orca remounts its drawn controls;
restored: true|false. Worker-side effects are not rolled back.
- --act gains the kit's slide, sort, selectRows, activateRow, loadMore, expand, collapse,
selectItem and activateItem.
- Docs: the dev watch first-load prompt; shot settle, restore and the new verbs.
* fix(plugins): a shot waits for the theme repaint and retries a bare capture
Linux CI captured a dark --theme shot as a bare background (2 colors). The renderer now waits
until the document shows the asked color scheme and paints, adds a frame after a --width
resize, and main recaptures a panel image that is still a background and a border (two colors
or fewer) up to 8 times, 150 ms apart, before returning it. Html panels keep their guest
composite.
* fix(plugins): capture the main window with stayHidden
webContents.capturePage(rect, { stayHidden: true }) keeps a hidden window rendering for the
capture, as the components gallery screenshots do. The repaint wait stays, and the bare-capture
retry stays as a fallback.
|
||
|
|
ce8ea081c0 |
Plugin check shows what drew; responsive Table columns; quieter Table and Stack (#37062)
* plugins: check shows what drew; responsive Table columns; quieter Table and Stack
- orca plugin check: each scenario result carries a summary (headings,
callouts, empty states, table and tree sizes, the selected tab, loading
placeholders, danger/warning text), printed under its line, and --json
adds the drawn tree. dev watch's check event keeps only the summary.
orca/testing gains panel.summary().
- Table columns take minWidth (floor for an auto column) and hideBelow
('sm' 360px, 'md' 480px), measured on the table itself; at least one
column stays. Cut-off cells and headers show their text on hover. The
kit's columnsOf(table, { width }) answers which columns fit.
- The active-row ring shows only while the grid has keyboard focus.
- A Stack with no align keeps buttons at their own width while fields,
tables and cards still fill it; align='stretch' stretches everything.
- Typings, docs, the skill's workflow reference, regenerated references;
the gallery's Kind column drops out in a narrow sidebar.
* plugins: check can render one scenario and return its drawn tree
For orca plugin shot --scenario (tree replay): checkPluginPanels takes
{ panel, scenario, drawnTree }, and with drawnTree returns each render's
validated tree exactly as Orca draws it (handlers as { $callback }),
re-validated on the CLI side. An unknown scenario is reported as an issue.
readPluginCheckScenarioNames(dir, entry) lists a panel's scenarios by
loading its .check file without mounting anything.
|
||
|
|
459745d9a4 |
fix(plugins): workspace.changed replays the current context on subscribe (#37044)
A panel that loaded before Orca focused a workspace showed nothing until the next change, and a
subscription made before the panel's push channel opened was never told at all. Now each new
subscriber gets the current context first: the panel bridge replays it once the channel is
open (or right away), and a worker's events.on('workspace.changed') reads it through
workspace.readContext. Unit tests for both, and the agents e2e checks the first event arrives
with no change happening.
|
||
|
|
2666bbddbe |
Plugin dev loop: dev request, shot --act, worker-panel template, output polish (#37049)
* feat(plugins): orca plugin dev request; --template worker-panel - orca plugin dev request <dir|key> <name> [--data] [--panel] [--timeout-ms] calls a dev plugin's orca.panels.handle handler through the real panel bridge (plugins.devRequest) and prints the answer; handler errors become plugin_request_failed with the worker's code. - orca plugin create --template worker-panel scaffolds a build-less trusted-worker plugin: a components panel, src/worker.ts, a shared src/protocol.ts contract, and panel and worker tests that pass under orca plugin test and type-check with tsc. * feat(plugins): shot --act, shots in the plugin folder, quieter logs - orca plugin shot --act '<json>' presses, types, selects and toggles in the live components panel (the test kit's queries and acts, through Orca's own controls and onEvent) before the capture, waiting for each to settle. Only that plugin's panel, only with Orca's window hidden or minimized; html panels refused; acts are not user gestures. Results in actions[]. - shot run inside a plugin folder defaults to its .orca/shots, like dev watch --shot. - dev watch prints identical [frame] lines once per reload with a count; logged stacks drop <anonymous>/native frames; the floating workspace button hides during a capture. - Skill: worker-panel template in the decision table, dev request, shot --act, and a gotcha that a worker save drops its in-memory state. |
||
|
|
8503ecda71 |
plugins: check runs the worker and scenarios; RelativeTime (#37047)
- orca plugin check mounts the plugin's worker (main) on the same mock host
as each panel, so plugin.request reaches its orca.panels.handle handlers
as in Orca; a worker that fails to activate or deactivate is an issue
named after its file. In orca/testing, a worker mounted on a host answers
that host's panel requests unless `requests` stubs the name.
- Check scenarios: <entry>.check.ts (e.g. src/panel.check.ts) exports
defineCheck({ name: { ...mockHost options, persisted, theme, events } });
check renders the panel once per scenario and labels each result.
- RelativeTime (catalog 3, since 1.4.247) and { type: 'relative', at } Table
cells: Orca ticks them (each second under a minute, then less often), so
plugins never poll. Inline inside Text; test kit reads them as drawn.
- Typings, docs, skill guide, generated references; gallery shows it.
|
||
|
|
a37a5b3e2d |
Plugin dev emit reaches panels; trim the plugin authoring skill (#37026)
* feat(plugins): dev emit also reaches the plugin's open panels orca plugin dev emit now publishes through the panel bridge to that dev plugin's open panels that subscribed with orca.on, under the same approval and capability rules as the worker, and reports delivered.panels. The bridge's publishEvent takes an optional pluginKey filter and returns how many panels heard it. A panel-only plugin no longer needs a worker; an event nothing listens for is refused. * docs(skills): trim the plugin authoring guide; details move to references The main guide drops from 299 to 121 lines: the decision table, build-less rules, core loop (create → dev watch --check --shot → read the PNG → test → check → release-entry), approval and top gotchas. Test/check and orca/testing (panels and workers), dev watch events, shot, open and dev emit move to a hand-written references/workflow.md; the panel/worker/data and agent status recipes to references/recipes.md, each with a read-when row. The guide test now also scans hand-written references for commands and caps the main guide at 150 lines. |
||
|
|
e5b87cb8e6 |
Components catalog 2 and worker tests: forms, tabs, table, tree, markdown, code, images (#36997)
* plugins: components catalog 2 (forms, tabs, table, tree, markdown, code, images)
Adds 18 components to TSX components panels, drawn with Orca's own
primitives and gated by ui.catalog 2 / ui.has() (Orca 1.4.247):
Textarea, Checkbox, RadioGroup, Slider, Field, Tabs/Tab, Collapsible,
Toolbar, Grid, a virtualized Table with sort, selection and pull-based
paging, a keyboard Tree with lazy children, Markdown (sanitized, links
through the checked open path), CodeView (Orca's highlighter), Image
(header-checked plugin files read by main), KeyValue, Callout and
Skeleton, plus a tooltip prop on Button and IconButton.
Value controls share the edit protocol; handlers get plain JSON event
args, including small objects ({ keys }, { key, expanded }). The test kit
gains matching actions and hides unselected tabs and closed collapsibles;
a components-gallery example and e2e cover the catalog end to end.
* plugins: worker test harness, host refusals in tests and check
- orca/testing gains mountWorker(main, { host, events, commands }): it calls
the default-exported activate(orca) with the orca object Orca itself builds
(createPluginWorkerOrcaApi, moved to shared so the kit reuses it), backed by
mockHost. emit() checks payloads against Orca's event schemas and only
delivers subscribed events; command(), request() (the worker's
orca.panels.handle, as a panel's plugin.request), posts, logs, refusals and
deactivate(). Storage and settings share the mock host's in-memory store.
- orca plugin test loads a worker main's module graph as Orca's worker host
does (.ts/.mts transform, Node resolution, no runtime orca import).
- mockHost records refusals (capability_denied, not_permitted, ...) even
when plugin code catches them; panel.refusals exposes them and
orca plugin check reports them as warnings. Check dedupes repeated issues.
- Typings, docs and a generated references/testing.md skill reference; the
reference generator reads the split orca typings.
|
||
|
|
1392c8e894 |
Plugin dev loop: orca plugin open/shot, dev emit, and dogfood fixes (#37008)
* feat(plugins): orca plugin open and shot; dev watch --shot - orca plugin open <key> [--panel|--editor] shows a panel in the right sidebar or a file in the plugin's custom editor, by changing renderer state only (no focus or window raise). - orca plugin shot <key> [--panel] [--theme] [--width] [--out] [--json] waits for the panel's current code to draw, then crops webContents.capturePage to the panel (html guests included), with background throttling off while hidden. --width and --theme are temporary in-memory overrides; the PNG returns base64 (5 MiB cap), so --environment works. - dev watch --shot <dir> writes a PNG per panel after the load and each good reload. - The authoring skill's loop is now dev watch --check --shot → read the PNG → plugin test. * fix(plugins): shots never disturb a visible Orca - shot --theme is skipped while the Orca window is on screen (themeApplied: false with a themeReason); the override applies only to a hidden or minimized window. - dev watch --shot opens the first panel (or --shot-panel <id>) once, then shoots it with ifShown: if the user moved the sidebar away it emits one shot_skipped instead of switching. * fix(plugins): dev loop dogfood fixes; orca plugin dev emit - dev watch: approval re-run command drops watch-only flags and old --approve; a folder added by a refused load is owned by the next approved load (Ctrl-C unloads it); check events carry the plugin's status (no green while pending/errored); reloaded prints before that reload's logs; --json errors are one NDJSON line. - Errors: log, banner and check share source-mapped positions (a line-1 prelude hands sucrase mappings to the components runtime); check dedupes issues and locates syntax errors; Orca runtime frames and the error module's bogus location are dropped from logs. - .orca/types tsconfig sets preserveSymlinks (tsc 7 loaded typings twice under /tmp). - Templates' READMEs use dev watch. - orca plugin dev emit <dir|key> <event> [--data] sends a test event to a dev plugin's worker through the real delivery rules (plugins.devEmit). * test(plugins): fixed timestamp in the dev emit test * fix(plugins): shot captures an html panel's guest itself On Linux the main window's capturePage leaves a <webview> guest blank. The renderer now reports the guest's rect, and main captures the guest's own webContents and draws it over the window capture at that rect (raw bitmaps, nativeImage only). Components panels are drawn by the host, so the window capture alone stays right for them. * refactor(plugins): keep plugin-service and main-process-plugins under max-lines After rebasing onto #37013 both files crossed 300 lines. The guest log sink moves into the desktop view binding (bindPluginDesktopViews), and plugin-service tightens emitEvent and its event-delivery imports. |
||
|
|
ec0d3cedd1 |
Plugin panels talk to their worker; panel storage, settings and agent events (#37013)
* feat(plugins): panels talk to their own worker, read storage, settings and events
- plugin.request (host API 1.2): a panel or editor asks its plugin's worker, which answers with
orca.panels.handle(name); the worker pushes with orca.panels.post. Same plugin only, JSON
checked, 1 MiB answers, 256 KiB/200-per-10s posts, a deadline and the panel's lifetime.
- Panels and editors get storage.*, settings.* and secrets.* behind the same capabilities, and
storage/settings onDidChange on every surface; events.subscribe delivers workspace and agent
events to panels that hold the read capability.
- contributes.settings: typed settings (a JSON Schema subset) with defaults and value checks.
- Frames get orca.plugin/storage/settings/secrets; components panels import plugin, storage,
settings, secrets from 'orca', and atom(key, init, { persist: 'storage' }) shares a value with
the worker. Workers get orca.panels/storage/settings/secrets sugar.
- One typed contract: declare module 'orca' { interface PluginRequests / PluginPanelEvents }.
- Desktop push channel per open panel session (plugins:openPanelPush).
* feat(plugins): Orca Settings shows and edits a plugin's contributes.settings
The plugin page lists declared settings with Orca's own controls (switch, picker, text and
number fields, Reset to default). Edits are checked against the declaration in main, written to
the plugin's settings, and reach open panels and the running worker through onDidChange.
* docs(plugins): hello-tsx talks to its worker; authoring guide covers panels, workers and data
The example now uses one of each: plugin.request answered by orca.panels.handle, a worker post
heard with plugin.on, a persist: 'storage' atom the worker watches, two contributes.settings and
agent.status.changed in the panel, all typed by one src/protocol.ts. The build-less TSX e2e
drives the whole loop.
* fix(plugins): typed bridge overloads, panel call budget, settings IPC and catalog keys
- PluginBridgeApi/PanelsApi use overloads: declared names are typed (and complete), any other
name carries JSON; no never-unions for the type-aware lint.
- The per-plugin panel call budget is 120 per 10 s, since a panel loading storage atoms,
settings and its first requests makes a burst; the hostile fixture floods past it.
- Settings IPC builds its host per call; the settings section's strings are in en.json.
* test(plugins): the example's declared settings show under the plugin with the person's value
* refactor(plugins): keep hello-tsx the minimal starter; the bridge demo is hello-worker-panel
hello-tsx goes back to a sandboxed-ui panel with no worker or capabilities. The worker, protocol,
storage atom, settings and events move to examples/plugins/hello-worker-panel, with their own
e2e (plugin-worker-panel) and PR route.
* feat(plugins): test kit covers the panel bridge and plugin data
mockHost keeps the plugin's storage, settings and secrets in memory (seeded with storage,
settings, secrets; read with host.data()) and fakes the worker with requests, so plugin.request,
storage/settings/secrets and persist: 'storage' atoms work under mount(), writes reach
onDidChange, and panel.post(event, payload) is what plugin.on hears. Typed by PluginRequests and
PluginPanelEvents.
* test(plugins): focusInGuest waits for the guest and retries focusing as one step
Under load the custom-editor guest could still be attaching, so executeJavaScript threw
'dom-ready' before the focus poll began. Wait for waitForGuestReady, then retry focusing the
webview and the element together, tolerating a guest that is still attaching or remounts.
* docs(plugins): the authoring skill covers panels, workers and data; persistence leaves the renderer
- skill-guides/orca-plugin-authoring.md gains Panels, workers and data (plugin.request/on,
orca.panels.handle/post, persist: 'storage', contributes.settings, panel events, test kit), and
its generated references read orca-services.d.ts for shell, files, on and the bridge types.
- After the dev-loop rebase, persisted-atom wiring (queue plus storage sync) moves to
createDeclarativePersistence so the renderer stays under max-lines.
* feat(plugins): panels show agents: agents.list, terminal ids on agent events, workspace.changed
- agents.list (agent:read, every surface): the focused workspace's agents with terminalId,
title, agent kind, state and stateStartedAt, from the hook server's store.
- agent.status.changed carries terminalId and title: paneKey mapped through the runtime's
terminal list (tabId:leafId) or the status's terminal handle; looked up only while a plugin
listens, in arrival order.
- workspace.readContext terminals carry title; workspace.changed (workspace:read) fires after
workspace session writes when the focused workspace or its terminals change.
- Event subscriptions are kept per panel session, so one made before the push channel opens
still counts.
- shell.openExternal never prompts on a hidden or minimized window: it settles
{ opened: false, reason: 'consent-unavailable' } and says so in the plugin log, which also
notes 'waiting for the person to confirm' while a prompt is up; the rate budget is spent only
when Orca acts. e2e launches never open a real browser or mail client.
- A worker may export activate as default or by name; docs, typings and the authoring skill
document the entry, the agent events and a live agent status recipe.
* fix(plugins): the plugin page names the workspace.changed subscription
* fix(plugins): workers activate only by default export, and say so when it is named
Orca calls the worker module's default export. A module with a named `activate` and no default
now fails to start with an error naming the fix (`export default function activate(orca)`),
instead of being called by a fallback; docs, typings and the skill say default export.
hello-worker-panel gains a picker setting (suggestionOrder) the worker reads.
|