@ably/ai-transport 0.0.1 → 0.1.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.
Files changed (110) hide show
  1. package/README.md +54 -47
  2. package/dist/ably-ai-transport.js +1006 -539
  3. package/dist/ably-ai-transport.js.map +1 -1
  4. package/dist/ably-ai-transport.umd.cjs +1 -1
  5. package/dist/ably-ai-transport.umd.cjs.map +1 -1
  6. package/dist/constants.d.ts +4 -0
  7. package/dist/core/codec/types.d.ts +19 -2
  8. package/dist/core/transport/decode-history.d.ts +8 -6
  9. package/dist/core/transport/headers.d.ts +4 -2
  10. package/dist/core/transport/index.d.ts +4 -1
  11. package/dist/core/transport/pipe-stream.d.ts +3 -2
  12. package/dist/core/transport/stream-router.d.ts +11 -1
  13. package/dist/core/transport/tree.d.ts +171 -0
  14. package/dist/core/transport/turn-manager.d.ts +4 -1
  15. package/dist/core/transport/types.d.ts +270 -119
  16. package/dist/core/transport/view.d.ts +166 -0
  17. package/dist/errors.d.ts +19 -2
  18. package/dist/index.d.ts +3 -1
  19. package/dist/react/ably-ai-transport-react.js +1019 -486
  20. package/dist/react/ably-ai-transport-react.js.map +1 -1
  21. package/dist/react/ably-ai-transport-react.umd.cjs +1 -1
  22. package/dist/react/ably-ai-transport-react.umd.cjs.map +1 -1
  23. package/dist/react/contexts/transport-context.d.ts +31 -0
  24. package/dist/react/contexts/transport-provider.d.ts +49 -0
  25. package/dist/react/create-transport-hooks.d.ts +124 -0
  26. package/dist/react/index.d.ts +14 -8
  27. package/dist/react/use-ably-messages.d.ts +14 -8
  28. package/dist/react/use-active-turns.d.ts +7 -3
  29. package/dist/react/use-client-transport.d.ts +78 -5
  30. package/dist/react/use-create-view.d.ts +22 -0
  31. package/dist/react/use-tree.d.ts +20 -0
  32. package/dist/react/use-view.d.ts +79 -0
  33. package/dist/vercel/ably-ai-transport-vercel.js +1478 -842
  34. package/dist/vercel/ably-ai-transport-vercel.js.map +1 -1
  35. package/dist/vercel/ably-ai-transport-vercel.umd.cjs +1 -1
  36. package/dist/vercel/ably-ai-transport-vercel.umd.cjs.map +1 -1
  37. package/dist/vercel/codec/tool-transitions.d.ts +50 -0
  38. package/dist/vercel/index.d.ts +3 -0
  39. package/dist/vercel/react/ably-ai-transport-vercel-react.js +9099 -852
  40. package/dist/vercel/react/ably-ai-transport-vercel-react.js.map +1 -1
  41. package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs +45 -1
  42. package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs.map +1 -1
  43. package/dist/vercel/react/contexts/chat-transport-context.d.ts +32 -0
  44. package/dist/vercel/react/contexts/chat-transport-provider.d.ts +84 -0
  45. package/dist/vercel/react/index.d.ts +5 -0
  46. package/dist/vercel/react/use-chat-transport.d.ts +61 -20
  47. package/dist/vercel/react/use-message-sync.d.ts +41 -9
  48. package/dist/vercel/react/use-staged-add-tool-approval-response.d.ts +30 -0
  49. package/dist/vercel/tool-approvals.d.ts +124 -0
  50. package/dist/vercel/tool-events.d.ts +26 -0
  51. package/dist/vercel/transport/chat-transport.d.ts +33 -11
  52. package/dist/vercel/transport/index.d.ts +5 -2
  53. package/package.json +23 -17
  54. package/src/constants.ts +6 -0
  55. package/src/core/codec/encoder.ts +10 -1
  56. package/src/core/codec/types.ts +19 -3
  57. package/src/core/transport/client-transport.ts +382 -364
  58. package/src/core/transport/decode-history.ts +229 -81
  59. package/src/core/transport/headers.ts +6 -2
  60. package/src/core/transport/index.ts +13 -5
  61. package/src/core/transport/pipe-stream.ts +8 -5
  62. package/src/core/transport/server-transport.ts +212 -58
  63. package/src/core/transport/stream-router.ts +21 -3
  64. package/src/core/transport/{conversation-tree.ts → tree.ts} +192 -77
  65. package/src/core/transport/turn-manager.ts +28 -10
  66. package/src/core/transport/types.ts +318 -139
  67. package/src/core/transport/view.ts +840 -0
  68. package/src/errors.ts +21 -1
  69. package/src/index.ts +10 -5
  70. package/src/react/contexts/transport-context.ts +37 -0
  71. package/src/react/contexts/transport-provider.tsx +164 -0
  72. package/src/react/create-transport-hooks.ts +144 -0
  73. package/src/react/index.ts +15 -8
  74. package/src/react/use-ably-messages.ts +34 -16
  75. package/src/react/use-active-turns.ts +28 -17
  76. package/src/react/use-client-transport.ts +184 -24
  77. package/src/react/use-create-view.ts +68 -0
  78. package/src/react/use-tree.ts +53 -0
  79. package/src/react/use-view.ts +233 -0
  80. package/src/react/vite.config.ts +4 -1
  81. package/src/vercel/codec/accumulator.ts +64 -79
  82. package/src/vercel/codec/decoder.ts +11 -8
  83. package/src/vercel/codec/encoder.ts +68 -54
  84. package/src/vercel/codec/index.ts +0 -2
  85. package/src/vercel/codec/tool-transitions.ts +122 -0
  86. package/src/vercel/index.ts +17 -0
  87. package/src/vercel/react/contexts/chat-transport-context.ts +40 -0
  88. package/src/vercel/react/contexts/chat-transport-provider.tsx +122 -0
  89. package/src/vercel/react/index.ts +14 -0
  90. package/src/vercel/react/use-chat-transport.ts +164 -42
  91. package/src/vercel/react/use-message-sync.ts +77 -19
  92. package/src/vercel/react/use-staged-add-tool-approval-response.ts +87 -0
  93. package/src/vercel/react/vite.config.ts +4 -2
  94. package/src/vercel/tool-approvals.ts +380 -0
  95. package/src/vercel/tool-events.ts +53 -0
  96. package/src/vercel/transport/chat-transport.ts +225 -79
  97. package/src/vercel/transport/index.ts +14 -3
  98. package/dist/core/transport/conversation-tree.d.ts +0 -9
  99. package/dist/react/use-conversation-tree.d.ts +0 -20
  100. package/dist/react/use-edit.d.ts +0 -7
  101. package/dist/react/use-history.d.ts +0 -19
  102. package/dist/react/use-messages.d.ts +0 -7
  103. package/dist/react/use-regenerate.d.ts +0 -7
  104. package/dist/react/use-send.d.ts +0 -7
  105. package/src/react/use-conversation-tree.ts +0 -71
  106. package/src/react/use-edit.ts +0 -24
  107. package/src/react/use-history.ts +0 -111
  108. package/src/react/use-messages.ts +0 -32
  109. package/src/react/use-regenerate.ts +0 -24
  110. package/src/react/use-send.ts +0 -25
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ably/ai-transport",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "description": "Ably transport and codecs for building AI applications with Ably.",
5
5
  "type": "module",
