@agentshouse/mdruntime-adapter 0.1.0-alpha.2 → 0.1.0-alpha.21

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 (49) hide show
  1. package/AGENTS.md +120 -22
  2. package/dist/actions.d.ts +3 -11
  3. package/dist/actions.d.ts.map +1 -1
  4. package/dist/actions.js +118 -223
  5. package/dist/actions.js.map +1 -1
  6. package/dist/adapter.d.ts +3 -1
  7. package/dist/adapter.d.ts.map +1 -1
  8. package/dist/adapter.js +45 -101
  9. package/dist/adapter.js.map +1 -1
  10. package/dist/discovery.d.ts +34 -7
  11. package/dist/discovery.d.ts.map +1 -1
  12. package/dist/discovery.js +132 -29
  13. package/dist/discovery.js.map +1 -1
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/model.d.ts +19 -0
  18. package/dist/model.d.ts.map +1 -0
  19. package/dist/model.js +64 -0
  20. package/dist/model.js.map +1 -0
  21. package/dist/read.d.ts +16 -12
  22. package/dist/read.d.ts.map +1 -1
  23. package/dist/read.js +109 -30
  24. package/dist/read.js.map +1 -1
  25. package/dist/refusal.d.ts +4 -1
  26. package/dist/refusal.d.ts.map +1 -1
  27. package/dist/refusal.js +13 -0
  28. package/dist/refusal.js.map +1 -1
  29. package/dist/site.d.ts +4 -0
  30. package/dist/site.d.ts.map +1 -0
  31. package/dist/site.js +205 -0
  32. package/dist/site.js.map +1 -0
  33. package/dist/stream.d.ts +7 -5
  34. package/dist/stream.d.ts.map +1 -1
  35. package/dist/stream.js +117 -82
  36. package/dist/stream.js.map +1 -1
  37. package/dist/transport.d.ts +16 -5
  38. package/dist/transport.d.ts.map +1 -1
  39. package/dist/transport.js +5 -1
  40. package/dist/transport.js.map +1 -1
  41. package/dist/version.d.ts +14 -0
  42. package/dist/version.d.ts.map +1 -0
  43. package/dist/version.js +29 -0
  44. package/dist/version.js.map +1 -0
  45. package/dist/write.d.ts +6 -0
  46. package/dist/write.d.ts.map +1 -1
  47. package/dist/write.js +24 -7
  48. package/dist/write.js.map +1 -1
  49. package/package.json +3 -3
package/AGENTS.md CHANGED
@@ -7,36 +7,134 @@ enforcement.
7
7
  ## The MD Runtime API
8
8
 
9
9
  App code reaches the MD Runtime through the remote client. The surfaces are
10
- Rooms, Query, Observe, Types, Draft, Save, Queue, History, Call, Log,
11
- storage loss, the reported persistence answer, Sync Status, reauthorization,
12
- conversation, the unsent instruction, and Presentation State.
13
-
14
- Rooms, Query, Observe, Types, Draft, and Save never touch the network.
10
+ Rooms, Slots, Query, Search, Observe, Types, Room Draft, Save, History, Call, Log,
11
+ storage loss, the reported persistence answer, deployment facts, Sync Status, reauthorization,
12
+ conversation, the chat draft, and Presentation State.
13
+
14
+ Every `observe` resolves, once its first value has arrived, to a handle:
15
+ `current()` is the latest complete value, `subscribe(listener)` hears each later
16
+ value whole and returns its unsubscribe, and `close()` ends the observation.
17
+ Closing the port ends every handle and rejects every pending call and
18
+ observation; a client that reconnects observes again.
19
+
20
+ Each conversation a Provider card names is held complete in the browser.
21
+ Observe one with `observe('conversation', id, { limit })`: the view
22
+ holds its latest `limit` messages, or all of them, oldest first, with the
23
+ growing text and the session summary while it is viewed, and its chat draft.
24
+ `chatDraft.write` and `chatDraft.discard` keep what the App Creator composes;
25
+ sending it is a Provider Action that needs the Provider reachable and is never
26
+ queued.
27
+
28
+ Slots answers each Slot the Provider declares, in its order, with its entries
29
+ in order, each a Room and its segment; a Room two Slots list is the same Room.
30
+
31
+ Rooms, Slots, Query, Search, Observe, Types, Room Draft, and Save never touch the
32
+ network.
15
33
  History and Call go to the Provider through the Adapter. Every member of the
