From 9312491656e8bc1a84a0155a707c67a2faf2cfc4 Mon Sep 17 00:00:00 2001 From: Hesham Karm <24391550+hishamkaram@users.noreply.github.com> Date: Sun, 17 May 2026 06:06:16 +0200 Subject: [PATCH] feat(protocol): add interactive prompt resolution tombstone --- prompt.go | 16 ++++++++++++++++ prompt_test.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 prompt.go create mode 100644 prompt_test.go diff --git a/prompt.go b/prompt.go new file mode 100644 index 0000000..814ed08 --- /dev/null +++ b/prompt.go @@ -0,0 +1,16 @@ +package protocol + +// MsgInteractivePromptResolved is the daemon->PWA tombstone broadcast after +// an interactive prompt answer is accepted by the daemon-side agent adapter. +const MsgInteractivePromptResolved = "interactive_prompt_resolved" + +// InteractivePromptResolvedPayload is carried by MsgInteractivePromptResolved. +// The PWA records QuestionID as resolved for SessionID and clears only matching +// pending/in-flight prompt UI state. Reason is a stable, human-readable machine +// string such as "answered". +type InteractivePromptResolvedPayload struct { + QuestionID string `json:"question_id"` + SessionID string `json:"session_id"` + Reason string `json:"reason"` + ResolvedAt int64 `json:"resolved_at"` +} diff --git a/prompt_test.go b/prompt_test.go new file mode 100644 index 0000000..bec93cb --- /dev/null +++ b/prompt_test.go @@ -0,0 +1,45 @@ +package protocol + +import ( + "encoding/json" + "strings" + "testing" +) + +func TestMsgInteractivePromptResolvedConstant(t *testing.T) { + t.Parallel() + + if MsgInteractivePromptResolved != "interactive_prompt_resolved" { + t.Fatalf("MsgInteractivePromptResolved = %q, want %q", MsgInteractivePromptResolved, "interactive_prompt_resolved") + } +} + +func TestInteractivePromptResolvedPayloadRoundtrip(t *testing.T) { + t.Parallel() + + original := InteractivePromptResolvedPayload{ + QuestionID: "q-123", + SessionID: "s-123", + Reason: "answered", + ResolvedAt: 1778915600123, + } + + raw, err := json.Marshal(original) + if err != nil { + t.Fatalf("marshal: %v", err) + } + rawStr := string(raw) + for _, want := range []string{`"question_id"`, `"session_id"`, `"reason"`, `"resolved_at"`} { + if !strings.Contains(rawStr, want) { + t.Fatalf("marshaled JSON missing %s: %s", want, rawStr) + } + } + + var decoded InteractivePromptResolvedPayload + if err := json.Unmarshal(raw, &decoded); err != nil { + t.Fatalf("unmarshal: %v", err) + } + if decoded != original { + t.Fatalf("roundtrip mismatch:\n got: %+v\n want: %+v", decoded, original) + } +}