feat(go): durable transaction engine and Kiro IDE 1.0 adapter

Split the monolithic transaction into focused modules: append-only
crash-recoverable journal under ~/.elydora/transactions with owner
tokens and per-transaction namespaces, staged atomic publication
(stage .pending, fsync, rename, fsync), platform-specific atomic
replacement (MoveFileEx, renameat2, renamex_np), private-directory
hardening per OS, lock-guarded recovery, and workspace cleanup that
removes only transaction-created empty directories.

Add the Kiro IDE 1.0 plugin: strict v1 hook schema with one enabled
PreToolUse guard and PostToolUse audit pair bound to the active
workspace, agent-to-workspace identity binding in runtime config, and
exact migration of the legacy elydora-audit.kiro.hook contract.

Gates: go test ./... and -race pass, go vet clean, govulncheck reports
zero reachable vulnerabilities, git diff --check clean.
This commit is contained in:
ZacharyZhang-NY committed 2026-07-21 19:02:57 -04:00
1 parent dd72793cdb
commit eb41b991bf
70 files changed
+8182 -358

No files matched your search

+460
View File
@@ -0,0 +1,460 @@
package plugins
import (
"bytes"
"encoding/json"
"fmt"
"math"
"reflect"
)
const (
kiroIdeAgentKey = "kiroide"
kiroIdeConfigFile = "elydora-audit.json"
kiroIdeLegacyFile = "elydora-audit.kiro.hook"
kiroIdeGuardScript = "guard.js"
kiroIdeAuditScript = "hook.js"
kiroIdeGuardName = "elydora-guard"
kiroIdeAuditName = "elydora-audit"
kiroIdeHookTimeout = float64(10)
kiroIdeGuardDescription = "Block tool use when the Elydora agent is frozen"
kiroIdeAuditDescription = "Record tool use in the Elydora audit trail"
)
var kiroIdeTriggers = stringSet(
"SessionStart",
"Stop",
"PreToolUse",
"PostToolUse",
"PreTaskExec",
"PostTaskExec",
"UserPromptSubmit",
"PostFileCreate",
"PostFileSave",
"PostFileDelete",
)
type kiroIdeDocument struct {
exists bool
filePath string
root map[string]any
hooks []map[string]any
snapshot *managedFileSnapshot
}
type kiroIdeLegacyDocument struct {
filePath string
snapshot *managedFileSnapshot
contract *kiroIdeRuntimeContract
}
type kiroIdeRenderedDocument struct {
document *kiroIdeDocument
changed bool
next []byte
remove bool
}
type kiroIdeRuntimeContract struct {
agentID string
guardPath string
auditPath string
}
type kiroIdeHookSpecification struct {
description string
trigger string
scriptName string
}
func kiroIdeSpecification(name any) (kiroIdeHookSpecification, bool) {
switch name {
case kiroIdeGuardName:
return kiroIdeHookSpecification{
kiroIdeGuardDescription, "PreToolUse", kiroIdeGuardScript,
}, true
case kiroIdeAuditName:
return kiroIdeHookSpecification{
kiroIdeAuditDescription, "PostToolUse", kiroIdeAuditScript,
}, true
default:
return kiroIdeHookSpecification{}, false
}
}
func cloneKiroIdeObject(value map[string]any) map[string]any {
clone := make(map[string]any, len(value))
for key, item := range value {
clone[key] = item
}
return clone
}
func validateKiroIdeAction(value any, label string) error {
action, ok := value.(map[string]any)
if !ok || action == nil {
return fmt.Errorf("%s action must be an object", label)
}
switch action["type"] {
case "command":
command, ok := action["command"].(string)
if !ok || command == "" {
return fmt.Errorf("%s command action requires a non-empty command", label)
}
case "agent":
prompt, ok := action["prompt"].(string)
if !ok || prompt == "" {
return fmt.Errorf("%s agent action requires a non-empty prompt", label)
}
default:
return fmt.Errorf(
`%s action has unsupported type %q`,
label,
fmt.Sprint(action["type"]),
)
}
return nil
}
func validateKiroIdeHook(value any, index int, label string) (map[string]any, error) {
hook, ok := value.(map[string]any)
if !ok || hook == nil {
return nil, fmt.Errorf("%s hooks[%d] must be an object", label, index)
}
hookLabel := fmt.Sprintf("%s hooks[%d]", label, index)
name, ok := hook["name"].(string)
if !ok || name == "" {
return nil, fmt.Errorf("%s requires a name", hookLabel)
}
trigger, ok := hook["trigger"].(string)
if !ok {
return nil, fmt.Errorf("%s requires a trigger", hookLabel)
}
if _, supported := kiroIdeTriggers[trigger]; !supported {
return nil, fmt.Errorf("%s has unsupported trigger %q", hookLabel, trigger)
}
if value, exists := hook["description"]; exists {
if _, ok := value.(string); !ok {
return nil, fmt.Errorf("%s description must be a string", hookLabel)
}
}
if value, exists := hook["matcher"]; exists {
if _, ok := value.(string); !ok {
return nil, fmt.Errorf("%s matcher must be a string", hookLabel)
}
}
if value, exists := hook["timeout"]; exists {
timeout, ok := value.(float64)
if !ok || math.IsNaN(timeout) || math.IsInf(timeout, 0) ||
timeout < 0 || timeout > 9007199254740991 || math.Trunc(timeout) != timeout {
return nil, fmt.Errorf("%s timeout must be a non-negative integer", hookLabel)
}
}
if value, exists := hook["enabled"]; exists {
if _, ok := value.(bool); !ok {
return nil, fmt.Errorf("%s enabled must be a boolean", hookLabel)
}
}
if err := validateKiroIdeAction(hook["action"], hookLabel); err != nil {
return nil, err
}
return hook, nil
}
func parseKiroIdeDocument(
filePath string,
snapshot *managedFileSnapshot,
) (*kiroIdeDocument, error) {
if snapshot == nil {
return &kiroIdeDocument{
filePath: filePath,
root: map[string]any{},
hooks: []map[string]any{},
}, nil
}
label := fmt.Sprintf("Kiro IDE hooks at %s", filePath)
root, err := decodeStrictJSONObject(snapshot.contents, label)
if err != nil {
return nil, err
}
if root["version"] != "v1" {
return nil, fmt.Errorf(`Kiro IDE hooks version must be "v1": %s`, filePath)
}
values, ok := root["hooks"].([]any)
if !ok {
return nil, fmt.Errorf(`Kiro IDE hooks field "hooks" must be an array: %s`, filePath)
}
hooks := make([]map[string]any, 0, len(values))
for index, value := range values {
hook, err := validateKiroIdeHook(value, index, "Kiro IDE hooks")
if err != nil {
return nil, err
}
hooks = append(hooks, hook)
}
return &kiroIdeDocument{
exists: true, filePath: filePath, root: root, hooks: hooks,
snapshot: snapshot,
}, nil
}
func exactKiroIdeKeys(value map[string]any, keys ...string) bool {
if len(value) != len(keys) {
return false
}
for _, key := range keys {
if _, exists := value[key]; !exists {
return false
}
}
return true
}
func ownedKiroIdeReference(
hook map[string]any,
) (*kiroIdeRuntimeReference, error) {
specification, managed := kiroIdeSpecification(hook["name"])
if !managed {
return nil, nil
}
action, ok := hook["action"].(map[string]any)
if !ok || action["type"] != "command" {
return nil, nil
}
command, ok := action["command"].(string)
if !ok {
return nil, nil
}
return kiroIdeRuntimeReferenceForCommand(command, specification.scriptName)
}
func managedKiroIdeReference(
hook map[string]any,
) (*kiroIdeRuntimeReference, error) {
specification, managed := kiroIdeSpecification(hook["name"])
if !managed ||
!exactKiroIdeKeys(
hook,
"name", "description", "trigger", "matcher", "action", "timeout", "enabled",
) ||
hook["description"] != specification.description ||
hook["trigger"] != specification.trigger || hook["matcher"] != ".*" ||
hook["timeout"] != kiroIdeHookTimeout || hook["enabled"] != true {
return nil, nil
}
action, ok := hook["action"].(map[string]any)
if !ok || !exactKiroIdeKeys(action, "type", "command") ||
action["type"] != "command" {
return nil, nil
}
return ownedKiroIdeReference(hook)
}
func requireAvailableKiroIdeHooks(hooks []map[string]any) error {
for _, hook := range hooks {
if _, managed := kiroIdeSpecification(hook["name"]); !managed {
continue
}
reference, err := ownedKiroIdeReference(hook)
if err != nil {
return err
}
if reference == nil {
return fmt.Errorf(
"Kiro IDE hook name %q conflicts with the Elydora contract",
hook["name"],
)
}
}
return nil
}
func buildKiroIdeHook(
name string,
runtimePath string,
scriptPath string,
) (map[string]any, error) {
specification, managed := kiroIdeSpecification(name)
if !managed {
return nil, fmt.Errorf("unsupported Elydora Kiro IDE hook name %q", name)
}
command, err := buildKiroIdeCommand(runtimePath, scriptPath)
if err != nil {
return nil, err
}
return map[string]any{
"name": name, "description": specification.description,
"trigger": specification.trigger, "matcher": ".*",
"action": map[string]any{"type": "command", "command": command},
"timeout": kiroIdeHookTimeout, "enabled": true,
}, nil
}
func withoutManagedKiroIdeHooks(
hooks []map[string]any,
agentID string,
) []map[string]any {
filtered := make([]map[string]any, 0, len(hooks))
for _, hook := range hooks {
reference, err := ownedKiroIdeReference(hook)
remove := err == nil && reference != nil &&
(agentID == "" || sameKiroIdeAgentID(reference.agentID, agentID))
if !remove {
filtered = append(filtered, hook)
}
}
return filtered
}
func entirelyManagedKiroIdeDocument(document *kiroIdeDocument) bool {
if document == nil || !document.exists ||
!exactKiroIdeKeys(document.root, "version", "hooks") ||
len(document.hooks) == 0 {
return false
}
for _, hook := range document.hooks {
reference, err := managedKiroIdeReference(hook)
if err != nil || reference == nil {
return false
}
}
return true
}
func renderKiroIdeDocument(
document *kiroIdeDocument,
hooks []map[string]any,
) (*kiroIdeRenderedDocument, error) {
if document == nil {
return nil, fmt.Errorf("Kiro IDE rendering requires a hook document")
}
if !document.exists && len(hooks) == 0 {
return &kiroIdeRenderedDocument{document: document}, nil
}
if reflect.DeepEqual(hooks, document.hooks) {
return &kiroIdeRenderedDocument{document: document}, nil
}
if len(hooks) == 0 && entirelyManagedKiroIdeDocument(document) {
return &kiroIdeRenderedDocument{
document: document, changed: true, remove: true,
}, nil
}
root := cloneKiroIdeObject(document.root)
root["version"] = "v1"
values := make([]any, 0, len(hooks))
for _, hook := range hooks {
values = append(values, hook)
}
root["hooks"] = values
next, err := json.MarshalIndent(root, "", " ")
if err != nil {
return nil, fmt.Errorf("encode Kiro IDE hooks: %w", err)
}
next = append(next, '\n')
validation := &managedFileSnapshot{contents: next}
if _, err := parseKiroIdeDocument(document.filePath, validation); err != nil {
return nil, fmt.Errorf("validate rendered Kiro IDE hooks: %w", err)
}
return &kiroIdeRenderedDocument{
document: document,
changed: !document.exists || !bytes.Equal(next, document.snapshot.contents),
next: next,
}, nil
}
func kiroIdeRuntimeContracts(
hooks []map[string]any,
) ([]kiroIdeRuntimeContract, error) {
managedHooks := make([]map[string]any, 0, 2)
for _, hook := range hooks {
_, managed := kiroIdeSpecification(hook["name"])
if managed {
managedHooks = append(managedHooks, hook)
}
}
if len(managedHooks) != 2 {
return []kiroIdeRuntimeContract{}, nil
}
nodePath, err := resolveNodeRuntime()
if err != nil {
return nil, err
}
var guard, audit *kiroIdeRuntimeReference
for _, hook := range managedHooks {
reference, err := currentKiroIdeReference(hook, nodePath)
if err != nil {
return nil, err
}
if reference == nil {
return []kiroIdeRuntimeContract{}, nil
}
if hook["name"] == kiroIdeGuardName {
if guard != nil {
return []kiroIdeRuntimeContract{}, nil
}
guard = reference
} else {
if audit != nil {
return []kiroIdeRuntimeContract{}, nil
}
audit = reference
}
}
if guard == nil || audit == nil ||
!sameKiroIdeAgentID(guard.agentID, audit.agentID) {
return []kiroIdeRuntimeContract{}, nil
}
return []kiroIdeRuntimeContract{{
agentID: guard.agentID, guardPath: guard.scriptPath,
auditPath: audit.scriptPath,
}}, nil
}
func parseKiroIdeLegacyDocument(
filePath string,
snapshot *managedFileSnapshot,
) (*kiroIdeLegacyDocument, error) {
document := &kiroIdeLegacyDocument{filePath: filePath, snapshot: snapshot}
if snapshot == nil {
return document, nil
}
label := fmt.Sprintf("legacy Kiro IDE hook at %s", filePath)
root, err := decodeStrictJSONObject(snapshot.contents, label)
if err != nil {
return nil, err
}
if !exactKiroIdeKeys(root, "name", "hooks") || root["name"] != "Elydora Audit" {
return document, nil
}
hooks, ok := root["hooks"].(map[string]any)
if !ok || !exactKiroIdeKeys(hooks, "pre_tool_use", "post_tool_use") {
return document, nil
}
guard, ok := hooks["pre_tool_use"].(map[string]any)
if !ok || !exactKiroIdeKeys(guard, "command", "timeout_ms") ||
guard["timeout_ms"] != float64(5000) {
return document, nil
}
audit, ok := hooks["post_tool_use"].(map[string]any)
if !ok || !exactKiroIdeKeys(audit, "command", "timeout_ms") ||
audit["timeout_ms"] != float64(5000) {
return document, nil
}
guardReference, err := legacyKiroIdeGoReference(guard["command"], kiroIdeGuardScript)
if err != nil {
return nil, err
}
auditReference, err := legacyKiroIdeGoReference(audit["command"], kiroIdeAuditScript)
if err != nil {
return nil, err
}
if guardReference == nil || auditReference == nil ||
!sameKiroIdeAgentID(guardReference.agentID, auditReference.agentID) {
return document, nil
}
document.contract = &kiroIdeRuntimeContract{
agentID: guardReference.agentID, guardPath: guardReference.scriptPath,
auditPath: auditReference.scriptPath,
}
return document, nil
}