6
6
  "main": "dist/ably-ai-transport.umd.cjs",
@@ -46,13 +46,15 @@
46
46
  "prepare": "npm run build",
47
47
  "lint": "eslint .",
48
48
  "lint:fix": "eslint --fix .; (npm run format > /dev/null)",
49
- "format": "prettier --list-different --write src demo/vercel/react/*/src",
50
- "format:check": "prettier --check src demo/vercel/react/*/src",
49
+ "format": "prettier --list-different --write .",
50
+ "format:check": "prettier --check .",
51
51
  "typecheck": "tsc --noEmit",
52
52
  "test": "vitest run",
53
53
  "test:integration": "vitest run --config vitest.config.integration.ts",
54
54
  "check:error-codes": "tsx scripts/validate-error-codes.ts",
55
- "precommit": "npm run format:check && npm run lint && npm run typecheck"
55
+ "precommit": "npm run format:check && npm run lint && npm run typecheck",
56
+ "docs": "typedoc",
57
+ "docs:lint": "typedoc --emit none"
56
58
  },
57
59
  "files": [
58
60
  "dist/**",
@@ -94,28 +96,32 @@
94
96
  }
95
97
  },
96
98
  "devDependencies": {
97
- "@eslint/compat": "^1.2.7",
99
+ "@ai-sdk/react": "^3.0.151",
100
+ "@eslint/compat": "^2.0.5",
98
101
  "@eslint/eslintrc": "^3.3.0",
99
- "@eslint/js": "^9.12.0",
102
+ "@eslint/js": "^10.0.1",
100
103
  "@testing-library/react": "^16.3.2",
101
104
  "@types/react": "^19.2.14",
102
- "@typescript-eslint/eslint-plugin": "^8.26.1",
103
- "@typescript-eslint/parser": "^8.26.1",
105
+ "@typescript-eslint/eslint-plugin": "^8.58.2",
106
+ "@typescript-eslint/parser": "^8.58.2",
107
+ "@vitest/coverage-v8": "^4.1.2",
104
108
  "ai": "^6.0.137",
105
- "eslint": "^9.31.0",
106
- "eslint-plugin-import": "^2.31.0",
107
- "eslint-plugin-jsdoc": "^60.7.0",
109
+ "eslint": "^10.2.0",
110
+ "eslint-plugin-import-x": "^4.16.2",
111
+ "eslint-plugin-jsdoc": "^62.9.0",
108
112
  "eslint-plugin-prefer-arrow-functions": "^3.6.2",
109
- "eslint-plugin-security": "^3.0.1",
110
- "eslint-plugin-simple-import-sort": "^12.1.1",
111
- "eslint-plugin-unicorn": "^61.0.2",
112
- "globals": "^16.0.0",
113
+ "eslint-plugin-security": "^4.0.0",
114
+ "eslint-plugin-simple-import-sort": "^13.0.0",
115
+ "eslint-plugin-unicorn": "^64.0.0",
116
+ "globals": "^17.5.0",
113
117
  "jiti": "^2.6.1",
114
118
  "jsdom": "^29.0.1",
115
119
  "prettier": "^3.5.3",
116
120
  "tsx": "^4.21.0",
117
- "typescript": "^5.8.2",
118
- "typescript-eslint": "^8.26.1",
121
+ "typedoc": "^0.28.13",
122
+ "typedoc-plugin-no-inherit": "^1.5.0",
123
+ "typescript": "^6.0.2",
124
+ "typescript-eslint": "^8.58.2",
119
125
  "vite": "^8.0.0",
120
126
  "vite-plugin-dts": "^4.5.4",
121
127
  "vitest": "^4.1.0"
package/src/constants.ts CHANGED
@@ -22,6 +22,9 @@ export const HEADER_STATUS = 'x-ably-status';
22
22
  /** Header: stream identity. Set by the encoder on every streamed message; read by the decoder to correlate streams. */
23
23
  export const HEADER_STREAM_ID = 'x-ably-stream-id';
24
24
 
25
+ /** Header: marks a message as a discrete message part (from writeMessages). Set by publishDiscreteBatch; not set on lifecycle events from publishDiscrete. */
26
+ export const HEADER_DISCRETE = 'x-ably-discrete';
27
+
25
28
  // ---------------------------------------------------------------------------
26
29
  // Identity headers (used by transport for turn correlation)
27
30
  // ---------------------------------------------------------------------------
@@ -38,6 +41,9 @@ export const HEADER_TURN_CLIENT_ID = 'x-ably-turn-client-id';
38
41
  /** Header: message role (e.g. "user", "assistant"). */
39
42
  export const HEADER_ROLE = 'x-ably-role';
40
43
 
44
+ /** Header: the msg-id of the existing message this Ably message amends. Present on cross-turn amendment events. */
45
+ export const HEADER_AMEND = 'x-ably-amend';
46
+
41
47
  // ---------------------------------------------------------------------------
42
48
  // Cancel headers
43
49
  // ---------------------------------------------------------------------------
@@ -11,7 +11,7 @@
11
11
 
12
12
  import * as Ably from 'ably';
13
13
 
14
- import { HEADER_MSG_ID, HEADER_STATUS, HEADER_STREAM, HEADER_STREAM_ID } from '../../constants.js';
14
+ import { HEADER_DISCRETE, HEADER_MSG_ID, HEADER_STATUS, HEADER_STREAM, HEADER_STREAM_ID } from '../../constants.js';
15
15
  import { ErrorCode } from '../../errors.js';
16
16
  import type { Logger } from '../../logger.js';
17
17
  import { mergeHeaders } from '../../utils.js';
@@ -129,6 +129,13 @@ class DefaultEncoderCore implements EncoderCore {
129
129
  this._assertNotClosed();
130
130
  this._logger?.trace('DefaultEncoderCore.publishDiscreteBatch();', { count: payloads.length });
131
131
  const msgs = payloads.map((p) => this._buildDiscreteMessage(p, opts));
132
+ // Mark batch-published payloads as discrete message parts (from writeMessages).
133
+ // The decoder relies on this header to distinguish message parts from lifecycle
134
+ // events that also happen to be discrete (x-ably-stream: false).
135
+ for (const msg of msgs) {
136
+ // CAST: extras is built by _buildDiscreteMessage with a known { headers } shape.
137
+ (msg.extras as { headers: Record<string, string> }).headers[HEADER_DISCRETE] = 'true';
138
+ }
132
139
  return this._writer.publish(msgs);
133
140
  }
134
141
 
@@ -154,6 +161,7 @@ class DefaultEncoderCore implements EncoderCore {
154
161
  const result = await this._writer.publish(msg);
155
162
  const serial = result.serials[0];
156
163
 
164
+ // Spec: AIT-CD2a
157
165
  if (!serial) {
158
166
  throw new Ably.ErrorInfo(
159
167
  `unable to start stream; no serial returned for stream '${payload.name}' (streamId: ${streamId})`,
@@ -181,6 +189,7 @@ class DefaultEncoderCore implements EncoderCore {
181
189
  // Spec: AIT-CD3
182
190
  appendStream(streamId: string, data: string): void {
183
191
  this._assertNotClosed();
192
+ // Spec: AIT-CD3a
184
193
  const tracker = this._trackers.get(streamId);
185
194
  if (!tracker) {
186
195
  throw new Ably.ErrorInfo(
@@ -203,6 +203,19 @@ export interface MessageAccumulator<TEvent, TMessage> {
203
203
  processOutputs(outputs: DecoderOutput<TEvent, TMessage>[]): void;
204
204
  /** Apply an external update to a message (e.g. from an update callback). */
205
205
  updateMessage(message: TMessage): void;
206
+ /**
207
+ * Ensure the accumulator is ready to process events for the given message.
208
+ * If not already active, creates internal tracking state from the message.
209
+ * If already active, syncs internal state with the provided message
210
+ * (picking up external changes like cross-turn amendments).
211
+ * Idempotent — safe to call before every processOutputs.
212
+ */
213
+ initMessage(messageId: string, message: TMessage): void;
214
+ /**
215
+ * Mark a message as completed. Removes it from active tracking so it
216
+ * appears in {@link completedMessages}. No-op if not active.
217
+ */
218
+ completeMessage(messageId: string): void;
206
219
  /** All messages accumulated so far (in-progress and completed). */
207
220
  readonly messages: TMessage[];
208
221
  /** Only messages whose streams have finished. */
@@ -223,6 +236,12 @@ export interface EncoderOptions {
223
236
  extras?: Extras;
224
237
  /** Hook called before each Ably message is published. Mutate the message in place to add transport-level headers. */
225
238
  onMessage?: (message: Ably.Message) => void;
239
+ /**
240
+ * Domain-level message identity. Domain encoders use this as a fallback
241
+ * messageId when a lifecycle chunk (e.g. `start`) does not provide one,
242
+ * ensuring useChat and the transport accumulator assign the same ID.
243
+ */
244
+ messageId?: string;
226
245
  }
227
246
 
228
247
  /**
@@ -243,7 +262,4 @@ export interface Codec<TEvent, TMessage> {
243
262
 
244
263
  /** Whether an event signals stream completion (finish, error, abort). */
245
264
  isTerminal(event: TEvent): boolean;
246
-
247
- /** Return a stable key for a message (used by MessageStore for upsert/delete). */
248
- getMessageKey(message: TMessage): string;
249
265
  }