16
34
  client is asynchronous. Results are detached snapshots. Nothing in this
17
35
  surface returns the authorization, performs a request the App's code
18
36
  composes, or exposes the browser store.
19
37
 
38
+ Read deployment facts with `deploymentFacts()` and observe replacements with
39
+ `observe('deployment-facts')`. Interpret the opaque value using the
40
+ host's contract. [R038](https://github.com/agentshouse/mdruntime/blob/main/docs/requirements/ledger.md#r038--runtime-host-and-remote-client)
41
+ owns their boundary.
42
+
20
43
  The in-memory Adapter at `@agentshouse/mdruntime/memory` is for tests and
21
44
  ships in no bundle.
22
45
 
23
- ## Draft, Save, and outcome rules
24
-
25
- A Draft is durable: it survives reload and navigation. Save takes structured
26
- changes, validates locally, and on success appends one Change Batch to that
27
- Room's queue. A local validation failure leaves the Draft and creates
28
- nothing.
46
+ ## Local Query rules
47
+
48
+ `query(q)` answers once from local state; `observe('query', q)` holds the whole
49
+ result and hears it again only when it changes, keeping every unchanged Document
50
+ the same object, so a component can tell what changed by identity. A query is
51
+ data:
52
+
53
+ - **Filter.** `rooms` names Room locators and defaults to every Room. `types`
54
+ names MD Types, and `any` names the untyped Documents. `pathPrefix` is a
55
+ directory: `notes` keeps `notes/a.md` and not `notes2/a.md`.
56
+ - **Fields.** `where` names frontmatter fields, ANDs them, and nests `$and` and
57
+ `$or`. A bare value is equality; the operators are `$eq`, `$ne`, `$in`, `$nin`,
58
+ `$gt`, `$gte`, `$lt`, `$lte`, `$all`, `$size` and `$exists`. Equality on a list
59
+ asks whether it contains the value. Missing, null and empty differ:
60
+ `$exists: false` keeps a missing field, `null` an explicit null, `''` empty
61
+ text and `$size: 0` an empty list. A date compares in calendar order and never
62
+ with a date-time. A reference compares by the Room Path it resolves to.
63
+ Untyped frontmatter is plain values. There is no text containment or regular
64
+ expression.
65
+ - **Relations.** `hop: { field, types, where, pathPrefix }` keeps Documents whose
66
+ reference `field` points to a matching Document of the same Room.
67
+ `backlinks: { field, types, where, pathPrefix }` keeps Documents that a
68
+ matching Document of the same Room references. Each keeps a Document once.
69
+ - **Parts.** A Document query must `select`: `'all'`, or `fields`, `sections`,
70
+ `preamble`, `markdown` and `backlinks`, where `fields` and `sections` are
71
+ `'all'` or a list of names. Room, Path and MD Type always return, and an
72
+ unselected part is absent. Select only what a screen shows: a list that selects
73
+ the Markdown hears every edit of it.
74
+ - **Order and groups.** `orderBy` sorts by `room`, `path`, `type` or `{ field }`
75
+ with TanStack DB's own options, and ties end with Room, then Path. `groupBy` by
76
+ `room`, `type` or `{ field }` answers `{ groups: [{ key, count }] }`, counted
77
+ over every match and ordered by `count` or a key. `limit` bounds the Documents
78
+ or the groups.
79
+ - **Results and errors.** A result carries no Room Version: read freshness from
80
+ Rooms and Sync Status. An invalid query fails with `invalid-query`,
81
+ `unknown-type`, `unknown-field` or `wrong-kind`, naming where, and never
82
+ answers an empty result in its place.
83
+
84
+ ## Local Search rules
85
+
86
+ `search({ text, documents, conversations, limit })` answers once, by words, from
87
+ local state, with MiniSearch at its default configuration. There is no observed
88
+ search.
89
+
90
+ - **What is found.** A Document by its field values, its sections' headings and
91
+ text, its preamble and its Path; a message by its text. Frontmatter keys, Room
92
+ Drafts, chat drafts and growing text are never found. There are no phrases,
93
+ word forms, typo tolerance or prefix matching.
94
+ - **Narrowing.** `documents` is a Local Query filter: `rooms`, `types`, `where`,
95
+ `pathPrefix`, `hop` and `backlinks`. `conversations` names conversations. Both
96
+ apply before `limit`, which bounds each list; without it every hit returns.
97
+ - **Results.** `{ documents, messages }`, each ranked by `score`, which compares
98
+ hits of one list of one search. A Document hit names its `room`, `path` and
99
+ `type`, a message hit its `conversation` and `identity`, and `matches` maps each
100
+ matched word to the parts it matched in: `fields`, `sections`, `preamble` or
101
+ `path`, or `text`. A hit carries no position; mark the words in the text you
102
+ show. An invalid filter fails with its Local Query error.
103
+
104
+ ## Room Draft, Save, and outcome rules
105
+
106
+ The MD Runtime owns one Room Draft per Room and Path, shared by every App and
107
+ tab. It is durable: it survives reload and navigation. A Room that Discovery
108
+ stops listing takes its Room Drafts, its queue and their outcomes with it.
109
+ Write a Room Draft with `roomDraft.write(room, path, revision, parts)`, naming
110
+ the revision you read, or `null` when there was none, and every part it should
111
+ now change: fields, sections and the preamble, or the whole Markdown, a
112
+ creation, a move, or a removal. A part new to the Room Draft names its base,
113
+ the value you showed when it was opened; the MD Runtime keeps a part's first
114
+ base. A write or a discard from an older revision is refused as `stale` with
115
+ the current Room Draft; read it and write again.
116
+
117
+ Save takes one Room and the Room Drafts of it to save, each with its revision,
118
+ validates locally, and on success appends one Change Batch to that Room's
119
+ queue and removes those Room Drafts. It is refused as `stale`, as
120
+ `changed-underneath` naming each part whose base no longer matches the visible
121
+ state, or by a local validation rule, and then creates nothing and leaves every
122
+ Room Draft in place. A directory move is a move part on each Document beneath
123
+ it, saved together.
29
124
 
