@smooai/smooth-operator 1.18.0 → 1.21.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.
@@ -66,6 +66,10 @@ export interface CreateConversationSessionRequest {
66
66
  * Browser fingerprint string (e.g. from ThumbmarkJS) used for anonymous user correlation across sessions.
67
67
  */
68
68
  browserFingerprint?: string;
69
+ /**
70
+ * Client render capabilities for this session — a per-kind list gating the Rich Interactions the server may emit mid-turn (`interaction_required`). Each interaction kind declares the capability that gates it (e.g. kind `identity_intake` → capability `identity_form`); future kinds add their own values (`date_picker`, `file_upload`, …). Text-only channels (SMS, voice) omit this and the server degrades each kind to its conversational fallback. Unknown values are ignored (forward-compatible).
71
+ */
72
+ supports?: string[];
69
73
  /**
70
74
  * Arbitrary key/value metadata to attach to the session.
71
75
  */
@@ -321,6 +325,10 @@ export interface SendMessageRequest {
321
325
  * Whether to receive incremental `stream_chunk` and `stream_token` events. Defaults to `true`. Set to `false` to receive only the final `eventual_response`.
322
326
  */
323
327
  stream?: boolean;
328
+ /**
329
+ * Optional gateway model id to run THIS turn on (e.g. a /smooth-mode preset). Absent → the server's configured default model.
330
+ */
331
+ model?: string;
324
332
  }
325
333
 
326
334
  // ── from actions/send-message.schema.json ──
@@ -372,6 +380,49 @@ export interface GeneralAgentResponse {
372
380
  suggestedNextActions: string[];
373
381
  }
374
382
 
383
+ // ── from actions/submit-interaction.schema.json ──
384
+ /**
385
+ * Fields sent by the client to submit (or decline) a parked interaction.
386
+ */
387
+ export interface SubmitInteractionRequest {
388
+ /**
389
+ * Action discriminator.
390
+ */
391
+ action: 'submit_interaction';
392
+ /**
393
+ * Must match the `requestId` from the `interaction_required` event being responded to.
394
+ */
395
+ requestId: string;
396
+ /**
397
+ * Session ID of the parked session.
398
+ */
399
+ sessionId: string;
400
+ /**
401
+ * Must match the `interactionId` from the `interaction_required` event, so a stale submit can never resolve a newer park.
402
+ */
403
+ interactionId: string;
404
+ /**
405
+ * Optional interaction kind, for cross-checking; the server already knows the parked interaction's kind. When present and mismatched, the submit is rejected.
406
+ */
407
+ kind?: string;
408
+ /**
409
+ * Kind-specific submitted values. Required unless `declined` is true. Shape per `interactions/<kind>.schema.json#/$defs/Values` (e.g. identity_intake's `{ name?, email?, phone? }`). Validated server-side by the kind's validator.
410
+ */
411
+ values?: {};
412
+ /**
413
+ * True when the visitor refused the interaction. The turn resumes with a declined payload so the agent can proceed gracefully. When true, `values` is ignored.
414
+ */
415
+ declined?: boolean;
416
+ }
417
+
418
+ // ── from actions/submit-interaction.schema.json ──
419
+ /**
420
+ * No dedicated response event for `submit_interaction`. Valid values (or a decline) are acked with an `immediate_response` and the parked turn resumes its normal streaming sequence. Invalid values emit `interaction_invalid` (the turn stays parked). This schema is provided for documentation completeness only.
421
+ */
422
+ export interface SubmitInteractionResponse {
423
+ [k: string]: unknown;
424
+ }
425
+
375
426
  // ── from actions/verify-otp.schema.json ──
376
427
  /**
377
428
  * Fields sent by the client to submit an OTP code.
@@ -718,6 +769,10 @@ export interface Session {
718
769
  * The conversation this session is attached to.
719
770
  */
720
771
  conversationId: string;
772
+ /**
773
+ * The organization that owns this session. Mirrors `organizationId` on the conversation, participants, and messages so org-scoping is uniform across every domain type and storage backends can write the session's org directly.
774
+ */
775
+ organizationId: string;
721
776
  /**
722
777
  * The agent handling this session.
723
778
  */
@@ -804,6 +859,7 @@ export interface ActionEnvelope {
804
859
  | 'get_conversation_messages'
805
860
  | 'confirm_tool_action'
806
861
  | 'verify_otp'
862
+ | 'submit_interaction'
807
863
  | 'ping';
808
864
  /**
809
865
  * Client-generated correlation ID. Will be echoed back on all related server events. Should be unique per in-flight request. If omitted the server may generate one, but correlating responses becomes the client's problem.
@@ -919,6 +975,23 @@ export interface EventualResponse {
919
975
  * Human-readable escalation reason when `needsEscalation` is true.
920
976
  */
921
977
  escalationReason?: string;
978
+ /**
979
+ * Per-turn token accounting and cost, captured from the engine's terminal completion event. Lets a client accumulate live session cost. Optional and back-compatible: absent when the engine reported no usage for the turn (e.g. an offline/mock turn).
980
+ */
981
+ usage?: {
982
+ /**
983
+ * Accumulated cost in USD across every LLM call in this turn (gateway-priced).
984
+ */
985
+ costUsd?: number;
986
+ /**
987
+ * Accumulated prompt (input) tokens across every LLM call in this turn.
988
+ */
989
+ promptTokens?: number;
990
+ /**
991
+ * Accumulated completion (output) tokens across every LLM call in this turn.
992
+ */
993
+ completionTokens?: number;
994
+ };
922
995
  /**
923
996
  * The sources that grounded this answer, when any were retrieved. Collected by the runtime from the documents that actually grounded the turn — the auto-injected `[Relevant knowledge]` context and any `knowledge_search` tool results — deduplicated by source id and capped. Optional and back-compatible: absent when the turn used no knowledge sources. Each item is a `Citation` (see `domain/citation.schema.json`).
924
997
  */
@@ -985,6 +1058,127 @@ export interface ImmediateResponse {
985
1058
  timestamp?: number;
986
1059
  }
987
1060
 
1061
+ // ── from events/interaction-invalid.schema.json ──
1062
+ /**
1063
+ * Event: `interaction_invalid`. Emitted when a `submit_interaction` action carried values that failed the kind's server-side validation. The turn REMAINS parked — the client should re-render the interaction card with the per-field errors and let the visitor resubmit. Mirrors `otp_invalid`: invalid input is a retryable state, never a terminal `error` event.
1064
+ */
1065
+ export interface InteractionInvalid {
1066
+ /**
1067
+ * Event type discriminator.
1068
+ */
1069
+ type: 'interaction_invalid';
1070
+ /**
1071
+ * Echoes the `requestId` of the parked turn (same correlation as the `interaction_required` event).
1072
+ */
1073
+ requestId?: string;
1074
+ /**
1075
+ * Validation failure details.
1076
+ */
1077
+ data: {
1078
+ /**
1079
+ * The request ID this interaction belongs to.
1080
+ */
1081
+ requestId: string;
1082
+ /**
1083
+ * Per-field validation errors.
1084
+ */
1085
+ data: {
1086
+ /**
1087
+ * The interaction instance the rejected submit targeted.
1088
+ */
1089
+ interactionId: string;
1090
+ /**
1091
+ * The interaction kind (e.g. `identity_intake`).
1092
+ */
1093
+ kind: string;
1094
+ /**
1095
+ * One entry per failed field. `field` is a kind-specific field key (identity_intake: `name` | `email` | `phone`).
1096
+ *
1097
+ * @minItems 1
1098
+ */
1099
+ errors: [
1100
+ {
1101
+ /**
1102
+ * The kind-specific field that failed validation.
1103
+ */
1104
+ field: string;
1105
+ /**
1106
+ * Human-readable validation message for this field.
1107
+ */
1108
+ message: string;
1109
+ },
1110
+ ...{
1111
+ /**
1112
+ * The kind-specific field that failed validation.
1113
+ */
1114
+ field: string;
1115
+ /**
1116
+ * Human-readable validation message for this field.
1117
+ */
1118
+ message: string;
1119
+ }[],
1120
+ ];
1121
+ /**
1122
+ * Human-readable summary suitable for a card-level error line.
1123
+ */
1124
+ message: string;
1125
+ };
1126
+ };
1127
+ /**
1128
+ * Unix epoch milliseconds when the event was emitted.
1129
+ */
1130
+ timestamp?: number;
1131
+ }
1132
+
1133
+ // ── from events/interaction-required.schema.json ──
1134
+ /**
1135
+ * Event: `interaction_required`. The Rich Interactions envelope: emitted mid-turn when the agent requests a structured interaction (identity intake, a date picker, choice chips, …) and the session declared the interaction kind's render capability in `supports` at `create_conversation_session`. The turn is parked until the client replies with a `submit_interaction` action carrying the same `requestId` + `interactionId` (values or `declined: true`). Sessions without the capability never receive this event — the server degrades that kind to its conversational fallback instead. `kind` selects the client card and the server validator; `spec` is the kind-specific payload whose shape is defined by `interactions/<kind>.schema.json` (e.g. `interactions/identity-intake.schema.json#/$defs/Spec`).
1136
+ */
1137
+ export interface InteractionRequired {
1138
+ /**
1139
+ * Event type discriminator.
1140
+ */
1141
+ type: 'interaction_required';
1142
+ /**
1143
+ * Echoes the `requestId` from the originating `send_message` action. Must be included in the `submit_interaction` reply.
1144
+ */
1145
+ requestId?: string;
1146
+ /**
1147
+ * Interaction prompt details.
1148
+ */
1149
+ data: {
1150
+ /**
1151
+ * The request ID this interaction belongs to.
1152
+ */
1153
+ requestId: string;
1154
+ /**
1155
+ * The interaction the client should render.
1156
+ */
1157
+ data: {
1158
+ /**
1159
+ * Server-generated ID for this specific interaction instance. Must be echoed on the `submit_interaction` reply so a stale submit can never resolve a newer park.
1160
+ */
1161
+ interactionId: string;
1162
+ /**
1163
+ * The interaction kind (e.g. `identity_intake`). Selects the client's card component and the server's validator. The kind catalog lives in `spec/interactions/`.
1164
+ */
1165
+ kind: string;
1166
+ /**
1167
+ * Kind-specific render spec. Shape per `interactions/<kind>.schema.json#/$defs/Spec` (e.g. identity_intake's `{ fields: [...] }`).
1168
+ */
1169
+ spec: {};
1170
+ /**
1171
+ * Human-readable reason the agent raised this interaction, suitable for the card header (e.g. `to send you the quote`).
1172
+ */
1173
+ reason: string;
1174
+ };
1175
+ };
1176
+ /**
1177
+ * Unix epoch milliseconds when the event was emitted.
1178
+ */
1179
+ timestamp?: number;
1180
+ }
1181
+
988
1182
  // ── from events/keepalive.schema.json ──
989
1183
  /**
990
1184
  * Event: `keepalive`. Sent periodically by the server during long-running agent turns (typically every 30 seconds) to prevent AWS API Gateway's 10-minute idle connection timeout from closing the WebSocket while the backend is still computing. Clients should acknowledge receipt by updating their last-seen timestamp, but no reply action is needed. Distinct from `ping`/`pong` which are client-initiated.
@@ -1280,6 +1474,42 @@ export interface StreamChunk {
1280
1474
  timestamp?: number;
1281
1475
  }
1282
1476
 
1477
+ // ── from events/stream-reasoning.schema.json ──
1478
+ /**
1479
+ * Event: `stream_reasoning`. A single *reasoning* token from a reasoning-model's separate thinking channel (`reasoning_content`/`reasoning` deltas — e.g. DeepSeek, gpt-oss/harmony, MiniMax, GLM), forwarded to the client in real time. Corresponds to smooth-operator's `AgentEvent::ReasoningDelta`. Shaped identically to `stream_token` so clients can render it the same way, but on a distinct `type` so reasoning is shown as collapsible "thinking" and is NEVER folded into the answer. The final response (carried by `eventual_response`) already excludes reasoning. Clients that do not recognize this event MUST ignore it — the answer still streams via `stream_token`, so reasoning simply isn't shown.
1480
+ */
1481
+ export interface StreamReasoning {
1482
+ /**
1483
+ * Event type discriminator.
1484
+ */
1485
+ type: 'stream_reasoning';
1486
+ /**
1487
+ * Echoes the `requestId` from the originating `send_message` action.
1488
+ */
1489
+ requestId?: string;
1490
+ /**
1491
+ * The raw reasoning token text. Also present inside `data.token` for consumers that only inspect `data`.
1492
+ */
1493
+ token?: string;
1494
+ /**
1495
+ * Reasoning token event payload.
1496
+ */
1497
+ data: {
1498
+ /**
1499
+ * The request ID this reasoning token belongs to.
1500
+ */
1501
+ requestId: string;
1502
+ /**
1503
+ * The raw reasoning token text.
1504
+ */
1505
+ token: string;
1506
+ };
1507
+ /**
1508
+ * Unix epoch milliseconds when the event was emitted.
1509
+ */
1510
+ timestamp?: number;
1511
+ }
1512
+
1283
1513
  // ── from events/stream-token.schema.json ──
1284
1514
  /**
1285
1515
  * Event: `stream_token`. A single LLM output token forwarded to the client in real time. Corresponds to smooth-operator's `AgentEvent::TokenDelta`. Clients accumulate tokens to display a live typing animation. After the node finishes, a `stream_chunk` event carries the complete state snapshot for that node.
@@ -1356,3 +1586,90 @@ export interface WriteConfirmationRequired {
1356
1586
  */
1357
1587
  timestamp?: number;
1358
1588
  }
1589
+
1590
+ // ── from interactions/identity-intake.schema.json ──
1591
+ /**
1592
+ * The `spec` carried on `interaction_required` for kind `identity_intake`: which fields to collect.
1593
+ */
1594
+ export interface IdentityIntakeSpec {
1595
+ /**
1596
+ * The identity fields to collect, in display order.
1597
+ *
1598
+ * @minItems 1
1599
+ */
1600
+ fields: [
1601
+ {
1602
+ /**
1603
+ * Which identity field to collect.
1604
+ */
1605
+ key: 'name' | 'email' | 'phone';
1606
+ /**
1607
+ * Whether the visitor must provide this field to submit.
1608
+ */
1609
+ required: boolean;
1610
+ /**
1611
+ * Optional display label overriding the client's default for this field.
1612
+ */
1613
+ label?: string;
1614
+ },
1615
+ ...{
1616
+ /**
1617
+ * Which identity field to collect.
1618
+ */
1619
+ key: 'name' | 'email' | 'phone';
1620
+ /**
1621
+ * Whether the visitor must provide this field to submit.
1622
+ */
1623
+ required: boolean;
1624
+ /**
1625
+ * Optional display label overriding the client's default for this field.
1626
+ */
1627
+ label?: string;
1628
+ }[],
1629
+ ];
1630
+ }
1631
+
1632
+ // ── from interactions/identity-intake.schema.json ──
1633
+ /**
1634
+ * The `values` a client submits via `submit_interaction` for kind `identity_intake`. Validated server-side: required fields present, email shape, phone normalized to E.164.
1635
+ */
1636
+ export interface IdentityIntakeValues {
1637
+ /**
1638
+ * The visitor's display name.
1639
+ */
1640
+ name?: string;
1641
+ /**
1642
+ * The visitor's email address (validated server-side).
1643
+ */
1644
+ email?: string;
1645
+ /**
1646
+ * The visitor's phone number (normalized server-side to E.164).
1647
+ */
1648
+ phone?: string;
1649
+ }
1650
+
1651
+ // ── from interactions/identity-intake.schema.json ──
1652
+ /**
1653
+ * The canonical validated payload the parked turn resumes with (identical on the form and conversational paths). On success the server also attaches the identity to the session (metadata `userName` / `contactEmail` / `contactPhone` — the same keys the OTP contact seam reads).
1654
+ */
1655
+ export interface IdentityIntakePayload {
1656
+ /**
1657
+ * How the interaction resolved.
1658
+ */
1659
+ status: 'submitted' | 'declined' | 'no_response';
1660
+ /**
1661
+ * Present when `status` is `submitted`: the validated, normalized values.
1662
+ */
1663
+ values?: {
1664
+ name?: string;
1665
+ email?: string;
1666
+ /**
1667
+ * E.164-normalized (`+15551234567`).
1668
+ */
1669
+ phone?: string;
1670
+ };
1671
+ /**
1672
+ * Guidance for the agent when `status` is `declined` / `no_response`.
1673
+ */
1674
+ message?: string;
1675
+ }
package/src/types.ts CHANGED
@@ -22,6 +22,8 @@ import type {
22
22
  GetSessionRequest,
23
23
  GetSessionResponse,
24
24
  ImmediateResponse,
25
+ InteractionInvalid,
26
+ InteractionRequired,
25
27
  Keepalive,
26
28
  OtpInvalid,
27
29
  OtpSent,
@@ -33,6 +35,7 @@ import type {
33
35
  SendMessageResponse,
34
36
  StreamChunk,
35
37
  StreamToken,
38
+ SubmitInteractionRequest,
36
39
  VerifyOtpRequest,
37
40
  WriteConfirmationRequired,
38
41
  } from './generated/types.js';
@@ -55,6 +58,7 @@ export const ACTION_TYPES = [
55
58
  'get_conversation_messages',
56
59
  'confirm_tool_action',
57
60
  'verify_otp',
61
+ 'submit_interaction',
58
62
  'ping',
59
63
  ] as const;
60
64
  export type ActionType = (typeof ACTION_TYPES)[number];
@@ -71,6 +75,8 @@ export const EVENT_TYPES = [
71
75
  'otp_sent',
72
76
  'otp_verified',
73
77
  'otp_invalid',
78
+ 'interaction_required',
79
+ 'interaction_invalid',
74
80
  'error',
75
81
  'pong',
76
82
  ] as const;
@@ -89,6 +95,7 @@ export type ClientAction =
89
95
  | GetMessagesRequest
90
96
  | ConfirmToolActionRequest
91
97
  | VerifyOtpRequest
98
+ | SubmitInteractionRequest
92
99
  | PingRequest;
93
100
 
94
101
  // ───────────────────────────── Server events ───────────────────────────────
@@ -108,6 +115,8 @@ export type ServerEvent =
108
115
  | OtpSent
109
116
  | OtpVerified
110
117
  | OtpInvalid
118
+ | InteractionRequired
119
+ | InteractionInvalid
111
120
  | GeneratedErrorEvent
112
121
  | Pong;
113
122
 
@@ -125,6 +134,8 @@ export interface ServerEventByType {
125
134
  otp_sent: OtpSent;
126
135
  otp_verified: OtpVerified;
127
136
  otp_invalid: OtpInvalid;
137
+ interaction_required: InteractionRequired;
138
+ interaction_invalid: InteractionInvalid;
128
139
  error: GeneratedErrorEvent;
129
140
  pong: Pong;
130
141
  }
@@ -137,6 +148,7 @@ export interface ClientActionByType {
137
148
  get_conversation_messages: GetMessagesRequest;
138
149
  confirm_tool_action: ConfirmToolActionRequest;
139
150
  verify_otp: VerifyOtpRequest;
151
+ submit_interaction: SubmitInteractionRequest;
140
152
  ping: PingRequest;
141
153
  }
142
154
 
package/src/validate.ts CHANGED
@@ -47,6 +47,8 @@ const EVENT_SCHEMA_FILE: Record<EventType, string> = {
47
47
  otp_sent: 'events/otp-sent.schema.json',
48
48
  otp_verified: 'events/otp-verified.schema.json',
49
49
  otp_invalid: 'events/otp-invalid.schema.json',
50
+ interaction_required: 'events/interaction-required.schema.json',
51
+ interaction_invalid: 'events/interaction-invalid.schema.json',
50
52
  error: 'events/error.schema.json',
51
53
  pong: 'events/pong.schema.json',
52
54
  };
@@ -59,6 +61,7 @@ const ACTION_SCHEMA_REF: Record<ActionType, string> = {
59
61
  get_conversation_messages: 'actions/get-messages.schema.json#/$defs/Request',
60
62
  confirm_tool_action: 'actions/confirm-tool-action.schema.json#/$defs/Request',
61
63
  verify_otp: 'actions/verify-otp.schema.json#/$defs/Request',
64
+ submit_interaction: 'actions/submit-interaction.schema.json#/$defs/Request',
62
65
  ping: 'actions/ping.schema.json#/$defs/Request',
63
66
  };
64
67
 
@@ -84,7 +87,7 @@ export class ProtocolValidator {
84
87
 
85
88
  const validator = new ProtocolValidator(ajv);
86
89
 
87
- for (const sub of ['', 'actions', 'events', 'domain']) {
90
+ for (const sub of ['', 'actions', 'events', 'domain', 'interactions']) {
88
91
  const dir = sub ? join(specDir, sub) : specDir;
89
92
  const entries = await readdir(dir, { withFileTypes: true });
90
93
  for (const e of entries) {