@mhome/conversation-protocol 3.6.1 → 3.8.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # mhome-conversation-api
2
2
 
3
- Typed, transport-neutral DTOs and canonical surface identities for the mHome conversation API.
3
+ Typed, transport-neutral DTOs and canonical surface identities for the MeowLink conversation API.
4
4
 
5
5
  This crate owns wire targets, request and response bodies, user-visible message content, and
6
6
  conversation events. `ConversationSurface` is a canonical value object whose string form is the
@@ -11,6 +11,16 @@ contains no Agent runtime, persistence, transport implementation, or messaging-p
11
11
  threads themselves. The Conversation implementation owns active-thread creation, idle rotation,
12
12
  pending-interaction policy, idempotency, and enqueueing for that operation.
13
13
 
14
+ Each newly created `ThreadSummary` carries a durable `origin`: either
15
+ `{ "type": "turn", "requestId": "…" }` for creation/idle rotation caused by
16
+ `turn/submit`, or `{ "type": "operation", "operationId": "…" }` for an explicit
17
+ thread/session operation. The owner assigns it when committing creation; callers
18
+ cannot supply it as request metadata. It is immutable and appears in control,
19
+ catalog and thread-load projections, including queries after reconnection.
20
+ It identifies creation, not turn admission or execution. Origin can be absent
21
+ for records written before tracking was introduced; clients must not infer a
22
+ cause from operation-ID spelling, timing or the presence of an unbound send.
23
+
14
24
  The canonical `cs1` surface families are client personal (`cp`), client group (`cg`), messaging
15
25
  personal (`mp`), and messaging group (`mg`). Messaging surfaces include provider, provider account,
16
26
  external conversation, and an optional lane. A lane isolates a provider sub-conversation such as a
@@ -70,3 +80,30 @@ Final replies, failures, cancellations, queue state, and actionable user
70
80
  confirmations remain durable. Snapshot deltas keep their existing contiguous
71
81
  version/recovery rules. Agent checkpoints and billing are independent of live
72
82
  presentation delivery.
83
+
84
+ ## Waiting queue operations
85
+
86
+ `/chat/queue/update` replaces the complete content of one waiting request. Its
87
+ request ID, original admission fingerprint, author, and frozen execution settings
88
+ remain unchanged. Concurrent edits use the last successful server commit; client
89
+ clocks are irrelevant. Claiming the request for execution and editing it share the
90
+ same transactional boundary, so an edit cannot succeed after execution begins.
91
+
92
+ `/chat/queue/reorder` moves `requestId` before `beforeRequestId`; a null anchor
93
+ moves it to the tail. The server applies the move to its current queue, preserving
94
+ new arrivals and unrelated moves. A missing request or anchor is a conflict; a
95
+ client must never replace the whole queue from its stale local list.
96
+
97
+ Both operations require an `operationId`, scoped to the surface and request ID.
98
+ An exact retry returns `already_applied` and the original operation's queue version,
99
+ without replaying its changes. Reusing it with another payload is a conflict.
100
+ Command responses still carry a fresh authoritative control projection; clients
101
+ apply control versions monotonically rather than treating a mutation receipt as
102
+ a queue snapshot. Events and command responses use the same control versions.
103
+
104
+ Cancellation, including archive cleanup, retains a durable terminal admission
105
+ receipt independently of transcript retention. `/chat/request/status` reads that
106
+ receipt by request ID, returning null admission for an unknown request. It recovers
107
+ missed `admission.changed` events without reenqueuing or guessing from an empty
108
+ queue. Retry of the original submission still checks its original content, even
109
+ when the queued execution content was later edited.
@@ -11,7 +11,11 @@
11
11
  "createdAt": "2026-08-22T00:00:00Z",
12
12
  "updatedAt": "2026-08-22T00:00:00Z",
13
13
  "archivedAt": null,
14
- "purgeAfter": null
14
+ "purgeAfter": null,
15
+ "origin": {
16
+ "type": "operation",
17
+ "operationId": "session-start-2"
18
+ }
15
19
  },
16
20
  "activeRun": null,
17
21
  "pendingInteraction": null,