30
125
  Outcomes follow the Provider's answer. Accepted advances the confirmed
31
- layer. Conflicted and Rejected leave the head in place, carry the affected
32
- current state and the Provider's structured refusal code and fields as
33
- returned, and block that Room's queue. The App's code resolves a head by
34
- discarding it or by saving a corrected Change Batch that replaces it in
35
- place. Transport failure and Provider unavailability keep work Pending and
36
- do not infer a rejection. A refusal the Provider marks retryable stays
37
- Pending until the reset it names; the MD Runtime retries the same Change
38
- Batch identity and payload and never infers an outcome from transport
39
- uncertainty. The MD Runtime never combines, reorders, or rebases.
126
+ layer. Conflicted and Rejected, an expired outcome record, and a change that no
127
+ longer applies over the Room each leave the queue and return as Room Drafts on
128
+ the Documents they changed, marked with why: the Provider's structured refusal
129
+ code and fields as returned, not known to have applied, or withdrawn locally.
130
+ The rest of the queue keeps synchronizing, and Sync Status requires attention
131
+ until each returned Room Draft is saved again or discarded. `outcome` answers
132
+ the saved Change Batch's outcome, and `null` while it is Pending. Transport
133
+ failure and Provider unavailability keep work Pending and do not infer a
134
+ rejection. A refusal the Provider marks retryable stays Pending until the reset
135
+ it names; the MD Runtime retries the same Change Batch identity and payload and
136
+ never infers an outcome from transport uncertainty. The MD Runtime never
137
+ combines, reorders, or rebases.
40
138
 
41
139
  ## The neutrality rule
42
140
 
@@ -61,8 +159,8 @@ a desktop. Its strings are English only, dates and numbers follow the
61
159
  browser locale, and content is never translated. Light and dark themes come
62
160
  from CSS custom properties with the browser's preference as the default and
63
161
  an App's override respected. What an App Creator's agent adds carries none
64
- of these promises by itself. Reading and querying never wait for the
65
- network. Data lives only in the App Creator's browser and with the Provider.
162
+ of these promises by itself. Reading, querying and searching never wait for
163
+ the network. Data lives only in the App Creator's browser and with the Provider.
66
164
 
67
165
  ## App code imports only the client entry and never constructs a Runtime
68
166
 
