diff --git a/adapters/openclaw.md b/adapters/openclaw.md new file mode 100644 index 0000000..a0b877a --- /dev/null +++ b/adapters/openclaw.md @@ -0,0 +1,34 @@ +# OpenClaw Adapter Design + +## Goal + +Describe how reply-end-controls should connect to OpenClaw without baking all logic directly into Telegram-specific code. + +## Adapter responsibilities + +### Outbound integration + +- detect final assistant reply payloads +- append Telegram buttons + +### Callback integration + +- receive Telegram callback query events from OpenClaw's Telegram plugin layer +- normalize callback payload +- pass normalized event to reply-end-controls core + +### State integration + +- store reply-end state in OpenClaw conversation/session state +- expose the state to later assistant turns + +### Policy integration + +- before later assistant output generation, expose `lastChoice` +- if `stop`, suppress proactive continuation behavior + +## V1 expectation + +The OpenClaw adapter is the first concrete runtime adapter for the project. + +Even though Telegram is the only supported channel, the logic should still be split so the reusable core is not trapped inside one Telegram-only handler implementation. diff --git a/core/callback-contract.md b/core/callback-contract.md new file mode 100644 index 0000000..8b7bd07 --- /dev/null +++ b/core/callback-contract.md @@ -0,0 +1,37 @@ +# Callback Contract + +## Goal + +Define a stable callback payload contract between channel-specific button handlers and the reusable reply-end-controls core. + +## Telegram V1 callback values + +- `rec:continue` +- `rec:stop` + +## Optional message-scoped extension + +If V1 needs tighter message binding later, extend to: + +- `rec:continue:` +- `rec:stop:` + +## Normalized internal event + +Suggested normalized structure after channel parsing: + +```json +{ + "choice": "continue | stop", + "conversationId": "string", + "sessionKey": "string | null", + "sourceMessageId": "string", + "sourceCallbackId": "string", + "channel": "telegram", + "timestamp": "ISO timestamp" +} +``` + +## Why normalize + +The Telegram parser should convert platform-specific callback payloads into one internal structure, so the reusable policy/state layer does not need to know Telegram details. diff --git a/core/policy.md b/core/policy.md new file mode 100644 index 0000000..a0ca4b4 --- /dev/null +++ b/core/policy.md @@ -0,0 +1,29 @@ +# Policy + +## Goal + +Define how reply-end choices influence later assistant behavior. + +## Policy rules + +### If `lastChoice = continue` + +- assistant/runtime may continue normal follow-up behavior +- assistant may keep short-term task momentum + +### If `lastChoice = stop` + +- assistant should not proactively extend the previous thread +- assistant should avoid unnecessary follow-up prompts +- assistant should treat the prior exchange as closed + +### If user types a new message + +- typed input takes priority over button state +- the new message may implicitly reopen the interaction + +## V1 constraint + +V1 only needs to influence continuation behavior. + +It does not need to redesign routing, tool permissions, or long-term memory behavior. diff --git a/core/state-model.md b/core/state-model.md new file mode 100644 index 0000000..52a0931 --- /dev/null +++ b/core/state-model.md @@ -0,0 +1,49 @@ +# State Model + +## Goal + +Define the agent-neutral state model for reply-end controls. + +This state should be reusable across multiple agents, while Telegram remains the first delivery channel. + +## Core state + +Suggested canonical structure: + +```json +{ + "replyEndControls": { + "lastChoice": "continue | stop", + "lastChoiceAt": "ISO timestamp", + "sourceMessageId": "string", + "sourceCallbackId": "string", + "active": true + } +} +``` + +## Field meanings + +- `lastChoice` + - the latest explicit user choice +- `lastChoiceAt` + - when the choice was made +- `sourceMessageId` + - the assistant message whose buttons were clicked +- `sourceCallbackId` + - the callback event id from the channel +- `active` + - whether the latest state should still affect later assistant behavior + +## Semantics + +- `continue` + - the conversation should remain open to follow-up handling +- `stop` + - the conversation should be treated as closed unless the user explicitly types a new instruction + +## Reset rules + +- A new typed user message overrides prior button state. +- A later `continue` click overrides an earlier `stop` click. +- The system may optionally expire stale button state in a later version, but V1 does not require timeout expiry. diff --git a/telegram/callback-handler.md b/telegram/callback-handler.md new file mode 100644 index 0000000..4e6650f --- /dev/null +++ b/telegram/callback-handler.md @@ -0,0 +1,20 @@ +# Telegram Callback Handler + +## Responsibility + +Receive Telegram callback queries, parse reply-end button clicks, normalize them, and hand them off to the reusable state/policy layer. + +## V1 actions + +- parse `rec:continue` +- parse `rec:stop` +- map into normalized callback event +- write updated state +- update original message buttons to show resolved choice + +## V1 UI behavior after click + +Suggested resolved button state: + +- selected option gains a clear marker +- the message should not remain indefinitely clickable as if no decision happened diff --git a/telegram/reply-decorator.md b/telegram/reply-decorator.md new file mode 100644 index 0000000..6798084 --- /dev/null +++ b/telegram/reply-decorator.md @@ -0,0 +1,22 @@ +# Telegram Reply Decorator + +## Responsibility + +Append inline keyboard controls to every assistant reply sent to Telegram. + +## Required controls + +- `A. 繼續` +- `B. 就這樣吧,不需要額外處理` + +## Output shape + +The Telegram outbound payload should include: + +- normal reply text +- inline keyboard markup + +## V1 behavior + +- every assistant reply gets the two buttons +- no conditional rendering in V1