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

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 +112 -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,126 @@ 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
+ Each conversation a Provider card names is held complete in the browser.
15
+ Observe one with `observe('conversation', id, { limit }, listener)`: the view
16
+ holds its latest `limit` messages, or all of them, oldest first, with the
17
+ growing text and the session summary while it is viewed, and its chat draft.
18
+ `chatDraft.write` and `chatDraft.discard` keep what the App Creator composes;
19
+ sending it is a Provider Action that needs the Provider reachable and is never
20
+ queued.
21
+
22
+ Slots answers each Slot the Provider declares, in its order, with its entries
23
+ in order, each a Room and its segment; a Room two Slots list is the same Room.
24
+
25
+ Rooms, Slots, Query, Search, Observe, Types, Room Draft, and Save never touch the
26
+ network.
15
27
  History and Call go to the Provider through the Adapter. Every member of the
16
28
  client is asynchronous. Results are detached snapshots. Nothing in this
17
29
  surface returns the authorization, performs a request the App's code
18
30
  composes, or exposes the browser store.
19
31
 
32
+ Read deployment facts with `deploymentFacts()` and observe replacements with
33
+ `observe('deployment-facts', listener)`. Interpret the opaque value using the
34
+ host's contract. [R038](https://github.com/agentshouse/mdruntime/blob/main/docs/requirements/ledger.md#r038--runtime-host-and-remote-client)
35
+ owns their boundary.
36
+
20
37
  The in-memory Adapter at `@agentshouse/mdruntime/memory` is for tests and
21
38
  ships in no bundle.
