@nextclaw/ncp-react 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,12 +1,13 @@
1
- import { NcpAgentConversationSnapshot, NcpMessage, NcpAgentClientEndpoint } from '@nextclaw/ncp';
1
+ import { NcpAgentConversationSnapshot, NcpMessage, NcpRequestEnvelope, NcpAgentClientEndpoint } from '@nextclaw/ncp';
2
2
 
3
+ type NcpAgentSendInput = string | NcpRequestEnvelope;
3
4
  type UseNcpAgentResult = {
4
5
  snapshot: NcpAgentConversationSnapshot;
5
6
  visibleMessages: readonly NcpMessage[];
6
7
  activeRunId: string | null;
7
8
  isRunning: boolean;
8
9
  isSending: boolean;
9
- send: (content: string) => Promise<void>;
10
+ send: (input: NcpAgentSendInput) => Promise<void>;
10
11
  abort: () => Promise<void>;
11
12
  streamRun: () => Promise<void>;
12
13
  };
package/dist/index.js CHANGED
@@ -4,7 +4,9 @@ import { useCallback, useEffect as useEffect2, useRef as useRef2, useState as us
4
4
  // src/hooks/use-ncp-agent-runtime.ts
5
5
  import { useEffect, useRef, useState, useSyncExternalStore } from "react";
6
6
  import { DefaultNcpAgentConversationStateManager } from "@nextclaw/ncp-toolkit";
7
- import "@nextclaw/ncp";
7
+ import {
8
+ NcpEventType
9
+ } from "@nextclaw/ncp";
8
10
  function shouldDispatchEventToSession(event, sessionId) {
9
11
  const payload = "payload" in event ? event.payload : null;
10
12
  if (!payload || typeof payload !== "object") {
@@ -15,6 +17,51 @@ function shouldDispatchEventToSession(event, sessionId) {
15
17
  }
16
18
  return payload.sessionId === sessionId;
17
19
  }
20
+ function hasMessageContent(message) {
21
+ return message.parts.some((part) => {
22
+ if (part.type === "text" || part.type === "rich-text" || part.type === "reasoning") {
23
+ return part.text.trim().length > 0;
24
+ }
25
+ return true;
26
+ });
27
+ }
28
+ function normalizeSendEnvelope(input, sessionId) {
29
+ if (typeof input === "string") {
30
+ const content = input.trim();
31
+ if (!content) {
32
+ return null;
33
+ }
34
+ return {
35
+ sessionId,
36
+ message: {
37
+ id: `user-${Date.now().toString(36)}`,
38
+ sessionId,
39
+ role: "user",
40
+ status: "final",
41
+ parts: [{ type: "text", text: content }],
42
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
43
+ }
44
+ };
45
+ }
46
+ if (!hasMessageContent(input.message)) {
47
+ return null;
48
+ }
49
+ return {
50
+ ...input,
51
+ sessionId: input.sessionId,
52
+ message: {
53
+ ...input.message,
54
+ sessionId: input.message.sessionId || input.sessionId
55
+ }
56
+ };
57
+ }
58
+ function toHydrationPayload(sessionId, snapshot) {
59
+ return {
60
+ sessionId,
61
+ messages: snapshot.messages,
62
+ activeRun: snapshot.activeRun
63
+ };
64
+ }
18
65
  function useScopedAgentManager(sessionId) {
19
66
  const managerRef = useRef();
20
67
  if (!managerRef.current || managerRef.current.sessionId !== sessionId) {
@@ -54,23 +101,29 @@ function useNcpAgentRuntime({
54
101
  const visibleMessages = snapshot.streamingMessage ? [...snapshot.messages, snapshot.streamingMessage] : snapshot.messages;
55
102
  const activeRunId = snapshot.activeRun?.runId ?? null;
56
103
  const isRunning = !!snapshot.activeRun;
57
- const send = async (content) => {
58
- if (!content.trim() || isSending || isRunning) {
104
+ const send = async (input) => {
105
+ if (isSending || isRunning) {
106
+ return;
107
+ }
108
+ const envelope = normalizeSendEnvelope(input, sessionId);
109
+ if (!envelope) {
59
110
  return;
60
111
  }
61
112
  setIsSending(true);
62
- try {
63
- await client.send({
113
+ const previousSnapshot = manager.getSnapshot();
114
+ await manager.dispatch({
115
+ type: NcpEventType.MessageSent,
116
+ payload: {
64
117
  sessionId,
65
- message: {
66
- id: `user-${Date.now().toString(36)}`,
67
- sessionId,
68
- role: "user",
69
- status: "final",
70
- parts: [{ type: "text", text: content.trim() }],
71
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
72
- }
73
- });
118
+ message: envelope.message,
119
+ metadata: envelope.metadata
120
+ }
121
+ });
122
+ try {
123
+ await client.send(envelope);
124
+ } catch (error) {
125
+ manager.hydrate(toHydrationPayload(sessionId, previousSnapshot));
126
+ throw error;
74
127
  } finally {
75
128
  setIsSending(false);
76
129
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/ncp-react",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
5
  "description": "React bindings for building NCP-based agent applications.",
6
6
  "type": "module",
@@ -15,8 +15,8 @@
15
15
  "dist"
16
16
  ],
17
17
  "dependencies": {
18
- "@nextclaw/ncp-toolkit": "0.2.0",
19
- "@nextclaw/ncp": "0.2.0"
18
+ "@nextclaw/ncp": "0.3.0",
19
+ "@nextclaw/ncp-toolkit": "0.3.0"
20
20
  },
21
21
  "peerDependencies": {
22
22
  "react": "^18.0.0 || ^19.0.0"