@@ -14,7 +14,11 @@
14
14
  "createdAt": "2026-08-22T00:00:00Z",
15
15
  "updatedAt": "2026-08-22T00:00:00Z",
16
16
  "archivedAt": null,
17
- "purgeAfter": null
17
+ "purgeAfter": null,
18
+ "origin": {
19
+ "type": "turn",
20
+ "requestId": "turn-create-2"
21
+ }
18
22
  },
19
23
  "activeRun": null,
20
24
  "pendingInteraction": null,
@@ -3,7 +3,8 @@
3
3
  "direction": "request",
4
4
  "body": {
5
5
  "threadId": "thread-2",
6
- "expectedQueueVersion": 3,
7
- "orderedRequestIds": ["request-2", "request-1"]
6
+ "requestId": "request-2",
7
+ "operationId": "move-1",
8
+ "beforeRequestId": "request-1"
8
9
  }
9
10
  }
@@ -3,8 +3,10 @@
3
3
  "direction": "response",
4
4
  "body": {
5
5
  "receipt": {
6
+ "operationId": "move-1",
7
+ "requestId": "request-2",
6
8
  "queueVersion": 4,
7
- "items": []
9
+ "disposition": "applied"
8
10
  },
9
11
  "control": {
10
12
  "surfaceId": "cs1:cp:dXNlcg",
@@ -0,0 +1,17 @@
1
+ {
2
+ "target": "/chat/queue/update",
3
+ "direction": "request",
4
+ "body": {
5
+ "threadId": "thread-2",
6
+ "requestId": "request-2",
7
+ "operationId": "edit-1",
8
+ "content": {
9
+ "parts": [
10
+ {
11
+ "type": "text",
12
+ "text": "Updated instruction"
13
+ }
14
+ ]
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "target": "/chat/queue/update",
3
+ "direction": "response",
4
+ "body": {
5
+ "receipt": {
6
+ "operationId": "edit-1",
7
+ "requestId": "request-2",
8
+ "queueVersion": 4,
9
+ "disposition": "applied"
10
+ },
11
+ "control": {
12
+ "surfaceId": "cs1:cp:dXNlcg",
13
+ "version": "00000000000000000001:00000000000000000001",
14
+ "activeThread": {
15
+ "threadId": "thread-2",
16
+ "title": "New conversation",
17
+ "state": "active",
18
+ "createdAt": "2026-08-22T00:00:00Z",
19
+ "updatedAt": "2026-08-22T00:00:00Z",
20
+ "archivedAt": null,
21
+ "purgeAfter": null
22
+ },
23
+ "activeRun": null,
24
+ "pendingInteraction": null,
25
+ "queue": {
26
+ "queueVersion": 0,
27
+ "items": []
28
+ },
29
+ "historyVersion": 0
30
+ }
31
+ }
32
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "target": "/chat/request/status",
3
+ "direction": "request",
4
+ "body": {
5
+ "requestId": "request-2"
6
+ }
7
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "target": "/chat/request/status",
3
+ "direction": "response",
4
+ "body": {
5
+ "requestId": "request-2",
6
+ "admission": {
7
+ "state": "cancelled",
8
+ "admissionVersion": 2,
9
+ "reason": "cancelled",
10
+ "threadId": "thread-2",
11
+ "queueVersion": 5
12
+ }
13
+ }
14
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "manifest": "mhome.conversation.targets.v1",
3
- "contractVersion": "3.6.1",
3
+ "contractVersion": "3.8.0",
4
4
  "requestTargets": [
5
5
  "/chat/control/get",
6
6
  "/chat/thread/list",
@@ -14,7 +14,9 @@
14
14
  "/chat/interaction/answer",
15
15
  "/chat/queue/reorder",
16
16
  "/chat/request/cancel",
17
- "/chat/interaction/submit"
17
+ "/chat/interaction/submit",
18
+ "/chat/queue/update",
19
+ "/chat/request/status"
18
20
  ],
19
21
  "eventTargets": [
20
22
  "/chat/event",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mhome/conversation-protocol",
3
- "version": "3.6.1",
3
+ "version": "3.8.0",
4
4
  "description": "Build-time protocol artifacts for mhome.conversation",
5
5
  "license": "MIT",
6
6
  "repository": {
package/protocol.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "protocol": "mhome.conversation",
3
- "contractVersion": "3.6.1",
3
+ "contractVersion": "3.8.0",
4
4
  "manifest": "manifest/targets.v1.json",
5
5
  "schemas": {
6
6
  "frame": "schema/conversation-frame.v2.schema.json",
@@ -382,6 +382,9 @@
382
382
  "type": "null"
383
383
  }
384
384
  ]
385
+ },
386
+ "origin": {
387
+ "$ref": "#/$defs/threadOrigin"
385
388
  }
386
389
  }
387
390
  },
@@ -1572,6 +1575,15 @@
1572
1575
  },
1573
1576
  {
1574
1577
  "$ref": "#/$defs/catalogCommandResponse"
1578
+ },
1579
+ {
1580
+ "$ref": "#/$defs/updateRequest"
1581
+ },
1582
+ {
1583
+ "$ref": "#/$defs/statusRequest"
1584
+ },
1585
+ {
1586
+ "$ref": "#/$defs/statusResponse"
1575
1587
  }
1576
1588
  ]
1577
1589
  },
