Repository navigation
Conversation
Registers a `devin` agent so `kontext hook --agent devin` can translate the Devin runtime's plugin hook payloads. Policy evaluation is unchanged: the adapter only decodes the incoming event and encodes the decision in the form that runtime reads from stdout. Decode maps `session_id` and `tool_use_id` straight through — Devin session IDs are already namespaced, so unlike the Codex adapter there is no prefix to add and no synthetic ID to mint. `tool_response` is captured so post-tool-use records an outcome rather than only an intent. `prompt_id` is deliberately dropped for now. It is a useful per-turn correlator but has no home on hook.Event, and folding it into ToolInput would be wrong: ToolInput becomes the Cedar evaluation context, so a synthetic attribute there would be policy-visible and would change the parameters hash recorded on every decision. Adding a first-class field is left for a follow-up. Encode emits a decision only for pre-tool-use; every other event is observation and emits no directive. Anything other than an explicit allow blocks, so an unrecognized decision value fails closed. An updated tool input is refused rather than applied. That runtime replaces tool_input wholesale instead of merging, so emitting a partial rewrite would silently drop every field the policy did not restate, and the post-tool-use hook would then observe only the rewritten input — leaving the original absent from the ledger. Nothing populates UpdatedInput today, so this is a guard against a future change rather than active logic. Adds hook.ErrSkipEvent for events an adapter recognizes but does not translate, honored by both hook runners. Some agent runtimes read a non-zero hook exit as "block the tool call", so reporting an unhandled lifecycle event as a decode failure would stall the agent on a legitimate signal. Unknown event names skip for the same reason; malformed input still fails, since that indicates a bug or a contract change rather than an event we chose not to handle. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
tumberger
marked this pull request as ready for review
July 30, 2026 22:55
Greptile SummaryAdds a Devin hook adapter and a shared sentinel for silently skipping non-actionable lifecycle events.
Confidence Score: 5/5The PR appears safe to merge, with no concrete blocking or independently actionable non-blocking issues identified. The Devin adapter follows the existing registry and hook-runner boundaries, limits directives to pre-tool-use events, fails closed on non-allow decisions, and keeps deliberate event skips distinct from malformed input. Important Files Changed
Sequence DiagramsequenceDiagram
participant Devin as Devin runtime
participant CLI as kontext hook
participant Adapter as Devin adapter
participant Policy as Policy runtime
Devin->>CLI: Hook payload on stdin
CLI->>Adapter: DecodeDevinEvent
alt Unsupported lifecycle event
Adapter-->>CLI: ErrSkipEvent
CLI-->>Devin: Exit 0 with no output
else Supported event
Adapter-->>CLI: hook.Event
CLI->>Policy: Evaluate or ingest event
Policy-->>CLI: hook.Result
CLI->>Adapter: EncodeDevinResult
alt PreToolUse denied
Adapter-->>Devin: "decision=block and reason"
else Allowed or observation event
Adapter-->>Devin: Empty directive object
end
end
Reviews (1): Last reviewed commit: "feat(agent): add a Devin hook adapter" | Re-trigger Greptile |
hostedAgentIdentity mapped only Claude and Cowork, so a Devin session exported an
empty agent_provider alongside agent "devin". The hosted ledger accepts that and
the session still shows its own name, but an empty provider reads downstream as
"provider unknown" rather than as the vendor it came from, and it cannot be
grouped with other endpoints from the same vendor.
Maps devin to ("cognition", "devin"), matching the existing shape where the
provider is the vendor and the agent is the product. Verified against a running
daemon: a Devin session now records ("cognition", "devin") beside a Claude
session's ("anthropic", "claude_code").
Codex is still unmapped and still exports an empty provider. That is pre-existing
and left alone here rather than changed as a drive-by.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Registers a
devinagent adapter sokontext hook --agent devincan translatethe Devin runtime's plugin hook payloads. Policy evaluation is untouched — the
adapter decodes the incoming event and encodes the decision in the form that
runtime reads from stdout. Same shape as the existing Codex adapter.
What's here
internal/hookruntime/devin.go— decode/encodeinternal/agent/devin/— registration, plus the blank import inmain.gohook.ErrSkipEvent— a skip contract honored by both hook runnersDecisions worth reviewing
prompt_idis dropped for now. It's a useful per-turn correlator, but it hasno home on
hook.Event, and folding it intoToolInputwould be wrong:ToolInputbecomes the Cedar evaluation context, so a synthetic attribute therewould be policy-visible and would change the parameters hash recorded on every
decision. A first-class field is a follow-up.
Only pre-tool-use carries a decision. Every other event is observation and
emits no directive. Anything other than an explicit allow blocks, so an
unrecognized decision value fails closed.
An updated tool input is refused, not applied. That runtime replaces
tool_inputwholesale rather than merging, so emitting a partial rewrite wouldsilently drop every field the policy didn't restate, and the post-tool-use hook
would then observe only the rewritten input — leaving the original absent from
the ledger. Nothing populates
UpdatedInputtoday, so this is a guard against afuture change rather than active logic. Input rewriting is worth supporting
properly later, with the pre-rewrite input as the authoritative ledger entry.
hook.ErrSkipEventis new, and touches shared runners. Some agent runtimesread a non-zero hook exit as "block the tool call", so reporting an unhandled
lifecycle event as a decode failure would stall the agent on a legitimate
signal. Both runners now exit successfully and emit nothing for a skipped event.
Unknown event names skip for the same reason; malformed input still fails, since
that indicates a bug or a contract change rather than an event we chose not to
handle. The change is additive — it only triggers on that specific sentinel, so
existing adapters are unaffected.
Follow-ups, deliberately not in this PR
Both change behavior for existing agents and deserve their own review:
recover()on the hook path. An unrecovered panic exits 2, which someruntimes read as "block" — so a nil-map bug becomes an opaque stall rather
than a visible failure. Worth fixing for every adapter, not just this one.
kontext hookuses the managed-observe lifecycle butexits 2 on any decode failure;
--modegets the runner that always writes adecision and exits 0, but bypasses the lifecycle and dials the Guard socket
with no autostart. Neither branch is quite right for a hook that must never
stall the agent.
Testing
New tests cover field mapping,
prompt_idexclusion from the Cedar context,large-integer precision, skip vs. failure classification, the block/allow encode
forms, observation events never emitting a directive, rewrite refusal, and
fail-closed behavior on an unknown decision.
go build ./...,go vet, andgofmtare clean. Fullgo test ./...passesexcept two pre-existing load-sensitive timing tests in
internal/guard/judge(
llama_server_test.go), which also fail onmainunder full-suite load andpass in isolation.