package/dist/actions.d.ts CHANGED
@@ -1,16 +1,8 @@
1
- import { type ActionDeclaration, type ActionRequest, type ActionResult, type ConversationCards } from '@agentshouse/mdruntime';
1
+ import { type AcceptedMessage, type ActionDeclaration, type ActionRequest, type ActionResult, type ConversationCards } from '@agentshouse/mdruntime';
2
2
  import type { HouseDoorAnswer, HouseTransport } from './transport.js';
3
- export declare const CONVERSATION_ACTIONS: readonly ['conversation.create', 'conversation.send', 'conversation.answer', 'conversation.interrupt', 'conversation.kill', 'conversation.cancel', 'conversation.delete', 'conversation.history'];
4
- export declare const HOUSE_ACTS: readonly ['conversations/interaction/read', 'conversations/originals/read', 'apps/read', 'apps/customize', 'rooms/read', 'rooms/access/read', 'rooms/access/invitations', 'rooms/access/entries', 'rooms/access/invite', 'rooms/access/invitation-cancel', 'rooms/access/revoke', 'rooms/access/leave', 'rooms/roles/define', 'rooms/roles/remove', 'rooms/roles/assign', 'rooms/roles/unassign', 'rooms/settings/processing/read', 'rooms/settings/processing/write', 'profiles/create', 'profiles/read', 'profiles/edit', 'profiles/duplicate', 'profiles/delegate', 'agents/create', 'agents/read', 'routines/requests', 'routines/access', 'routines/owned', 'routines/granted', 'routines/grant', 'routines/send', 'routines/cancel', 'routines/start', 'routines/schedules', 'routines/schedules/create', 'routines/schedules/enabled', 'account/security/read', 'storage/inventory', 'storage/usage', 'usage/statement', 'storage/blob-store', 'storage/blob-store/select', 'storage/connections', 'storage/archive', 'storage/archive/status', 'environments/read', 'environments/create', 'environments/start', 'environments/stop', 'environments/delete', 'environments/renewals/read', 'environments/work/read', 'nodes/read'];
5
- export declare const HOUSE_PROVIDER_ACTIONS: readonly ["conversation.create", "conversation.send", "conversation.answer", "conversation.interrupt", "conversation.kill", "conversation.cancel", "conversation.delete", "conversation.history", "conversations/interaction/read", "conversations/originals/read", "apps/read", "apps/customize", "rooms/read", "rooms/access/read", "rooms/access/invitations", "rooms/access/entries", "rooms/access/invite", "rooms/access/invitation-cancel", "rooms/access/revoke", "rooms/access/leave", "rooms/roles/define", "rooms/roles/remove", "rooms/roles/assign", "rooms/roles/unassign", "rooms/settings/processing/read", "rooms/settings/processing/write", "profiles/create", "profiles/read", "profiles/edit", "profiles/duplicate", "profiles/delegate", "agents/create", "agents/read", "routines/requests", "routines/access", "routines/owned", "routines/granted", "routines/grant", "routines/send", "routines/cancel", "routines/start", "routines/schedules", "routines/schedules/create", "routines/schedules/enabled", "account/security/read", "storage/inventory", "storage/usage", "usage/statement", "storage/blob-store", "storage/blob-store/select", "storage/connections", "storage/archive", "storage/archive/status", "environments/read", "environments/create", "environments/start", "environments/stop", "environments/delete", "environments/renewals/read", "environments/work/read", "nodes/read"];
6
- export declare const HISTORY_PAGE = 100;
7
3
  export declare const HOUSE_CONVERSATION_CARDS: ConversationCards;
8
- export declare function declaredActions(): readonly ActionDeclaration[];
4
+ export declare function declaredActions(routes: readonly string[]): readonly ActionDeclaration[];
9
5
  export declare function houseAction(transport: HouseTransport, request: ActionRequest): Promise<ActionResult>;
10
6
  export declare function actionOutcome(answer: HouseDoorAnswer, name: string): ActionResult;
