Apologies for the churn first. My earlier reports (#417/#459, filed against 0.12.333) framed the payload channel as USER_PROMPT, and PR #471 (2.2.17) built the fix against exactly that — the Kiro IDE adapter reads process.env.USER_PROMPT. On the current IDE build (1.0.89) the channel is the opposite: the payload arrives on stdin, and USER_PROMPT is empty. So the shipped adapter reads "", and the audit/sensor and log-subagent hooks go silent again. The adapter is byte-identical from 2.2.17 through 2.2.19, so this is the state of the latest release.
I re-verified live on 2.2.19 / IDE 1.0.89 (probe hook + a full workflow run, AIDLC_HOOK_DEBUG on).
Shared root cause
- Channel = stdin JSON;
USER_PROMPT is empty. The stdin JSON channel is the IDE 1.x hook contract (1.0.52 onward per the Kiro docs); I tested on 1.0.89. The adapter's own hookDebug logs hasUserPrompt=false on every PostToolUse dispatch, so the shipped process.env.USER_PROMPT read (aidlc-kiro-adapter.ts:73) leaves ide = {} and every branch degrades to a no-op.
- Field names are snake_case
tool_name / tool_input / tool_response.
tool_input is {} on every event; the write path / agent identity live only in tool_response prose (Created the <PATH> file. / Replaced text in <PATH>), which extractWrittenPath (:161-169) already matches verbatim. So only the acquisition step reads the wrong channel.
Fix 1 — acquisition (fixes #417 audit/sensor, and unblocks #459)
Read stdin first, fall back to USER_PROMPT, map onto the {tool_name, tool_input, tool_response} shape the downstream already parses:
// aidlc-kiro-adapter.ts — replace the USER_PROMPT-only acquisition block
let ide: IdeHookContext = {};
{
// 1) stdin first (IDE 1.x). The IDE may open stdin but not close it,
// so race the read against a short timeout to avoid hanging.
let stdinText = "";
if (!process.stdin.isTTY) {
try {
stdinText = await Promise.race([
Bun.stdin.text(),
new Promise<string>((r) => setTimeout(() => r(""), 2000)),
]);
} catch { stdinText = ""; }
}
if (stdinText.trim().length > 0) {
try {
const s = JSON.parse(stdinText); // { tool_name, tool_input, tool_response }
ide = {
toolName: s.tool_name,
toolArgs: s.tool_input,
toolResult: typeof s.tool_response === "string" ? s.tool_response : "",
toolSuccess: undefined, // stdin carries no success flag
};
} catch { ide = {}; }
} else {
// 2) USER_PROMPT env fallback (IDE 0.12.x).
const raw = process.env.USER_PROMPT ?? "";
if (raw.length > 0) { try { ide = JSON.parse(raw); } catch { ide = {}; } }
}
}
(Requires the enclosing dispatch to be async / top-level await; the timeout race keeps it from hanging when the IDE holds stdin open.) Reading stdin first with the USER_PROMPT fallback covers 1.0.52+ without dropping the 0.12.x contract. With this in place on 2.2.19, a live 1.0.89 run recorded ARTIFACT_CREATED / ARTIFACT_UPDATED / SENSOR_FIRED / SENSOR_PASSED — the first time those land in the audit on Kiro IDE for me. This is the #417 fix.
Fix 2 — subagent tool name (#459 log-subagent), on top of Fix 1
With the payload arriving, SUBAGENT_COMPLETED can be emitted — but PR #471 filters on invoke_sub_agent, and that is not the name the IDE sends for aidlc delegates on 1.0.89. In the same clean run every delegate completion arrived under subagent_<agentname>:
subagent_aidlc-product-lead-agent (reviewer, ×4 across two stages), each preceded by an empty subagent_response shell ("Response recorded.") that must be dropped.
invoke_sub_agent did not appear at all.
The identity is recovered from the **Reviewer:** … self-identification line in tool_response (tool_input is {}), giving Agent Type: aidlc-product-lead-agent in the audit — not unknown. I kept the hook matcher broad (^(subagent_.+|invoke_sub_agent)$) and did the subagent_response exclusion in the adapter gate. This is what I ran on 1.0.89, and it recorded both reviewer completions correctly:
// adapter log-subagent gate — accept both delegate forms, drop the empty shell
const tn = ide.toolName ?? "";
const isSubagentCompletion =
tn === "invoke_sub_agent" ||
(tn.startsWith("subagent_") && tn !== "subagent_response");
Fix 2 only works on top of Fix 1 (without the payload there is nothing to match), so both are needed.
Not affected: #456 (runtime-compile)
For completeness: #456 does not regress on 1.0.89. PR #471 fixed it payload-free — gated on the audit tail with an mtime idempotency guard, not on the (empty) tool_input.command — so it does not depend on this channel. In the clean run I saw transition-gate hasTransition=true recompiling after transitions and the idempotency skip when current, with zero audit empty past the first pre-audit moment.
Apologies for the churn first. My earlier reports (#417/#459, filed against 0.12.333) framed the payload channel as
USER_PROMPT, and PR #471 (2.2.17) built the fix against exactly that — the Kiro IDE adapter readsprocess.env.USER_PROMPT. On the current IDE build (1.0.89) the channel is the opposite: the payload arrives on stdin, andUSER_PROMPTis empty. So the shipped adapter reads"", and the audit/sensor and log-subagent hooks go silent again. The adapter is byte-identical from 2.2.17 through 2.2.19, so this is the state of the latest release.I re-verified live on 2.2.19 / IDE 1.0.89 (probe hook + a full workflow run,
AIDLC_HOOK_DEBUGon).Shared root cause
USER_PROMPTis empty. The stdin JSON channel is the IDE 1.x hook contract (1.0.52 onward per the Kiro docs); I tested on 1.0.89. The adapter's ownhookDebuglogshasUserPrompt=falseon every PostToolUse dispatch, so the shippedprocess.env.USER_PROMPTread (aidlc-kiro-adapter.ts:73) leaveside = {}and every branch degrades to a no-op.tool_name/tool_input/tool_response.tool_inputis{}on every event; the write path / agent identity live only intool_responseprose (Created the <PATH> file./Replaced text in <PATH>), whichextractWrittenPath(:161-169) already matches verbatim. So only the acquisition step reads the wrong channel.Fix 1 — acquisition (fixes #417 audit/sensor, and unblocks #459)
Read stdin first, fall back to
USER_PROMPT, map onto the{tool_name, tool_input, tool_response}shape the downstream already parses:(Requires the enclosing dispatch to be
async/ top-levelawait; the timeout race keeps it from hanging when the IDE holds stdin open.) Reading stdin first with theUSER_PROMPTfallback covers 1.0.52+ without dropping the 0.12.x contract. With this in place on 2.2.19, a live 1.0.89 run recordedARTIFACT_CREATED/ARTIFACT_UPDATED/SENSOR_FIRED/SENSOR_PASSED— the first time those land in the audit on Kiro IDE for me. This is the #417 fix.Fix 2 — subagent tool name (#459 log-subagent), on top of Fix 1
With the payload arriving,
SUBAGENT_COMPLETEDcan be emitted — but PR #471 filters oninvoke_sub_agent, and that is not the name the IDE sends for aidlc delegates on 1.0.89. In the same clean run every delegate completion arrived undersubagent_<agentname>:subagent_aidlc-product-lead-agent(reviewer, ×4 across two stages), each preceded by an emptysubagent_responseshell ("Response recorded.") that must be dropped.invoke_sub_agentdid not appear at all.The identity is recovered from the
**Reviewer:** …self-identification line intool_response(tool_inputis{}), givingAgent Type: aidlc-product-lead-agentin the audit — notunknown. I kept the hook matcher broad (^(subagent_.+|invoke_sub_agent)$) and did thesubagent_responseexclusion in the adapter gate. This is what I ran on 1.0.89, and it recorded both reviewer completions correctly:Fix 2 only works on top of Fix 1 (without the payload there is nothing to match), so both are needed.
Not affected: #456 (runtime-compile)
For completeness: #456 does not regress on 1.0.89. PR #471 fixed it payload-free — gated on the audit tail with an mtime idempotency guard, not on the (empty)
tool_input.command— so it does not depend on this channel. In the clean run I sawtransition-gate hasTransition=truerecompiling after transitions and the idempotency skip when current, with zeroaudit emptypast the first pre-audit moment.