Repository navigation
Conversation
|
@microsoft-github-policy-service agree |
|
Thanks, this looks good overall. Two things on the side call before we merge: Start droid the same way the other side calls start their agent. That keeps the prompt safe on Windows. |
Factory Droid documents the same hook contract this plugin already speaks -- session_id, transcript_path, cwd, hook_event_name, and SessionStart's hookSpecificOutput.additionalContext -- so it needs an adapter, not a second design. - trace/droid.py reads a Droid session: one JSONL file per session under ~/.factory/sessions/<encoded workspace>/, a session_start header carrying the cwd, and message records shaped like a Claude Code transcript. Tool names are mapped (Execute and Script to Bash, Create and Write to Write, Edit and ApplyPatch to Edit). `started` is epoch seconds, as on every other host. - A prompt is a user message that is neither an injected reminder nor a tool result, and not one Droid hides (`visibility: user_only` or `llm_only`). - Session cost: Droid's transcript carries no usage, but `<session id>.settings.json` beside it carries `tokenUsage`. Only that figure is read -- `inclusiveTokenUsage` also counts child sessions, which are folded on their own. - plugin/hosts.py grows a Host base with defaults for the reader methods the mechanisms call, and a Droid host on top; history.py uses those methods instead of Claude Code's functions, so the history import, the envelope replay and the folder rule work for either. - Side calls run through `droid exec`, which writes a session log of its own; they stay off unless TE_DROID_SIDECALL=1. With them off, `microskills=envfacts` still learns. - Tool pruning reports itself unavailable: the TUI has no persistent per-tool setting (Factory issue microsoft#65), and `droid exec --only-tools` has no interactive equivalent. - scripts/build_plugin.py --droid writes a native bundle: .factory-plugin/plugin.json, hooks/hooks.json in Droid's top-level shape, commands/ named tokeneconomizer-*, and the package copied into lib/ (Droid copies the plugin into its cache, so a symlink would break). - Tests: tests/test_droid.py covers the reader, the mapping, usage and fallback pricing, the history import, the hidden-message rule, the hook's own output including an injected fact, and the bundle; test_live_hooks.py exercises the guard on Droid payloads. Not verified end to end: a learned fact reaching a live Droid model. The hook writes the right JSON; Droid appending it to the model's context is documented, not observed here. Refs: microsoft#85
The Factory desktop app gives its sessions no way to set an environment variable, so `TE_DROID_SIDECALL=1` could only be used from a terminal. `droid_sidecall` says the same thing in the config file, which both surfaces read, and stays off by default: `droid exec` writes a session log of its own.
A new Droid 0.233.0 session received the 793-character block as `hookSpecificOutput.additionalContext` and the model repeated it verbatim. Update the four places that still called this hop unverified. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
The row said only `0.233.0`, while the Cursor row names the platforms it was tested on. Support was verified on 0.233.0 on macOS only, so the table now says so and points at the notes below, the way the experimental hosts beside it do. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
docs/DROID.md said the adapter "is not part of an upstream release yet", which stops being true once it is merged. The experimental status, the tested version and the platform stay in the same paragraph. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Grok, merged into main while this branch was open, added itself to six lists that never named Droid: the hosts in docs/PLUGIN.md's opening, the side-call commands in its privacy section and its per-agent limitations, the run table in plugin/README.md, the `--host` values in hook.py's docstring, and the module table in trace/README.md. Droid is now in each, with what the branch already says about it. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
`droid_call` ran `[cli, "exec", ...]`, so on Windows an npm-installed droid would be a `.cmd` shim that cmd.exe parses again, prompt included. It now starts through `program(cli)`, like every other side call, and its throwaway directory ignores cleanup errors as theirs do. `test_every_side_call_starts_its_cli_through_program` now covers droid_call (it failed before this change), and the autouse `_no_model_calls` fixture stubs droid_call with the other calls. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
The side call relied on its system prompt ("Do not call any tool") and on Droid's default
read-only autonomy, which on 0.233.0 still allows Execute, the web tools and every MCP tool
(`droid exec --list-tools`). It now passes `--only-tools Read`, Droid's own allowlist. The list
cannot be empty -- `--only-tools ""` reads as no filter and an unknown name is refused -- so it
keeps the one read-only tool, as Grok's `--tools read_file` does. With the call's own flags,
`--list-tools` reports Read as the only tool allowed, MCP tools included. A live side call
asked to run shell commands, write a file and search the web called no tool (its session log
holds no tool_use) and wrote nothing.
Droid has no setting for a call with no tool at all, and `droid exec` writes a session log of
its own, so side calls stay off by default (`droid_sidecall`, or TE_DROID_SIDECALL=1).
docs/DROID.md says so in "Side calls are off by default"; docs/PLUGIN.md, README.md and
plugin/README.md say the same, and test_droid.py pins the flag.
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
b97d529 to
da7ed1b
Compare
|
Thanks for the review. Both side-call points are done, and the branch is rebased:
|
|
Previous comment was from Opus 5.5, hope everything is fine now! |
|
thezelium resolve the merge conflicts in this pull request before the merge |
An experimental adapter for Factory Droid 0.233.0 on macOS, continuing #143 and related to
#85. Rebased onto
mainat0aa2aca, so it carries the Grok host (#148): Grok and Droid sit sideby side in every host list, and Grok is detected first.
What this adds
tokeneconomizer/trace/droid.pyreads a Droid session log: one JSONL per session under~/.factory/sessions/<encoded workspace>/, asession_startheader carrying the cwd, andClaude-shaped message records. It maps Droid's tool names (
Execute/Scriptto bash,Create/Writeto create,Edit/ApplyPatchto edit), counts prompts (Droid'suser_onlyand
llm_onlymessages and injected reminders are not prompts), and prices the session fromthe
tokenUsageDroid writes in<session id>.settings.jsonbeside the log. OnlytokenUsageis read:inclusiveTokenUsagealso counts child sessions, which are folded ontheir own.
tokeneconomizer/plugin/hosts.pygrows adroidhost on the Claude-shaped hook contract(
session_id,transcript_path,cwd,hook_event_name), and defaults on theHostbasefor the session-log details the history import reads, so
history.pyno longer hard-codesClaude Code's functions.
te --host droid history importlearns from earlier Droid sessions. It makes no model calland runs only into a repository with nothing folded yet.
plugin/.factory-plugin/plugin.json,plugin/hooks/droid-hooks.jsonandscripts/build_plugin.py --droidmake the installable bundle:SessionStart,UserPromptSubmit,PostToolUse,Stop,SessionEnd, and noPostToolUseFailure, whichDroid does not document. Micro-skills arrive as
hookSpecificOutput.additionalContext; theloop guard's still-stuck notice travels in the model's reply, because Droid documents no
systemMessage.droid_sidecall, off by default: the plugin's own calls rundroid exec, which writes asession log of its own, so they stay off unless
config set droid_sidecall=onorTE_DROID_SIDECALL=1. When on, the call starts throughprogram()like every other sidecall and passes
--only-tools Read, soReadis the only tool it may use; Droid has nosetting for a call with no tool at all. With them off,
microskills=envfactsstill learns andthe loop guard keeps its rule-based backstop.
docs/DROID.md, and the README, plugin README,CLAUDE.md,docs/PLUGIN.md,trace/README.mdandhook.pyentries -- all marked experimental, with the version andplatform that were tested.
Verification
python -m pytest tests experiments/tests: 1225 tests, 1205 passed, 19 skipped, 1 failed.The one failure is
experiments/tests/test_tooling.py::test_harness_emits_the_goals_toggle_and_the_cli_accepts_the_whole_mask,which fails the same way on unmodified
main(0aa2aca) on this machine: its 90 scodex --strict-configprobe times out against the locally installedcodex. It is not fromthis change. On
mainthe suite is 1202 tests with that same single failure; the 23 addedtests are this adapter's.
python -m pytest tests(what CI runs): 828 tests, 812 passed, 16 skipped, 0 failed, onPython 3.11 and 3.13.
hookSpecificOutput.additionalContext, and the model repeated it verbatim.droid exec --list-toolsreportsReadas the only tool allowed, MCP tools included (without--only-tools, the default read-only autonomy also allowsExecute, the web tools and everyMCP tool). A real side call asked to run shell commands, write a file and search the web
called no tool (its session log holds no
tool_use) and wrote nothing.Limits
suite has run, in CI.
droid execwith no tool at all(
--only-tools ""means no filter), and the call writes a session log of its own.Protect canonical index when aggregation output is explicit #65), and
droid exec --only-toolsis exec-only. The controller reports it as unavailablerather than writing a deny list.
AI disclosure: the adapter and its tests were produced with Factory Droid (an AI coding agent) at
my direction.
Closes #143
Related to #85