11
- export declare function readConversationHistory(transport: HouseTransport, conversationId: string): Promise<{
12
- messages: unknown[];
13
- } | {
14
- outcome: ActionResult;
15
- }>;
7
+ export declare function acceptedMessage(entry: unknown): AcceptedMessage | null;
16
8
  //# sourceMappingURL=actions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACvB,MAAM,wBAAwB,CAAC;AAGhC,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEtE,eAAO,MAAM,oBAAoB,YAC/B,qBAAqB,EACrB,mBAAmB,EACnB,qBAAqB,EACrB,wBAAwB,EACxB,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,CACd,CAAC;AAEX,eAAO,MAAM,UAAU,YACrB,gCAAgC,EAChC,8BAA8B,EAC9B,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,mBAAmB,EACnB,0BAA0B,EAC1B,sBAAsB,EACtB,qBAAqB,EACrB,gCAAgC,EAChC,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,sBAAsB,EACtB,gCAAgC,EAChC,iCAAiC,EACjC,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,EACf,aAAa,EACb,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,EACpB,2BAA2B,EAC3B,4BAA4B,EAC5B,uBAAuB,EACvB,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,oBAAoB,EACpB,2BAA2B,EAC3B,qBAAqB,EACrB,iBAAiB,EACjB,wBAAwB,EACxB,mBAAmB,EACnB,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACrB,4BAA4B,EAC5B,wBAAwB,EACxB,YAAY,CACJ,CAAC;AAEX,eAAO,MAAM,sBAAsB,o2CAAoD,CAAC;AAIxF,eAAO,MAAM,YAAY,MAAM,CAAC;AAiBhC,eAAO,MAAM,wBAAwB,EAAE,iBAKtC,CAAC;AAEF,wBAAgB,eAAe,IAAI,SAAS,iBAAiB,EAAE,CAM9D;AAED,wBAAgB,WAAW,CACzB,SAAS,EAAE,cAAc,EACzB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,YAAY,CAAC,CAWvB;AA4DD,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,CASjF;AA6ID,wBAAsB,uBAAuB,CAC3C,SAAS,EAAE,cAAc,EACzB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC;IAAE,QAAQ,EAAE,OAAO,EAAE,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,YAAY,CAAA;CAAE,CAAC,CA4B9D"}