@@ -1914,23 +1926,29 @@
1914
1926
  "additionalProperties": false,
1915
1927
  "required": [
1916
1928
  "threadId",
1917
- "expectedQueueVersion",
1918
- "orderedRequestIds"
1929
+ "requestId",
1930
+ "operationId",
1931
+ "beforeRequestId"
1919
1932
  ],
1920
1933
  "properties": {
1921
1934
  "threadId": {
1922
1935
  "$ref": "#/$defs/id"
1923
1936
  },
1924
- "expectedQueueVersion": {
1925
- "type": "integer",
1926
- "minimum": 0
1937
+ "requestId": {
1938
+ "$ref": "#/$defs/id"
1927
1939
  },
1928
- "orderedRequestIds": {
1929
- "type": "array",
1930
- "uniqueItems": true,
1931
- "items": {
1932
- "$ref": "#/$defs/id"
1933
- }
1940
+ "operationId": {
1941
+ "$ref": "#/$defs/id"
1942
+ },
1943
+ "beforeRequestId": {
1944
+ "oneOf": [
1945
+ {
1946
+ "$ref": "#/$defs/id"
1947
+ },
1948
+ {
1949
+ "type": "null"
1950
+ }
1951
+ ]
1934
1952
  }
1935
1953
  }
1936
1954
  }
@@ -2214,7 +2232,10 @@
2214
2232
  ],