22
39
 
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.
40
+ ## Local Query rules
41
+
42
+ `query(q)` answers once from local state; `observe('query', q, listener)` hears
43
+ the whole result at once and again only when it changes. A query is data:
44
+
45
+ - **Filter.** `rooms` names Room locators and defaults to every Room. `types`
46
+ names MD Types, and `any` names the untyped Documents. `pathPrefix` is a
47
+ directory: `notes` keeps `notes/a.md` and not `notes2/a.md`.
48
+ - **Fields.** `where` names frontmatter fields, ANDs them, and nests `$and` and
49
+ `$or`. A bare value is equality; the operators are `$eq`, `$ne`, `$in`, `$nin`,
50
+ `$gt`, `$gte`, `$lt`, `$lte`, `$all`, `$size` and `$exists`. Equality on a list
51
+ asks whether it contains the value. Missing, null and empty differ:
52
+ `$exists: false` keeps a missing field, `null` an explicit null, `''` empty
53
+ text and `$size: 0` an empty list. A date compares in calendar order and never
54
+ with a date-time. A reference compares by the Room Path it resolves to.
55
+ Untyped frontmatter is plain values. There is no text containment or regular
56
+ expression.
57
+ - **Relations.** `hop: { field, types, where, pathPrefix }` keeps Documents whose
58
+ reference `field` points to a matching Document of the same Room.
59
+ `backlinks: { field, types, where, pathPrefix }` keeps Documents that a
60
+ matching Document of the same Room references. Each keeps a Document once.
61
+ - **Parts.** A Document query must `select`: `'all'`, or `fields`, `sections`,
62
+ `preamble`, `markdown` and `backlinks`, where `fields` and `sections` are
63
+ `'all'` or a list of names. Room, Path and MD Type always return, and an
64
+ unselected part is absent. Select only what a screen shows: a list that selects
65
+ the Markdown hears every edit of it.
66
+ - **Order and groups.** `orderBy` sorts by `room`, `path`, `type` or `{ field }`
67
+ with TanStack DB's own options, and ties end with Room, then Path. `groupBy` by
68
+ `room`, `type` or `{ field }` answers `{ groups: [{ key, count }] }`, counted
69
+ over every match and ordered by `count` or a key. `limit` bounds the Documents
70
+ or the groups.
71
+ - **Results and errors.** A result carries no Room Version: read freshness from
72
+ Rooms and Sync Status. An invalid query fails with `invalid-query`,
73
+ `unknown-type`, `unknown-field` or `wrong-kind`, naming where, and never
74
+ answers an empty result in its place.
75
+
76
+ ## Local Search rules
77
+
78
+ `search({ text, documents, conversations, limit })` answers once, by words, from
79
+ local state, with MiniSearch at its default configuration. There is no observed
80
+ search.
81
+
82
+ - **What is found.** A Document by its field values, its sections' headings and
83
+ text, its preamble and its Path; a message by its text. Frontmatter keys, Room
84
+ Drafts, chat drafts and growing text are never found. There are no phrases,
85
+ word forms, typo tolerance or prefix matching.
86
+ - **Narrowing.** `documents` is a Local Query filter: `rooms`, `types`, `where`,
87
+ `pathPrefix`, `hop` and `backlinks`. `conversations` names conversations. Both
88
+ apply before `limit`, which bounds each list; without it every hit returns.
89
+ - **Results.** `{ documents, messages }`, each ranked by `score`, which compares
90
+ hits of one list of one search. A Document hit names its `room`, `path` and
91
+ `type`, a message hit its `conversation` and `identity`, and `matches` maps each
92
+ matched word to the parts it matched in: `fields`, `sections`, `preamble` or
93
+ `path`, or `text`. A hit carries no position; mark the words in the text you
94
+ show. An invalid filter fails with its Local Query error.
95
+
96
+ ## Room Draft, Save, and outcome rules
97
+
98
+ The MD Runtime owns one Room Draft per Room and Path, shared by every App and
99
+ tab. It is durable: it survives reload and navigation. A Room that Discovery
100
+ stops listing takes its Room Drafts, its queue and their outcomes with it.
101
+ Write a Room Draft with `roomDraft.write(room, path, revision, parts)`, naming
102
+ the revision you read, or `null` when there was none, and every part it should
103
+ now change: fields, sections and the preamble, or the whole Markdown, a
104
+ creation, a move, or a removal. A part new to the Room Draft names its base,
105
+ the value you showed when it was opened; the MD Runtime keeps a part's first
106
+ base. A write or a discard from an older revision is refused as `stale` with
107
+ the current Room Draft; read it and write again.
108
+
109
+ Save takes one Room and the Room Drafts of it to save, each with its revision,
110
+ validates locally, and on success appends one Change Batch to that Room's
111
+ queue and removes those Room Drafts. It is refused as `stale`, as
112
+ `changed-underneath` naming each part whose base no longer matches the visible
113
+ state, or by a local validation rule, and then creates nothing and leaves every
114
+ Room Draft in place. A directory move is a move part on each Document beneath
115
+ it, saved together.
29
116
 
30
117
  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.
118
+ layer. Conflicted and Rejected, an expired outcome record, and a change that no
119
+ longer applies over the Room each leave the queue and return as Room Drafts on
120
+ the Documents they changed, marked with why: the Provider's structured refusal
121
+ code and fields as returned, not known to have applied, or withdrawn locally.
122
+ The rest of the queue keeps synchronizing, and Sync Status requires attention
123
+ until each returned Room Draft is saved again or discarded. `outcome` answers
124
+ the saved Change Batch's outcome, and `null` while it is Pending. Transport
125
+ failure and Provider unavailability keep work Pending and do not infer a
126
+ rejection. A refusal the Provider marks retryable stays Pending until the reset
127
+ it names; the MD Runtime retries the same Change Batch identity and payload and
128
+ never infers an outcome from transport uncertainty. The MD Runtime never
129
+ combines, reorders, or rebases.
40
130
 
41
131
  ## The neutrality rule
42
132
 
@@ -61,8 +151,8 @@ a desktop. Its strings are English only, dates and numbers follow the
61
151
  browser locale, and content is never translated. Light and dark themes come
62
152
  from CSS custom properties with the browser's preference as the default and
63
153
  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.
154
+ of these promises by itself. Reading, querying and searching never wait for
155
+ the network. Data lives only in the App Creator's browser and with the Provider.
66
156
 
67
157
  ## App code imports only the client entry and never constructs a Runtime
68
158
 
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