1
+ {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EAEvB,MAAM,wBAAwB,CAAC;AAGhC,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAiBtE,eAAO,MAAM,wBAAwB,EAAE,iBAKtC,CAAC;AAEF,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,iBAAiB,EAAE,CASvF;AAED,wBAAgB,WAAW,CACzB,SAAS,EAAE,cAAc,EACzB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,YAAY,CAAC,CAMvB;AAoED,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,CAWjF;AAuID,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,GAAG,IAAI,CAUtE"}
package/dist/actions.js CHANGED
@@ -1,121 +1,78 @@
1
- import { VERSION } from '@agentshouse/mdmodel';
2
1
  import { changeBatchIdentity } from '@agentshouse/mdruntime';
3
2
  import { doorRefusal } from './refusal.js';
4
- import { PRIVATE_AUTHORITY } from './transport.js';
5
- export const CONVERSATION_ACTIONS = [
6
- 'conversation.create',
7
- 'conversation.send',
8
- 'conversation.answer',
9
- 'conversation.interrupt',
10
- 'conversation.kill',
11
- 'conversation.cancel',
12
- 'conversation.delete',
13
- 'conversation.history'
14
- ];
15
- export const HOUSE_ACTS = [
16
- 'conversations/interaction/read',
17
- 'conversations/originals/read',
18
- 'apps/read',
19
- 'apps/customize',
20
- 'rooms/read',
21
- 'rooms/access/read',
22
- 'rooms/access/invitations',
23
- 'rooms/access/entries',
24
- 'rooms/access/invite',
25
- 'rooms/access/invitation-cancel',
26
- 'rooms/access/revoke',
27
- 'rooms/access/leave',
28
- 'rooms/roles/define',
29
- 'rooms/roles/remove',
30
- 'rooms/roles/assign',
31
- 'rooms/roles/unassign',
32
- 'rooms/settings/processing/read',
33
- 'rooms/settings/processing/write',
34
- 'profiles/create',
35
- 'profiles/read',
36
- 'profiles/edit',
37
- 'profiles/duplicate',
38
- 'profiles/delegate',
39
- 'agents/create',
40
- 'agents/read',
41
- 'routines/requests',
42
- 'routines/access',
43
- 'routines/owned',
44
- 'routines/granted',
45
- 'routines/grant',
46
- 'routines/send',
47
- 'routines/cancel',
48
- 'routines/start',
49
- 'routines/schedules',
50
- 'routines/schedules/create',
51
- 'routines/schedules/enabled',
52
- 'account/security/read',
53
- 'storage/inventory',
54
- 'storage/usage',
55
- 'usage/statement',
56
- 'storage/blob-store',
57
- 'storage/blob-store/select',
58
- 'storage/connections',
59
- 'storage/archive',
60
- 'storage/archive/status',
61
- 'environments/read',
62
- 'environments/create',
63
- 'environments/start',
64
- 'environments/stop',
65
- 'environments/delete',
66
- 'environments/renewals/read',
67
- 'environments/work/read',
68
- 'nodes/read'
69
- ];
70
- export const HOUSE_PROVIDER_ACTIONS = [
71
- ...CONVERSATION_ACTIONS,
72
- ...HOUSE_ACTS
73
- ];
74
- const DECLARED = new Set(HOUSE_PROVIDER_ACTIONS);
75
- export const HISTORY_PAGE = 100;
3
+ import { PRIVATE_LOCATOR } from './transport.js';
4
+ const HISTORY_PAGE = 100;
76
5
  const OCTET_STREAM = 'application/octet-stream';
77
- const CONVERSATION_ROUTES = {
78
- 'conversation.create': '/door/conversations/create',
79
- 'conversation.send': '/door/conversations/send',
80
- 'conversation.answer': '/door/conversations/interaction/answer',
81
- 'conversation.interrupt': '/door/conversations/interrupt',
82
- 'conversation.kill': '/door/conversations/kill',
83
- 'conversation.cancel': '/door/conversations/cancel',
84
- 'conversation.delete': '/door/conversations/delete',
85
- 'conversation.history': '/door/conversations/history',
86
- 'conversations/interaction/read': '/door/conversations/interaction/read',
87
- 'conversations/originals/read': '/door/conversations/originals/read'
88
- };
6
+ const CONVERSATION_ROUTES = new Map([
7
+ [
8
+ 'conversation.create',
9
+ '/door/conversations/create'
10
+ ],
11
+ [
12
+ 'conversation.send',
13
+ '/door/conversations/send'
14
+ ],
15
+ [
16
+ 'conversation.answer',
17
+ '/door/conversations/interaction/answer'
18
+ ],
19
+ [
20
+ 'conversation.interrupt',
21
+ '/door/conversations/interrupt'
22
+ ],
23
+ [
24
+ 'conversation.kill',
25
+ '/door/conversations/kill'
26
+ ],
27
+ [
28
+ 'conversation.cancel',
29
+ '/door/conversations/cancel'
30
+ ],
31
+ [
32
+ 'conversation.delete',
33
+ '/door/conversations/delete'
34
+ ],
35
+ [
36
+ 'conversation.history',
37
+ '/door/conversations/history'
38
+ ]
39
+ ]);
89
40
  export const HOUSE_CONVERSATION_CARDS = {
90
- locator: PRIVATE_AUTHORITY,
41
+ locator: PRIVATE_LOCATOR,
91
42
  type: 'conversation',
92
43
  identity: 'conversation',
93
44
  agent: 'agent'
94
45
  };
95
- export function declaredActions() {
96
- return HOUSE_PROVIDER_ACTIONS.map((name)=>({
97
- name,
46
+ export function declaredActions(routes) {
47
+ const mapped = new Map([
48
+ ...CONVERSATION_ROUTES
49
+ ].map(([name, route])=>[
50
+ route.slice('/door/'.length),
51
+ name
52
+ ]));
53
+ return routes.map((route)=>({
54
+ name: mapped.get(route) ?? route,
98
55
  inputShape: {},
99
56
  resultKind: 'house'
100
57
  }));
101
58
  }
102
59
  export function houseAction(transport, request) {
103
- if (!DECLARED.has(request.name)) {
104
- return Promise.resolve({
105
- kind: 'rejected',
106
- code: 'action-unimplemented',
107
- fields: {
108
- name: request.name,
109
- mdModel: VERSION
110
- }
111
- });
112
- }
113
60
  if (request.name === 'conversation.send') return sendAction(transport, request.input);
114
61
  if (request.name === 'conversation.history') return historyAction(transport, request.input);
115
- return carry(transport, request.name, request.input);
62
+ const route = routeOf(request.name);
63
+ if (route === null) return Promise.resolve(malformed());
64
+ return carry(transport, route, request.name, request.input);
116
65
  }
117
66
  function routeOf(name) {
118
- return CONVERSATION_ROUTES[name] ?? `/door/${name}`;
67
+ const mapped = CONVERSATION_ROUTES.get(name);
68
+ if (mapped !== undefined) return mapped;
69
+ const parts = name.split('/');
70
+ if (parts.some((part)=>part === '.' || part === '..')) return null;
71
+ try {
72
+ return `/door/${parts.map(encodeURIComponent).join('/')}`;
73
+ } catch {
74
+ return null;
75
+ }
119
76
  }
120
77
  function record(value) {
121
78
  return typeof value === 'object' && value !== null ? value : {};
@@ -186,6 +143,13 @@ export function actionOutcome(answer, name) {
186
143
  fields: refusal.fields
187
144
  };
188
145
  }
146
+ if (answer.status === 404) return {
147
+ kind: 'rejected',
148
+ code: 'action-unimplemented',
149
+ fields: {
150
+ name
151
+ }
152
+ };
189
153
  if (answer.status === 0 || answer.status >= 400) return {
190
154
  kind: 'transport-uncertainty'
191
155
  };
@@ -198,8 +162,8 @@ export function actionOutcome(answer, name) {
198
162
  result: answer.body
199
163
  };
200
164
  }
201
- async function carry(transport, name, input) {
202
- const answer = await transport.door(routeOf(name), bodyOf(name, input));
165
+ async function carry(transport, route, name, input) {
166
+ const answer = await transport.door(route, bodyOf(name, input));
203
167
  return actionOutcome(answer, name);
204
168
  }
205
169
  function sendFile(entry) {
@@ -307,148 +271,79 @@ async function sendAction(transport, input) {
307
271
  return outcome;
308
272
  }
309
273
  function historyCursor(value) {
310
- let parsed;
311
- try {
312
- parsed = JSON.parse(value);
313
- } catch {
314
- return null;
315
- }
316
- const acceptedAt = text(parsed, 'accepted_at');
317
- const messageId = text(parsed, 'message_id');
274
+ const acceptedAt = text(value, 'accepted_at');
275
+ const messageId = text(value, 'message_id');
318
276
  if (acceptedAt === undefined || messageId === undefined) return null;
319
277
  return {
320
278
  accepted_at: acceptedAt,
321
279
  message_id: messageId
322
280
  };
323
281
  }
324
- function turned(entry) {
325
- const turn = text(entry, 'message_id');
326
- if (turn === undefined) return null;
327
- return {
328
- turn,
329
- message: structuredClone(entry)
330
- };
331
- }
332
- function olderCursor(entry) {
333
- const acceptedAt = text(entry.message, 'accepted_at');
334
- const messageId = text(entry.message, 'message_id');
335
- if (acceptedAt === undefined || messageId === undefined) return null;
282
+ function position(cursor) {
336
283
  return JSON.stringify({
337
- accepted_at: acceptedAt,
338
- message_id: messageId
284
+ accepted_at: cursor.accepted_at,
285
+ message_id: cursor.message_id
339
286
  });
340
287
  }
341
- export async function readConversationHistory(transport, conversationId) {
342
- const messages = [];
343
- let after;
344
- for(;;){
345
- const answer = await transport.door('/door/conversations/history', {
346
- conversation_id: conversationId,
347
- ...after === undefined ? {} : {
348
- after
349
- },
350
- limit: HISTORY_PAGE
351
- });
352
- const outcome = actionOutcome(answer, 'conversation.history');
353
- if (outcome.kind !== 'action') return {
354
- outcome
355
- };
356
- const held = record(outcome.result);
357
- if (!Array.isArray(held.messages)) return {
358
- outcome: {
359
- kind: 'transport-uncertainty'
360
- }
361
- };
362
- for (const entry of held.messages){
363
- if (text(entry, 'message_id') === undefined) {
364
- return {
365
- outcome: {
366
- kind: 'transport-uncertainty'
367
- }
368
- };
369
- }
370
- messages.push(entry);
371
- }
372
- if (held.next === null || held.next === undefined) return {
373
- messages
374
- };
375
- const next = record(held.next);
376
- const acceptedAt = text(next, 'accepted_at');
377
- const messageId = text(next, 'message_id');
378
- if (acceptedAt === undefined || messageId === undefined) {
379
- return {
380
- outcome: {
381
- kind: 'transport-uncertainty'
382
- }
383
- };
384
- }
385
- after = {
386
- accepted_at: acceptedAt,
387
- message_id: messageId
388
- };
288
+ function positionCursor(value) {
289
+ try {
290
+ return historyCursor(JSON.parse(value));
291
+ } catch {
292
+ return null;
389
293
  }
390
294
  }
391
- function historyPage(conversationId, messages, until) {
392
- const from = Math.max(0, until - HISTORY_PAGE);
393
- const page = messages.slice(from, until);
394
- const first = page[0];
395
- const cursor = from === 0 || first === undefined ? null : olderCursor(first);
396
- if (from > 0 && cursor === null) return {
397
- kind: 'transport-uncertainty'
398
- };
295
+ export function acceptedMessage(entry) {
296
+ const cursor = historyCursor(entry);
297
+ if (cursor === null) return null;
399
298
  return {
400
- kind: 'action',
401
- result: {
402
- conversation_id: conversationId,
403
- messages: page,
404
- ...cursor === null ? {} : {
405
- cursor
406
- }
407
- }
299
+ identity: cursor.message_id,
300
+ turn: cursor.message_id,
301
+ position: position(cursor),
302
+ text: text(entry, 'text') ?? '',
303
+ message: structuredClone(entry)
408
304
  };
409
305
  }
410
306
  async function historyAction(transport, input) {
411
307
  const conversationId = text(input, 'conversation');
412
308
  if (conversationId === undefined) return malformed();
413
- const cursorValue = text(input, 'cursor');
414
- let olderThan;
415
- if (cursorValue !== undefined) {
416
- const parsed = historyCursor(cursorValue);
417
- if (parsed === null) {
418
- return {
419
- kind: 'rejected',
420
- code: 'unknown-cursor',
421
- fields: {
422
- cursor: cursorValue
423
- }
424
- };
425
- }
426
- olderThan = parsed;
427
- }
428
- const held = await readConversationHistory(transport, conversationId);
429
- if ('outcome' in held) return held.outcome;
309
+ const afterValue = text(input, 'after');
310
+ const after = afterValue === undefined ? undefined : positionCursor(afterValue);
311
+ if (after === null) return malformed();
312
+ const answer = await transport.door('/door/conversations/history', {
313
+ conversation_id: conversationId,
314
+ ...after === undefined ? {} : {
315
+ after
316
+ },
317
+ limit: HISTORY_PAGE
318
+ });
319
+ const outcome = actionOutcome(answer, 'conversation.history');
320
+ if (outcome.kind !== 'action') return outcome;
321
+ const held = record(outcome.result);
322
+ if (!Array.isArray(held.messages)) return {
323
+ kind: 'transport-uncertainty'
324
+ };
430
325
  const messages = [];
431
326
  for (const entry of held.messages){
432
- const mapped = turned(entry);
433
- if (mapped === null) return {
327
+ const message = acceptedMessage(entry);
328
+ if (message === null) return {
434
329
  kind: 'transport-uncertainty'
435
330
  };
436
- messages.push(mapped);
437
- }
438
- if (olderThan === undefined) {
439
- return historyPage(conversationId, messages, messages.length);
440
- }
441
- const at = messages.findIndex((entry)=>text(entry.message, 'accepted_at') === olderThan.accepted_at && text(entry.message, 'message_id') === olderThan.message_id);
442
- if (at === -1) {
443
- return {
444
- kind: 'rejected',
445
- code: 'unknown-cursor',
446
- fields: {
447
- cursor: cursorValue
448
- }
449
- };
331
+ messages.push(message);
450
332
  }
451
- return historyPage(conversationId, messages, at);
333
+ const next = held.next === null || held.next === undefined ? undefined : historyCursor(held.next);
334
+ if (next === null) return {
335
+ kind: 'transport-uncertainty'
336
+ };
337
+ const page = {
338
+ messages,
339
+ ...next === undefined ? {} : {
340
+ next: position(next)
341
+ }
342
+ };
343
+ return {
344
+ kind: 'action',
345
+ result: page
346
+ };
452
347
  }
453
348
 
454
349
  //# sourceMappingURL=actions.js.map