2215
2233
  "properties": {
2216
2234
  "target": {
2217
- "const": "/chat/queue/reorder"
2235
+ "enum": [
2236
+ "/chat/queue/reorder",
2237
+ "/chat/queue/update"
2238
+ ]
2218
2239
  },
2219
2240
  "direction": {
2220
2241
  "const": "response"
@@ -2228,7 +2249,7 @@
2228
2249
  ],
2229
2250
  "properties": {
2230
2251
  "receipt": {
2231
- "$ref": "#/$defs/queue"
2252
+ "$ref": "#/$defs/queueMutationReceipt"
2232
2253
  },
2233
2254
  "control": {
2234
2255
  "oneOf": [
@@ -2817,6 +2838,180 @@
2817
2838
  }
2818
2839
  }
2819
2840
  }
2841
+ },
2842
+ "threadOrigin": {
2843
+ "oneOf": [
2844
+ {
2845
+ "type": "object",
2846
+ "additionalProperties": false,
2847
+ "required": [
2848
+ "type",
2849
+ "requestId"
2850
+ ],
2851
+ "properties": {
2852
+ "type": {
2853
+ "const": "turn"
2854
+ },
2855
+ "requestId": {
2856
+ "$ref": "#/$defs/id"
2857
+ }
2858
+ }
2859
+ },
2860
+ {
2861
+ "type": "object",
2862
+ "additionalProperties": false,
2863
+ "required": [
2864
+ "type",
2865
+ "operationId"
2866
+ ],
2867
+ "properties": {
2868
+ "type": {
2869
+ "const": "operation"
2870
+ },
2871
+ "operationId": {
2872
+ "$ref": "#/$defs/id"
2873
+ }
2874
+ }
2875
+ }
2876
+ ]
2877
+ },
2878
+ "updateRequest": {
2879
+ "type": "object",
2880
+ "additionalProperties": false,
2881
+ "required": [
2882
+ "target",
2883
+ "direction",
2884
+ "body"
2885
+ ],
2886
+ "properties": {
2887
+ "target": {
2888
+ "const": "/chat/queue/update"
2889
+ },
2890
+ "direction": {
2891
+ "const": "request"
2892
+ },
2893
+ "body": {
2894
+ "type": "object",
2895
+ "additionalProperties": false,
2896
+ "required": [
2897
+ "threadId",
2898
+ "requestId",
2899
+ "operationId",
2900
+ "content"
2901
+ ],
2902
+ "properties": {
2903
+ "threadId": {
2904
+ "$ref": "#/$defs/id"
2905
+ },
2906
+ "requestId": {
2907
+ "$ref": "#/$defs/id"
2908
+ },
2909
+ "operationId": {
2910
+ "$ref": "#/$defs/id"
2911
+ },
2912
+ "content": {
2913
+ "$ref": "#/$defs/content"
2914
+ }
2915
+ }
2916
+ }
2917
+ }
2918
+ },
2919
+ "queueMutationReceipt": {
2920
+ "type": "object",
2921
+ "additionalProperties": false,
2922
+ "required": [
2923
+ "operationId",
2924
+ "requestId",
2925
+ "queueVersion",
2926
+ "disposition"
2927
+ ],
2928
+ "properties": {
2929
+ "operationId": {
2930
+ "$ref": "#/$defs/id"
2931
+ },
2932
+ "requestId": {
2933
+ "$ref": "#/$defs/id"
2934
+ },
2935
+ "queueVersion": {
2936
+ "type": "integer",
2937
+ "minimum": 0
2938
+ },
2939
+ "disposition": {
2940
+ "enum": [
2941
+ "applied",
2942
+ "already_applied"
2943
+ ]
2944
+ }
2945
+ }
2946
+ },
2947
+ "statusRequest": {
2948
+ "type": "object",
2949
+ "additionalProperties": false,
2950
+ "required": [
2951
+ "target",
2952
+ "direction",
2953
+ "body"
2954
+ ],
2955
+ "properties": {
2956
+ "target": {
2957
+ "const": "/chat/request/status"
2958
+ },
2959
+ "direction": {
2960
+ "const": "request"
2961
+ },
2962
+ "body": {
2963
+ "type": "object",
2964
+ "additionalProperties": false,
2965
+ "required": [
2966
+ "requestId"
2967
+ ],
2968
+ "properties": {
2969
+ "requestId": {
2970
+ "$ref": "#/$defs/id"
2971
+ }
2972
+ }
2973
+ }
2974
+ }
2975
+ },
2976
+ "statusResponse": {
2977
+ "type": "object",
2978
+ "additionalProperties": false,
2979
+ "required": [
2980
+ "target",
2981
+ "direction",
2982
+ "body"
2983
+ ],
2984
+ "properties": {
2985
+ "target": {
2986
+ "const": "/chat/request/status"
2987
+ },
2988
+ "direction": {
2989
+ "const": "response"
2990
+ },
2991
+ "body": {
2992
+ "type": "object",
2993
+ "additionalProperties": false,
2994
+ "required": [
2995
+ "requestId",
2996
+ "admission"
2997
+ ],
2998
+ "properties": {
2999
+ "requestId": {
3000
+ "$ref": "#/$defs/id"
3001
+ },
3002
+ "admission": {
3003
+ "oneOf": [
3004
+ {
3005
+ "$ref": "#/$defs/requestAdmission"
3006
+ },
3007
+ {
3008
+ "type": "null"
3009
+ }
3010
+ ]
3011
+ }
3012
+ }
3013
+ }
3014
+ }
2820
3015
  }
2821
3016
  }
2822
3017
  }