summaryrefslogtreecommitdiffhomepage
path: root/packages/transport-http/src/logic.ts
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-21 16:56:42 +0900
committerAdam Malczewski <[email protected]>2026-06-21 16:56:42 +0900
commitea0e938eca3072649dc8707c999ec00cf87b986a (patch)
tree41f96ed517fe779ed612fc4cb3e8359a841ad088 /packages/transport-http/src/logic.ts
parent36e950ba2cd2591e86f0dcc898f740481c59d912 (diff)
downloaddispatch-ea0e938eca3072649dc8707c999ec00cf87b986a.tar.gz
dispatch-ea0e938eca3072649dc8707c999ec00cf87b986a.zip
feat(transport): CLI endpoints + conversation.open broadcast (Wave 2)
transport-http: GET /conversations (list with ?q= prefix filter), GET /conversations/:id/last (blocks until turn settles, returns last AI text), POST /conversations/:id/open (emits conversationOpened hook), PUT /conversations/:id/title (set title). emit threaded from host.emit. extractLastAssistantText pure helper. 21 new tests (166 total). transport-ws: subscribes to conversationOpened hook, broadcasts ConversationOpenMessage to all connected WS clients. 2 new tests. session-orchestrator: conversationOpened hook descriptor (exported).
Diffstat (limited to 'packages/transport-http/src/logic.ts')
-rw-r--r--packages/transport-http/src/logic.ts32
1 files changed, 31 insertions, 1 deletions
diff --git a/packages/transport-http/src/logic.ts b/packages/transport-http/src/logic.ts
index aa5394c..d20713c 100644
--- a/packages/transport-http/src/logic.ts
+++ b/packages/transport-http/src/logic.ts
@@ -1,4 +1,4 @@
-import type { AgentEvent, ReasoningEffort } from "@dispatch/kernel";
+import type { AgentEvent, ChatMessage, ReasoningEffort } from "@dispatch/kernel";
const VALID_REASONING_EFFORTS: readonly ReasoningEffort[] = [
"low",
@@ -221,3 +221,33 @@ export function isReasoningEffortParseError(
): result is ParseError {
return typeof result === "object" && result !== null && "error" in result;
}
+
+/**
+ * Extract the text of the last assistant message's last `text` chunk — the
+ * "show me the last reply" affordance for `GET /conversations/:id/last`.
+ *
+ * Scan from the END for the last message with `role: "assistant"`, then within
+ * THAT message for the last `type: "text"` chunk. Returns its `text`. Returns
+ * `""` when there is no assistant message, or when the last assistant message
+ * has no text chunk (e.g. only tool-call chunks).
+ *
+ * Pure (input → output); zero I/O, so it tests directly without mocks.
+ */
+export function extractLastAssistantText(messages: readonly ChatMessage[]): string {
+ for (let i = messages.length - 1; i >= 0; i--) {
+ const msg = messages[i];
+ if (msg === undefined || msg.role !== "assistant") continue;
+ // Found the last assistant message — scan its chunks from the end for
+ // the last `text` chunk. Stop here (do not keep scanning earlier
+ // assistant messages): the contract is "the last assistant message's
+ // last text chunk", not "the most recent text chunk anywhere".
+ for (let j = msg.chunks.length - 1; j >= 0; j--) {
+ const chunk = msg.chunks[j];
+ if (chunk !== undefined && chunk.type === "text") {
+ return chunk.text;
+ }
+ }
+ return "";
+ }
+ return "";
+}