feat(kiro-ide): read the 1.x stdin hook context channel - #615
Conversation
…ce (2.5.4) Kiro IDE >= 1.0.1xx silently stopped executing the legacy .kiro.hook format, leaving dist/kiro-ide installs with no hooks layer (field-proven on 1.0.165). Ships both formats for coexistence: v2 JSON for IDE >= 1.0, legacy for pre-1.0. - Add nine .kiro/hooks/aidlc-*.json (v2 schema, PascalCase triggers) alongside the existing legacy .kiro.hook files; same adapter targets and commands - Coexistence safe: pre-1.0 ignores v2 files, 1.x+ ignores legacy (no double-fire) - Known boundary: Stop trigger is advisory-only on IDE (cannot block); stop and session-end fire for observability/audit, not enforcement - Known boundary: payload-dependent targets (audit-and-sensors, log-subagent) fire but no-op on 1.x until the stdin context channel lands (awslabs#615) - Five remaining hooks (mint, block, session-start, runtime-compile, sync-statusline) are payload-independent and fully functional on 1.x - Docs updated: kiro-ide guide context-channel caveat, README both formats, 14-claude-features Automation row; version 2.5.4 + CHANGELOG Addresses awslabs#555; the stdin channel fix for awslabs#543 follows in awslabs#615.
…ce (2.5.4) Kiro IDE >= 1.0.1xx silently stopped executing the legacy .kiro.hook format, leaving dist/kiro-ide installs with no hooks layer (field-proven on 1.0.165). Ships both formats for coexistence: v2 JSON for IDE >= 1.0, legacy for pre-1.0. - Add nine .kiro/hooks/aidlc-*.json (v2 schema, PascalCase triggers) alongside the existing legacy .kiro.hook files; same adapter targets and commands - Coexistence safe: pre-1.0 ignores v2 files, 1.x+ ignores legacy (no double-fire) - Known boundary: Stop trigger is advisory-only on IDE (cannot block); stop and session-end fire for observability/audit, not enforcement - Known boundary: payload-dependent targets (audit-and-sensors, log-subagent) fire but no-op on 1.x until the stdin context channel lands (awslabs#615) - Five remaining hooks (mint, block, session-start, runtime-compile, sync-statusline) are payload-independent and fully functional on 1.x - Docs updated: kiro-ide guide context-channel caveat, README both formats, 14-claude-features Automation row; version 2.5.4 + CHANGELOG Addresses awslabs#555; the stdin channel fix for awslabs#543 follows in awslabs#615.
c076013 to
db876c3
Compare
…ce (2.5.4) Kiro IDE >= 1.0.1xx silently stopped executing the legacy .kiro.hook format, leaving dist/kiro-ide installs with no hooks layer (field-proven on 1.0.165). Ships both formats for coexistence: v2 JSON for IDE >= 1.0, legacy for pre-1.0. - Add nine .kiro/hooks/aidlc-*.json (v2 schema, PascalCase triggers) alongside the existing legacy .kiro.hook files; same adapter targets and commands - Coexistence safe: pre-1.0 ignores v2 files, 1.x+ ignores legacy (no double-fire) - Known boundary: Stop trigger is advisory-only on IDE (cannot block); stop and session-end fire for observability/audit, not enforcement - Known boundary: payload-dependent targets (audit-and-sensors, log-subagent) fire but no-op on 1.x until the stdin context channel lands (awslabs#615) - Five remaining hooks (mint, block, session-start, runtime-compile, sync-statusline) are payload-independent and fully functional on 1.x - Docs updated: kiro-ide guide context-channel caveat, README both formats, 14-claude-features Automation row; version 2.5.4 + CHANGELOG Addresses awslabs#555; the stdin channel fix for awslabs#543 follows in awslabs#615.
* fix(kiro-ide): ship v2 hook schema alongside legacy for IDE coexistence (2.5.4) Kiro IDE >= 1.0.1xx silently stopped executing the legacy .kiro.hook format, leaving dist/kiro-ide installs with no hooks layer (field-proven on 1.0.165). Ships both formats for coexistence: v2 JSON for IDE >= 1.0, legacy for pre-1.0. - Add nine .kiro/hooks/aidlc-*.json (v2 schema, PascalCase triggers) alongside the existing legacy .kiro.hook files; same adapter targets and commands - Coexistence safe: pre-1.0 ignores v2 files, 1.x+ ignores legacy (no double-fire) - Known boundary: Stop trigger is advisory-only on IDE (cannot block); stop and session-end fire for observability/audit, not enforcement - Known boundary: payload-dependent targets (audit-and-sensors, log-subagent) fire but no-op on 1.x until the stdin context channel lands (#615) - Five remaining hooks (mint, block, session-start, runtime-compile, sync-statusline) are payload-independent and fully functional on 1.x - Docs updated: kiro-ide guide context-channel caveat, README both formats, 14-claude-features Automation row; version 2.5.4 + CHANGELOG Addresses #555; the stdin channel fix for #543 follows in #615. * fix(kiro-ide): address round-2 review - correctness, coverage, and docs (2.5.8) Addresses all 7 items from the leandrodamascena review: 1. Rebase + re-bump to 2.5.8 (2.5.7 taken by #639 which also touched the adapter file). CHANGELOG entry above 2.5.7, badge and version.ts set explicitly (the auto-merge trap). 2. Install commands use content-copy semantics (mkdir -p + cp -R src/. dst/) on both Kiro trees (IDE and CLI) in README.md, kiro-ide.md, and kiro-cli.md. The previous cp -r form nested .kiro inside .kiro on existing installs. 3. session-start.json trigger changed from UserPromptSubmit to SessionStart (the real once-per-session trigger on IDE 1.x). The legacy file keeps its per-prompt behavior unchanged for pre-1.0. 4. session-end v2 registration REMOVED: the IDE's Stop trigger fires at the end of every assistant turn, not at conversation close, so it was appending SESSION_ENDED between prompts. The legacy .kiro.hook stays (pre-1.0 behavior unchanged). Documented in kiro-ide.md and the CHANGELOG. 5. log-subagent adapter: empty payload (IDE 1.x) now returns null with a visible recordHookDrop instead of forwarding a fabricated SUBAGENT_COMPLETED row with Agent Type: unknown. Non-empty payload without an identity marker keeps the documented unknown fallback (t218 S3 still passes). 6. New t244-kiro-ide-hook-registrations.test.ts: table-driven structural test covering all 8 v2 JSONs x {version, trigger, matcher, command}, the absence of session-end.json, no unexpected extras, and all 9 legacy files present. Runs on both authored and dist trees. 7. Docs: aidlc-stop.json description corrected (advisory-only, not enforcement); README:115 now says cli.json is CLI-only (the IDE ignores it). Dist regenerated. Drift guard OK (5 harnesses), typecheck green, smoke+unit 176 files / 4350 assertions / 0 failures. * fix: round-4 doc fixes + t244->t245 rename * fix: round-4 - onboarding SESSION_ENDED claim, hook-payload.md dual-channel, t244->t245 rename, rebase to 2.5.10 --------- Co-authored-by: Camilo Soto <csotomon@amazon.com>
160c54e to
f5cab27
Compare
leandrodamascena
left a comment
There was a problem hiding this comment.
Two material issues remain:
- [P2] Make
log-subagentreachable for the documented Kiro IDE 1.x tool names (harness/kiro-ide/hooks/aidlc-log-subagent.json:7)
The registration matches only invoke_sub_agent, but the live evidence in #543 records subagent_<agent> on IDE 1.0.89, with no invoke_sub_agent event. Test N5 invokes the adapter directly with a synthetic invoke_sub_agent payload, bypassing the registration matcher entirely. Consequently, the changelog claim that subagent tracking is fully restored across Kiro IDE 1.x is not yet supported.
Please accept the observed completion forms, exclude subagent_response, and add a registration-level test using the captured tool names.
- [P2] Avoid the mandatory two-second delay on the supported 0.12 channel (
harness/kiro-ide/hooks/aidlc-kiro-adapter.ts:451-457)
On IDE 0.12, USER_PROMPT already contains the payload while stdin remains open indefinitely. The adapter nevertheless waits for the full stdin timeout before reading the available environment payload. This adds approximately two seconds to every audit-and-sensors and log-subagent invocation.
The existing N7 test confirms eventual fallback but does not constrain latency; it completed in 2.44 seconds, compared with 204 ms for the non-reading N6 path. Please use a non-empty USER_PROMPT immediately, or otherwise distinguish the legacy channel without imposing the timeout on every payload hook.
Non-blocking follow-ups:
aidlc adapter kiro-ide <target>still forcesinput = "", so the dispatcher route cannot consume the new 1.x stdin payload.- Narrow the statement that
tool_inputis “always empty” to the PostToolUse write/shell events actually captured; #543 records populated PreToolUse and delegation inputs on later 1.x builds. - Mark only the hook-registration portion of #555 as resolved; its agent/settings concerns remain open.
Verification: t218 passed 34 tests; t218 + t245 passed 63 tests in three consecutive runs; package parity passed for all harnesses.
f5cab27 to
66fe3fc
Compare
csotomon
left a comment
There was a problem hiding this comment.
Both P2 findings are fixed. Here is what happened and what changed:
1. log-subagent matcher — our mistake
We failed to validate the registration matcher against the actual tool names documented in #543. The issue explicitly states that completions arrive as subagent_aidlc-product-lead-agent (not invoke_sub_agent) and that subagent_response must be excluded. We took the #603 diff as the specification instead of cross-referencing the linked issue evidence, and our test (N5) invoked the adapter directly — bypassing the registration entirely — so the structural test (t245) pinned the wrong value without catching it.
Fix: matcher is now ^(subagent_.+-agent|invoke_sub_agent)$. The adapter also applies the same gate internally (defense-in-depth for direct/dispatcher invocations). New tests:
- t245: registration-level regex validation against all captured tool names
- N5a:
subagent_<agent>accepted, identity recovered - N5b:
invoke_sub_agentcompat,subagent_responseexcluded
2. Mandatory 2s delay on 0.12 — our mistake
We accepted N7's eventual fallback (it asserted correctness but not latency) without noticing that probing stdin first on 0.12 — where it never closes — imposed a deterministic 2s wait on every payload hook. The test completed in ~2.4s and we treated that as success.
Fix: a non-empty USER_PROMPT is now consumed immediately, without touching stdin. The timeout race fires only when USER_PROMPT is empty (the 1.x channel). N7 now asserts elapsedMs < 1900 with a held-open stdin and passes at ~330ms.
Non-blocking items (included in this push)
Dispatcher route
The aidlc adapter kiro-ide <target> route in core/tools/aidlc.ts now mirrors the adapter entry point's dual-generation contract instead of forcing input = "". This was introduced by #639 (merged after #603 was closed) and became semantically stale after this PR's stdin acquisition fix. N8 and N9 pin it.
tool_input scope
Narrowed to "captured PostToolUse write/shell events" in docs, comments, and the hook-payload reference. The follow-up observation from #543 (populated PreToolUse inputs at 1.0.116+, delegation inputs at 1.0.138+) is now cited rather than contradicted.
#555 roadmap claim
Corrected: only the hook-registration portion is marked resolved; the agent/settings surfaces remain open.
Verification
package --check: all five harnesses in synctypecheck+lint: green- Full default suite: 280 files / 5729 assertions; 3 failures are pre-existing upstream (t19: expired AWS creds; t66/t89: #647's
claim-sourcessensor broke its own golden fixture — untouched by this PR) - t218: 75 tests / 259 assertions (including the 5 new cases)
- t245: 35 tests / all green with registration-level matcher validation
|
CI note: the full default suite shows 3 pre-existing failures on this branch that also reproduce on bare
None of the 3 failures involve files changed by this PR. Happy to open an issue for the t66/t89 fixture drift if helpful. |
|
Reviewed at head f5cab27. The mechanism is sound and well-guarded: stdin-first acquisition raced against a 2s timeout, gated to the two payload-dependent targets, falling back to P2-1: the dispatcher's
|
Close the Kiro IDE 1.x payload boundary left by the v2 hook-schema migration, so artifact audit rows, sensor firing, and subagent tracking work on current IDE builds without regressing pre-1.0. - Consume a non-empty legacy USER_PROMPT immediately; read the 1.x stdin payload only when it is empty, bounded by a broken-channel ceiling - Record SUBAGENT_COMPLETED again on 1.x: the registration matcher reaches any delegate name (that generation sends subagent_<agent>, not invoke_sub_agent) while the adapter drops the empty subagent_response shell - Record a visible hook drop when neither channel yields a payload, so a broken context channel surfaces in --doctor instead of exiting silently - Apply the same dual-channel contract to the public aidlc adapter kiro-ide dispatcher route - Preserve both field spellings and the explicit toolSuccess:false write guard - Add the AIDLC_IDE_STDIN_TIMEOUT_MS seam so latency coverage asserts "never probed stdin" deterministically rather than against a tight millisecond budget - Restore held-open-stdin coverage for the agentStop targets and pin the dispatcher route - Scope the empty tool-input documentation to captured PostToolUse events and state that awslabs#543/awslabs#555 remain open pending manual closure Refs awslabs#543, awslabs#459.
66fe3fc to
f8d4f52
Compare
|
@apackeer thanks — you reviewed at P2-2 — I adopted your matcher over mine, and you were right to push backI had shipped You also called the trap precisely: broadening alone forwards the P2-3 — fixed, and it was a real asymmetryConfirmed: P3-2 — restoredYou were right that deleting 12b lost the #639 surface and that P3-1 / your latency nit — fixed togetherThe 2s cost on 0.12 is gone: a non-empty Nits
On your t66/t89 observationI verified this in a clean Still open, and I am not claiming otherwiseNo real delegate completion captured on 1.0.165. The matcher now covers either naming, but your ask to settle the naming question live remains unmet — I would rather leave it explicit than assert coverage I have not observed. One more thing worth flagging since you checked the slot on 2.5.11: 2.5.12 is contested — #646 and #535 also bump to it. Per the changelog policy the second to merge re-bumps. |
|
Re-reviewed at head
Two findings remain, both introduced by this round's changes. P2 - the new tool-name gate makes
|
|
@apackeer thanks for the thorough re-review of Broken-channel drop reachabilityConfirmed the regression: The empty-context check now runs before the tool-name gate. N10 exercises both payload-dependent targets, Identity fallback from
|
apackeer
left a comment
There was a problem hiding this comment.
Re-reviewed at head 24678685. All four of my round-3 findings are genuinely fixed, and the identity fallback you adopted (rather than deferring) is the right call. Verified independently on this head:
- Broken-channel drop reachability - the empty-context check now runs before the tool-name gate. Re-ran my round-3 repro (held-open stdin, empty
USER_PROMPT, 500ms ceiling):log-subagentnow recordslog-subagent: empty hook context ... SUBAGENT_COMPLETED not recordedandaudit-and-sensorsits own drop; both exit 0 in ~540ms. N10 looping both payload targets closes the drift-apart hole I flagged (grep "empty hook context" tests/now hits). - Identity fallback - probed all five shapes:
subagent_aidlc-developer-agentwith markerless prose lands as**Agent Type**: aidlc-developer-agent; a prose marker still wins when both are present; thesubagent_responseshell is still excluded; 0.12invoke_sub_agentwithout a marker staysunknown; and the degeneratesubagent_(empty suffix) falls back tounknownrather than an empty string. N5a pinning the markerless domain-expert case is exactly the regression I asked for. - Runtime validation - all four non-string
tool_nameshapes (7,{"a":1},["x"],true) now exit 0 with a visiblemalformed hook context fields (toolName)drop on the 1.x direct entry, the dispatcher route, and the 0.12 channel. ThetoolSuccessbypass is closed:"false"and0no longer audit the write (drop recorded instead), booleanfalsestill drops it per #417, andnullstill audits on both field spellings per the absent-value contract T4 now pins. Non-record roots ([1,2],"hello",42) drop asmalformed hook context fields (payload)instead of the old silentide = {}. - CHANGELOG - the 1.0.165 claim is now scoped to artifact audit + sensor firing, with subagent tracking resting on the #543 1.0.89-1.0.138 evidence, matching the PR body.
Gates on my side: package --check OK across all five harnesses, bun run typecheck exit 0, t218+t245+t68 = 3 files / 81 tests / 0 failures, plus a targeted run of the five new/changed cases (N5a, N10, N12, T3, T4) - 5 pass. PR CI is green on this head.
One non-blocking observation, no action needed: a malformed toolArgs alone (e.g. tool_input as a string) now drops the whole event, although the adapter never reads toolArgs downstream of normalization. Every live capture shows an object there and the drop is visible, so strict-and-visible is a fine trade; just noting the strictness exceeds consumption in case a future IDE build stringifies inputs.
Approving. Merge-slot note: 2.5.12 is also claimed by #646 and #535; whichever merges second re-bumps and renames its heading per the CHANGELOG policy.
leandrodamascena
left a comment
There was a problem hiding this comment.
The previous blockers are fixed, and the focused tests and CI are green. One audit-integrity issue remains.
Medium: Prefer the structured subagent identity over agent-authored prose
Location: harness/kiro-ide/hooks/aidlc-kiro-adapter.ts:267-275,440-446
For Kiro IDE 1.x, the tool name already carries the delegate identity as subagent_<agent>. However, extractAgentIdentity() prefers an **Agent:** or **Reviewer:** line written by the subagent itself.
I reproduced this payload:
{
"tool_name": "subagent_aidlc-developer-agent",
"tool_response": "**Agent:** aidlc-product-lead-agent\n\nDone"
}It records:
Agent Type: aidlc-product-lead-agent
This allows incorrect or prompt-injected result prose to misattribute SUBAGENT_COMPLETED audit events despite a more authoritative platform-provided identity.
For subagent_* events, derive the identity from tool_name. Use the prose marker only for the legacy invoke_sub_agent form, where no structured identity is available. Please also add a conflicting-identity regression test.
Verification
- Focused adapter, registration, and version tests: 81 passed.
- Package parity: passed across all harnesses.
- PR CI: green.
- Local typecheck was unavailable because
bun-typesis not installed. - Worktree remained clean.
- Reorder `extractAgentIdentity()` to check `subagent_<agent>` tool name before result prose markers, making platform-provided identity authoritative - Update identity precedence logic to prevent prompt-injected `**Agent:**` lines in agent-written output from misattributing SUBAGENT_COMPLETED rows to wrong personas - Clarify audit-integrity rationale in code comments: structured identity from tool name wins over agent-authored prose on IDE 1.x - Fallback to `**Reviewer:**` / `**Agent:**` markers remains the only signal for 0.12 `invoke_sub_agent` shape and degenerate `subagent_` suffixes - Update `docs/reference/kiro-ide-hook-payload.md` to document identity precedence and audit safety guarantees - Update CHANGELOG with detailed explanation of agent identity sourcing and audit row attribution behavior
|
@leandrodamascena fixed at head What was wrongWhen I adopted the tool-name fallback in round 3, I bolted it onto the end of Your payload on the old head recorded The fix
I wrote the reason into the function comment and the Your reproduction on this head: Tests
The existing pins still hold and now cover the complementary directions: S1/S2/S3 keep the 0.12 prose contract and the genuine Docs
One thing worth surfacing, not hiding@apackeer this reverses the ordering you suggested and verified by hand at Verification
On your typecheck note: Still unclaimed: no live delegate completion captured on IDE 1.0.165. This change reduces how much that gap costs — identity no longer depends on a delegate's self-reporting — but the naming question stays open, and both the PR body and CHANGELOG keep saying so.
|
|
LGTM |
leandrodamascena
left a comment
There was a problem hiding this comment.
The previous blocker is fixed. The structured subagent_<agent> identity now correctly takes precedence over agent-authored prose, with regression coverage for conflicting identities.
No additional findings.
Verification:
- t218: 45 passed
- t245: 31 passed
- t68: 7 passed
- Package parity: clean across all harnesses
- PR CI: green
- Clean merge with current
origin/v2 - Worktree remained clean
Local typecheck/lint could not run because development dependencies are absent, but both pass in CI.
Approving.
Summary
Closes the payload boundary #614 left open on Kiro IDE 1.x. That generation delivers hook context as snake_case JSON on stdin with an empty
USER_PROMPT, while 0.12 uses a camelCaseUSER_PROMPTand leaves stdin open forever. The adapter now serves both without penalising either, so artifact audit rows, sensor firing, and subagent tracking work again on current IDE builds.Behaviour
Channel acquisition. A non-empty
USER_PROMPTis consumed immediately (0.12). Only when it is empty does the adapter read stdin (1.x), bounded by a broken-channel ceiling. Both field spellings are accepted; the explicittoolSuccess: falsefailed-write guard (#417) is preserved. Acquisition is gated toaudit-and-sensorsandlog-subagent— every other target, including the per-tool-callblockfloor, touches neither channel.Subagent completions. IDE 1.x sends
subagent_<agent>, not the 0.12invoke_sub_agent, each preceded by an emptysubagent_responseshell. The registration matcher is therefore broad (^(subagent_.+|invoke_sub_agent)$) so any delegate name reaches the adapter — including fork-added agents that do not follow theaidlc-*-agentconvention — and the adapter dropssubagent_response, which carries prose but no identity and would otherwise be logged asAgent Type: unknown. That split is deliberate: the direct and dispatcher entry points bypass the matcher entirely.Broken-channel visibility. When neither channel yields a payload,
audit-and-sensorsnow records a visible hook drop instead of exiting silently, so/aidlc --doctorcan surface exactly the decay this harness exists to eliminate.Dispatcher parity. The public
aidlc adapter kiro-ide <target>route follows the same contract; previously it forcedinput = "".Test seam
AIDLC_IDE_STDIN_TIMEOUT_MSoverrides the stdin ceiling. The latency cases raise it well above any plausible CI scheduling delay and assert the process finishes far inside that window, so "did this path probe stdin at all?" is decided by a wide margin rather than a millisecond budget near the production ceiling. Verified the seam is genuinely honoured: a payload target parks at exactly the ceiling given (557 ms at 500, 4043 ms at 4000) whilemintexits in 48 ms.Coverage
USER_PROMPTwins without probing stdinsubagent_<agent>accepted and identity recovered;invoke_sub_agentcompat;subagent_responsedroppedstop/session-enddeleted with 12b (#639)Validation
bun scripts/package.ts --check: clean across all five harnessesbun run typecheck,bun run lint: cleanTwo integration files fail on this branch and fail identically on bare
upstream/v2— verified in a clean worktree (15 failures there too):t66andt89break on #647's newclaim-sourcessensor, whose golden export fixture and sensor-resolution fixtures were not updated. Neither file is touched here.t19is a live preflight that only needs authenticated credentials; it passes 2/2 once a valid profile is used.Not verified here
A real delegate completion on IDE 1.0.165 has not been captured, so the
subagent_<agent>naming rests on the 1.0.89-1.0.138 evidence in #543. The matcher is broad enough to cover either naming, but the live confirmation is still open.Upgrade
mkdir -p your-project/.kiro && cp -R dist/kiro-ide/.kiro/. your-project/.kiro/Version
Bumps to 2.5.12. Note that #646 and #535 also claim 2.5.12; per the repo changelog policy the second PR to merge rebases and re-bumps.
References
Fixestrailer on a PR targetingv2does not auto-closeharness/kiro-ide/) ships CLI-format surfaces — JSON agents,settings/cli.json, and legacy.kiro.hookfiles — that Kiro IDE 1.0 does not read (agents/settings) or flags for migration (hooks) #555's agent/settings surfaces remain open