feat(workstream): add a v2-engine Kiro CLI managed-workstream adapter

Add `ai-memory run kiro` (alias `kiro-cli`) for the AWS Kiro CLI (#356),
deliberately scoped to the binary's default v2 agent engine.

Cross-engine resume is prevented by construction, not detection: Kiro v3
sessions occupy a separate id space the v2 engine cannot resume (the
resume hint printed after a v3 session silently starts a fresh v2
session unless --v3 is added), and the v3 persisted-session format is
not publicly documented — so any `--v3`, `--mode`, or non-v2
`--agent-engine` selection turns the whole invocation into an unmanaged
passthrough with argv byte-identical. Headless `--no-interactive` runs
persist to Kiro's v1 SQLite store rather than the v2 session files this
adapter reads, so they pass through too, as do the one-shot
list/delete/model flags and every root utility subcommand except `chat`
(list verified against kiro-cli 2.16.0 --help-all).

Launch planning: session ids are server-assigned, so a fresh launch
injects nothing and the session is linked by the session-start hook or
discovered post-exit; a returning session appends `--resume-id <id>`
after user arguments (accepted at the root and on `chat`, verified on
the 2.16.0 binary). Explicit `-r/--resume`, `--resume-id`,
`--resume-picker`/`--list` selections always win. Kiro's `-v` is
verbose, not version, and stays managed.

Discovery/import: the interactive store is flat —
`$KIRO_HOME/sessions/cli/<uuid>.json` metadata + `<uuid>.jsonl` event
stream — and the adapter matches checkouts on the metadata `cwd`,
requiring the metadata session_id to agree with the file stem. The
parser imports the versioned v1 envelope (Prompt / AssistantMessage
with toolUse parts / ToolResults), ignores unknown record kinds for
forward compatibility, and annotates unknown envelope versions and
non-text parts as explicit losses. The stream is not verified
append-only, so it shares Kimi's rewrite tolerance: a prefix-hash
cursor that resets and replays on in-place rewrites, with stable
line-hash record ids deduplicating server-side.

`--yolo` maps to the official v2 flag `--trust-all-tools`, treats
`-a`/`--trust-tools` as already-satisfying (an explicit narrower trust
set is never widened), and maps nothing on non-v2 engines — v3 replaced
the flag with permissions.yaml — with a stderr notice.

Kiro joins the automatic bare-`run` pool (client and server side) and
the deterministic phase of the acceptance script, including a
byte-identical `--v3` passthrough check; the real-harness phase skips
kiro with an explanation because scripted headless turns cannot write
the store the adapter reads. Session-file shapes derive from public
kiro-cli 1.29.x references and are documented as pending revalidation
on a live logged-in install; the CLI argument contract was verified
against kiro-cli 2.16.0 locally.

Closes #356.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LJvj7D7czyZzqk4pDXgLxY
This commit is contained in:
Samir Hanna Verza
2026-08-04 13:07:51 -03:00
committed by AkitaOnRails
co-authored by Claude Fable 5
parent 70d13f8c4d
commit 4429adc34c
7 changed files with 334 additions and 9 deletions
+31
View File
@@ -217,6 +217,37 @@ case "${AI_MEMORY_ACCEPTANCE_FAKE_MODE:-argv}" in
packet=$(jq -r '.options.global_context_paths[-1]' "$CRUSH_GLOBAL_CONFIG/crush.json")
cp "$packet" "$AI_MEMORY_ACCEPTANCE_CRUSH_PACKET_LOG"
;;
kiro)
printf '%s\n' "$@" >"$AI_MEMORY_ACCEPTANCE_ARGV_LOG"
# Honor `--resume-id <id>` (resume); a fresh launch mints its own id
# because Kiro CLI session ids are server-assigned UUIDs.
session_id=""
previous_arg=""
for arg in "$@"; do
if [ "$previous_arg" = --resume-id ]; then
session_id=$arg
fi
previous_arg=$arg
done
if [ -z "$session_id" ]; then
session_id=$(cat /proc/sys/kernel/random/uuid)
fi
# Real layout: flat store, one `<uuid>.json` metadata + `<uuid>.jsonl`
# event-stream pair per session; discovery must read the metadata cwd.
store="${KIRO_HOME:?kiro fake mode requires KIRO_HOME}/sessions/cli"
mkdir -p "$store"
stream="$store/$session_id.jsonl"
if [ ! -f "$store/$session_id.json" ]; then
printf '{"session_id":"%s","cwd":"%s","created_at":"2026-08-01T10:00:00Z","updated_at":"2026-08-01T10:00:00Z","title":"acceptance","session_state":{"version":"v1","conversation_metadata":{}}}\n' \
"$session_id" "$PWD" >"$store/$session_id.json"
: >"$stream"
fi
sentinel=${AI_MEMORY_ACCEPTANCE_SENTINEL:-AMWS-FAKE-KIRO}
printf '{"version":"v1","kind":"Prompt","data":{"message_id":"m-%s-u","content":[{"kind":"text","data":"%s"}],"meta":{"timestamp":%s}}}\n' \
"$(date +%s)" "$sentinel" "$(date +%s)000" >>"$stream"
printf '{"version":"v1","kind":"AssistantMessage","data":{"message_id":"m-%s-a","content":[{"kind":"text","data":"%s reply"}],"meta":{"timestamp":%s}}}\n' \
"$(date +%s)" "$sentinel" "$(date +%s)000" >>"$stream"
;;
kimi)
printf '%s\n' "$@" >"$AI_MEMORY_ACCEPTANCE_ARGV_LOG"
# Honor `--session <id>` (resume); a fresh launch mints its own id