@agentshouse/app-bridge 0.1.0-ce42efd6bfc0c → 0.1.0-cedebf3bb1f58

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/dist/index.d.ts CHANGED
@@ -1,7 +1,6 @@
1
- import type { MdRuntimeClient } from './mdruntime/client.js';
1
+ import type { MdRuntimeClient, Open } from './mdruntime/client.js';
2
2
  import type { HouseCalls } from './calls.js';
3
3
  export type { HouseCalls } from './calls.js';
4
- type Open<Value extends string> = Value | (string & {});
5
4
  export type HouseCallInput<Name extends string> = Name extends keyof HouseCalls ? HouseCalls[Name] : unknown;
6
5
  export interface HouseRuntime extends Omit<MdRuntimeClient, 'call'> {
7
6
  call<Name extends Open<keyof HouseCalls>>(name: Name, input: HouseCallInput<Name>): ReturnType<MdRuntimeClient['call']>;
@@ -110,6 +109,20 @@ export interface HouseBrowserRefused extends Error {
110
109
  readonly code: string;
111
110
  readonly retryable: boolean;
112
111
  }
112
+ export interface HouseTrialWithheld {
113
+ readonly code: Open<'house_trial_withheld'>;
114
+ readonly retryable: true;
115
+ }
116
+ export type HouseTask = unknown;
117
+ export type HouseActivation = Open<'committed' | 'abandoned'>;
118
+ export interface HouseCooperation {
119
+ checkpoint(): Promise<void> | void;
120
+ persist(): Promise<void> | void;
121
+ capture(): Promise<HouseTask> | HouseTask;
122
+ restore(task: HouseTask | null): Promise<void> | void;
123
+ ready(): Promise<boolean> | boolean;
124
+ settle(activation: HouseActivation): void;
125
+ }
113
126
  export interface HouseBridge {
114
127
  readonly runtime: Promise<HouseRuntime>;
115
128
  address(): string | null;
@@ -130,6 +143,7 @@ export interface HouseBridge {
130
143
  onStatus(listener: (status: HouseStatus) => void): () => void;
131
144
  messages(listener: (message: HouseAppMessage) => void): () => void;
132
145
  analyticsConsent(choice: HouseAnalyticsChoice): void;
146
+ cooperate(cooperation: HouseCooperation): () => void;
133
147
  }
134
148
  export interface HouseHeld {
135
149
  readonly houseBridge: HouseBridge;
@@ -14,5 +14,5 @@ export type { Definition, JsonType, PatternFacts, PointerFacts, SectionSpec, Sha
14
14
  export type { ApplyResult, BodyResult, Change, Division, DivisionContext, Document, DocumentCheck, Frontmatter, FrontmatterResult, Library, ModelVerification, Link, LinkOccurrence, ParseResult, Projection, ProjectionContext, Reached, Reference, Refusal, Span, Validation, ValidationContext, VerifiedModel, } from './types.js';
15
15
  export { checkDocument, resolves, sectionReferences, validate, verify } from './validate.js';
16
16
  export { VERSION } from './version.js';
17
- export { equalValue, frontmatterValue } from './yaml.js';
17
+ export { bodyAfterFrontmatter, equalValue, frontmatterValue } from './yaml.js';
18
18
  export type { FrontmatterValue } from './yaml.js';
@@ -238,17 +238,15 @@ export type ContractUnavailable = ProviderRefusal & {
238
238
  export type DiscoverResult = Discovery | TransportUncertainty | RevokedAuthorization | ContractUnavailable;
239
239
  export type PageToken = string;
240
240
  export type ChangeAuthor = {
241
- kind: 'user';
241
+ kind: Open<'user' | 'site'>;
242
242
  id: string;
243
243
  name: string;
244
- } | {
245
- kind: 'site';
244
+ };
245
+ export type Member = {
246
+ kind: Open<'user'>;
246
247
  id: string;
247
248
  name: string;
248
249
  };
249
- export type Member = Extract<ChangeAuthor, {
250
- kind: 'user';
251
- }>;
252
250
  export type HeadChange = {
253
251
  author: ChangeAuthor | null;
254
252
  time: string;
@@ -279,6 +277,7 @@ export type BootstrapPage = {
279
277
  models?: RoomModels;
280
278
  documents: Readonly<Record<string, string>>;
281
279
  revisions: Readonly<Record<string, string>>;
280
+ fileIds?: Readonly<Record<string, string>>;
282
281
  changes: Readonly<Record<string, HeadChange>>;
283
282
  binaryFiles: Readonly<Record<string, BinaryFile>>;
284
283
  pageToken?: PageToken;
@@ -298,6 +297,7 @@ export type RefreshPage = {
298
297
  models?: RoomModels;
299
298
  documents: Readonly<Record<string, string>>;
300
299
  revisions: Readonly<Record<string, string>>;
300
+ fileIds?: Readonly<Record<string, string>>;
301
301
  changes: Readonly<Record<string, HeadChange>>;
302
302
  binaryFiles: Readonly<Record<string, BinaryFile>>;
303
303
  removals: readonly string[];
@@ -326,6 +326,7 @@ export type MutationResult = {
326
326
  roomVersion: string;
327
327
  documents: Readonly<Record<string, string>>;
328
328
  revisions: Readonly<Record<string, string>>;
329
+ fileIds?: Readonly<Record<string, string>>;
329
330
  changes: Readonly<Record<string, HeadChange>>;
330
331
  removals: readonly string[];
331
332
  binaryFiles?: Readonly<Record<string, BinaryFile>>;
@@ -425,6 +426,7 @@ export type RoomStreamEvent = {
425
426
  } | {
426
427
  kind: 'closed';
427
428
  revoked: boolean;
429
+ lost: boolean;
428
430
  } | {
429
431
  kind: 'opening';
430
432
  expired: readonly string[];
@@ -78,4 +78,5 @@ export type MdRuntimeClient = {
78
78
  export declare function connectMdRuntime(port: RuntimePort): MdRuntimeClient;
79
79
  export declare function inTurn(client: MdRuntimeClient, work: () => Promise<void>): Promise<void>;
80
80
  export type { Observation } from './observation.js';
81
- export type { RuntimePort } from './wire.js';
81
+ export type { Open } from './open.js';
82
+ export { unanswered, type RuntimePort, type WireMember } from './wire.js';
@@ -1,6 +1,7 @@
1
1
  import { type TypesResult } from './model.js';
2
2
  import { type Observation } from './observation.js';
3
3
  import { type DocumentQuery, type DocumentResult, type GroupQuery, type GroupResult, type PendingResult } from './query.js';
4
+ import { type EntrySeed } from './seed.js';
4
5
  import { type SearchRequest, type SearchResult } from './search.js';
5
6
  import { type Adapter, type Authorization, type Member, type ProviderRefusal, type ServiceMode } from './adapter.js';
6
7
  import { type ChatDraftSurface, type ConversationAgent, type ConversationEntry, type ConversationOptions, type ConversationSurface, type ConversationView } from './conversation.js';
@@ -14,7 +15,7 @@ import { type RoomDraft, type RoomDraftWrite } from './room-draft.js';
14
15
  import { type SyncStatus } from './status.js';
15
16
  import { type RuntimeShell } from './durability.js';
16
17
  import { type RuntimeDemand } from './acquisition.js';
17
- import { type CapacityCause, type CodeAvailability, type Persistence, type Readiness } from './readiness.js';
18
+ import { type CapacityCause, type CodeAvailability, type Persistence, type Readiness, type SearchObligation } from './readiness.js';
18
19
  import { type NarrowedView, type PortReach, type RuntimeView } from './reach.js';
19
20
  export type AppTypedSelection = {
20
21
  roots: readonly string[];
@@ -126,8 +127,10 @@ export type ReadinessPort = {
126
127
  readonly target: string;
127
128
  readonly code: CodeAvailability;
128
129
  readonly capacity: CapacityCause | null;
130
+ readonly search: SearchObligation;
129
131
  };
130
132
  export type ComposedRuntime = RuntimeView & {
133
+ seed(entry: EntrySeed): void;
131
134
  stop(): Promise<void>;
132
135
  lose(locators: readonly string[]): Promise<void>;
133
136
  acquire(demand: RuntimeDemand): void;
@@ -89,6 +89,7 @@ export type ConversationStore = {
89
89
  known(): boolean;
90
90
  reconnected(): void;
91
91
  reopened(): void;
92
+ lost(): void;
92
93
  watch(conversation: string, options: ConversationOptions, deliver: (view: ConversationView) => void): () => void;
93
94
  watchList(deliver: (entries: readonly ConversationEntry[]) => void): () => void;
94
95
  watchAgents(deliver: (agents: readonly ConversationAgent[]) => void): () => void;
@@ -100,4 +101,5 @@ export type ConversationStore = {
100
101
  };
101
102
  export declare function unheldView(conversation: string): ConversationView;
102
103
  export declare const SESSION_STARTED = "Session started";
104
+ export declare function newChat(agent: string): string;
103
105
  export declare function createConversationStore(deps: ConversationDependencies): ConversationStore;
@@ -29,12 +29,13 @@ export type ModelState = {
29
29
  types: VerifiedModel['types'];
30
30
  };
31
31
  export type TypesResult = {
32
- status: 'unavailable';
32
+ status: Open<'unavailable'>;
33
33
  reason: ModelUnavailableReason;
34
+ types: null;
34
35
  schema?: string | null;
35
36
  revision?: string | null;
36
37
  } | {
37
- status: 'current' | 'cached';
38
+ status: Open<'current' | 'cached'>;
38
39
  identity: string;
39
40
  version: string;
40
41
  verifiedWith: ModelIdentity;
@@ -2,6 +2,7 @@ import type { BinaryFile, HeadChange } from './adapter.js';
2
2
  import type { ChangeBatch } from './change.js';
3
3
  export type Heads = {
4
4
  revisions: Readonly<Record<string, string>>;
5
+ fileIds: Readonly<Record<string, string>>;
5
6
  changes: Readonly<Record<string, HeadChange>>;
6
7
  };
7
8
  export type HeldBinary = ({
@@ -1,6 +1,7 @@
1
1
  import { type Definition } from '../mdmodel/index.js';
2
2
  import type { HeadChange } from './adapter.js';
3
3
  import type { BinaryFileRecord } from './binary.js';
4
+ import type { Open } from './open.js';
4
5
  export type Value = string | number | boolean | null;
5
6
  export type Predicate = Value | {
6
7
  $eq: Value;
@@ -54,6 +55,7 @@ export type Select = 'all' | {
54
55
  markdown?: true;
55
56
  backlinks?: true;
56
57
  change?: true;
58
+ fileId?: true;
57
59
  };
58
60
  export type OrderOptions = {
59
61
  nulls?: 'first' | 'last';
@@ -94,6 +96,7 @@ export type QueryDocument = {
94
96
  markdown?: string;
95
97
  backlinks?: readonly Backlink[];
96
98
  change?: HeadChange | null;
99
+ fileId?: string | null;
97
100
  binaryFile?: BinaryFileRecord;
98
101
  };
99
102
  export type QueryGroup = {
@@ -110,7 +113,7 @@ export type QueryCoverage = {
110
113
  indexes: readonly string[];
111
114
  overlay: readonly string[];
112
115
  };
113
- export type Awaits = 'acquisition' | 'preparation' | 'index';
116
+ export type Awaits = Open<'acquisition' | 'preparation' | 'index'>;
114
117
  export type PendingRoom = {
115
118
  room: string;
116
119
  awaits: Awaits;
@@ -198,6 +201,7 @@ export type DocumentShape = {
198
201
  markdown: boolean;
199
202
  backlinks: boolean;
200
203
  change: boolean;
204
+ fileId: boolean;
201
205
  orderBy: readonly {
202
206
  key: KeyPlan;
203
207
  options: OrderOptions & {
@@ -218,6 +222,7 @@ export type PlanReach = {
218
222
  documents: readonly string[];
219
223
  };
220
224
  export type Plan = {
225
+ key: string;
221
226
  rooms: readonly string[] | null;
222
227
  reach: PlanReach | null;
223
228
  root: Scope;
@@ -239,8 +244,10 @@ type Entries = Readonly<Record<string, unknown>>;
239
244
  export declare function invalid(at: string, problem: string): never;
240
245
  export declare function plain(value: unknown): value is Entries;
241
246
  export declare function known(value: Entries, allowed: readonly string[], at: string): void;
247
+ export declare function flag(input: unknown, at: string): boolean;
242
248
  export declare function planOf(input: unknown, facts: HeldRooms, reach?: PlanReach | null): Plan;
243
249
  export declare function limitOf(input: unknown): number | null;
244
250
  export declare function filterOf(input: unknown, facts: HeldRooms, reach?: PlanReach | null): Plan;
251
+ export declare function queryKey(query: unknown): string;
245
252
  export declare function identityOf(plan: Plan): string;
246
253
  export {};
@@ -2,7 +2,7 @@ import type { ReferrerScope } from './adapter.js';
2
2
  import type { MdRuntime, ReadinessPort, RuntimeTopic } from './connection.js';
3
3
  import type { Plan, PlanReach, QueryResult } from './query.js';
4
4
  import type { Readiness } from './readiness.js';
5
- import type { SearchResult } from './search.js';
5
+ import { type SearchResult } from './search.js';
6
6
  export type ReachedDocument = {
7
7
  readonly room: string;
8
8
  readonly path: string;
@@ -27,7 +27,6 @@ export type Reachable = RuntimeView & {
27
27
  query(plan: Plan): Promise<QueryResult>;
28
28
  watchPlan(plan: Plan, deliver: (message: unknown) => void): () => void;
29
29
  search(input: unknown, reach: PortReach | null): Promise<SearchResult>;
30
- searching(target: string): void;
31
30
  watchReadiness(port: ReadinessPort, reach: PortReach | null, deliver: (readiness: Readiness) => void): () => void;
32
31
  referrerScope(referrer: unknown): ReferrerScope | null;
33
32
  };
@@ -87,7 +87,7 @@ type ChangedUnderneath = {
87
87
  readonly file?: string | null;
88
88
  };
89
89
  export type ReturnReason = ProviderRefusal & {
90
- readonly kind: 'conflicted' | 'rejected' | 'expired' | 'withdrawn';
90
+ readonly kind: Open<'conflicted' | 'rejected' | 'expired' | 'withdrawn'>;
91
91
  };
92
92
  export declare function returnedBy(kind: 'expired' | 'withdrawn'): ReturnReason;
93
93
  export type RoomDraft = {
@@ -130,6 +130,18 @@ export declare function underneath(parts: HeldParts, path: string, visible: Visi
130
130
  export declare function heldFile(blob: Blob, base: string | null): Promise<HeldFile>;
131
131
  export declare function binaryBase(visible: VisibleLayer | undefined, path: string): string | null;
132
132
  export declare function changedParts(path: string, report: ChangedUnderneath): readonly ChangedPart[];
133
+ export type TextParts = {
134
+ readonly fields?: Readonly<Record<string, {
135
+ readonly value: unknown;
136
+ }>>;
137
+ readonly sections?: Readonly<Record<string, {
138
+ readonly value: string;
139
+ }>>;
140
+ readonly preamble?: {
141
+ readonly value: string;
142
+ };
143
+ };
144
+ export declare function partsAfter(markdown: string, parts: TextParts): string;
133
145
  export declare function returnedInto(existing: HeldParts | undefined, returned: HeldParts): HeldParts;
134
146
  export declare function moveTarget(move: {
135
147
  readonly to: string;
@@ -15,6 +15,7 @@ export type DocumentRow = {
15
15
  directories: readonly string[];
16
16
  text: string | null;
17
17
  change: HeadChange | null;
18
+ fileId: string | null;
18
19
  binary: BinaryFileRecord | null;
19
20
  };
20
21
  export type ReferenceRow = {
@@ -32,5 +33,5 @@ export type Row = DocumentRow | ReferenceRow;
32
33
  export declare function documentKey(room: string, path: string): string;
33
34
  export declare function roomOfKey(key: string): string;
34
35
  export declare function markdownPath(path: string): boolean;
35
- export declare function rowsOf(room: string, path: string, document: Frontmatter | null, composition: Composition, change: HeadChange | null, binary: BinaryFileRecord | null): readonly Row[];
36
+ export declare function rowsOf(room: string, path: string, document: Frontmatter | null, composition: Composition, change: HeadChange | null, fileId: string | null, binary: BinaryFileRecord | null): readonly Row[];
36
37
  export {};
@@ -1,3 +1,4 @@
1
+ import type { ProviderRefusal } from './adapter.js';
1
2
  import type { ChangeSet } from './change-set.js';
2
3
  import { type CoveredRoom, type Filter, type PendingRoom } from './query.js';
3
4
  import type { Open } from './open.js';
@@ -5,6 +6,7 @@ export type DocumentPart = Open<'fields' | 'sections' | 'preamble' | 'path'>;
5
6
  export type SearchRequest = {
6
7
  text: string;
7
8
  documents?: Filter;
9
+ collection?: true;
8
10
  conversations?: readonly string[];
9
11
  limit?: number;
10
12
  };
@@ -29,18 +31,23 @@ export type SearchCoverage = {
29
31
  };
30
32
  export type PendingConversation = {
31
33
  conversation: string;
32
- awaits: 'index';
33
- };
34
- export type SearchPending = {
35
- pending: readonly (PendingRoom | PendingConversation)[];
34
+ awaits: Open<'index'>;
36
35
  };
37
36
  export type SearchHits = {
38
37
  documents: readonly DocumentHit[];
39
38
  messages: readonly MessageHit[];
40
39
  };
40
+ export type SearchPending = SearchHits & {
41
+ pending: readonly (PendingRoom | PendingConversation)[];
42
+ coverage: SearchCoverage;
43
+ };
44
+ export type SearchUndeclared = ProviderRefusal & {
45
+ kind: 'undeclared';
46
+ };
41
47
  export type SearchResult = (SearchHits & {
42
48
  coverage: SearchCoverage;
43
- }) | SearchPending;
49
+ }) | SearchPending | SearchUndeclared;
50
+ export declare function undeclaredSearch(): SearchUndeclared;
44
51
  export type SearchedDocument = {
45
52
  room: string;
46
53
  path: string;
@@ -61,6 +68,7 @@ export type SearchSource = {
61
68
  export type SearchPlan = {
62
69
  text: string;
63
70
  documents: unknown;
71
+ collection: boolean;
64
72
  conversations: readonly string[] | null;
65
73
  limit: number | null;
66
74
  };
@@ -77,7 +85,7 @@ export type SearchIndex = {
77
85
  search(plan: SearchPlan, documents: readonly {
78
86
  room: string;
79
87
  path: string;
80
- }[] | null, conversations: readonly string[]): SearchHits;
88
+ }[] | null, conversations: readonly string[], rooms: ReadonlySet<string> | null, selections: ReadonlyMap<string, readonly string[]> | null): SearchHits;
81
89
  };
82
90
  export declare function searchPlanOf(input: unknown): SearchPlan;
83
91
  export declare function createSearchIndex(source: SearchSource): SearchIndex;
@@ -0,0 +1,15 @@
1
+ import type { KeyedResult } from './observation.js';
2
+ import type { QueryRows } from './query.js';
3
+ export type EntrySeed = {
4
+ readonly at: Readonly<Record<string, string>>;
5
+ readonly queries: readonly {
6
+ readonly query: unknown;
7
+ readonly rows: QueryRows;
8
+ }[];
9
+ };
10
+ export type Seeded = {
11
+ readonly rows: QueryRows;
12
+ readonly at: Readonly<Record<string, string>>;
13
+ };
14
+ export declare function entrySeedOf(value: unknown): EntrySeed | null;
15
+ export declare function seededResult(seed: Seeded): KeyedResult;
@@ -23,10 +23,11 @@ export type ReplicaRecord = {
23
23
  documents: Readonly<Record<string, string>>;
24
24
  binaries: Readonly<Record<string, HeldBinary>>;
25
25
  revisions: Readonly<Record<string, string>>;
26
+ fileIds: Readonly<Record<string, string>>;
26
27
  changes: Readonly<Record<string, HeadChange>>;
27
28
  written: Readonly<Record<string, string>>;
28
29
  };
29
- export type StagedLayer = Pick<ReplicaRecord, 'documents' | 'binaries' | 'revisions' | 'changes' | 'written'>;
30
+ export type StagedLayer = Pick<ReplicaRecord, 'documents' | 'binaries' | 'revisions' | 'fileIds' | 'changes' | 'written'>;
30
31
  export type TransferRecord = {
31
32
  locator: string;
32
33
  transfer: string;
@@ -48,7 +49,6 @@ export type PendingState = {
48
49
  };
49
50
  export type TargetRecord = {
50
51
  established: boolean;
51
- search: boolean;
52
52
  };
53
53
  export type QueueRecord = {
54
54
  locator: string;
@@ -86,6 +86,7 @@ export type ChatDraftRecord = {
86
86
  files: readonly ChatDraftFile[];
87
87
  revision: string;
88
88
  submitted?: readonly ChatSubmission[];
89
+ moves?: readonly string[];
89
90
  };
90
91
  export type RecordWrite = {
91
92
  next: ReplicaRecord;
@@ -128,7 +129,7 @@ export type RuntimeStore = {
128
129
  turnArrived(conversation: string, turn: string): Promise<boolean>;
129
130
  dropConversations(conversations: readonly string[]): Promise<void>;
130
131
  chatDrafts(): Promise<readonly ChatDraftRecord[]>;
131
- keepChatDraft(record: ChatDraftRecord): Promise<void>;
132
+ keepChatDrafts(records: readonly ChatDraftRecord[]): Promise<void>;
132
133
  dropChatDraft(conversation: string): Promise<void>;
133
134
  keepRoomDraft(record: RoomDraftRecord): Promise<void>;
134
135
  discardRoomDraft(locator: string, path: string): Promise<void>;
@@ -155,4 +156,5 @@ export declare function authoredBy(holder: string): Promise<boolean>;
155
156
  export declare function authoredBinding(): Promise<string | null>;
156
157
  export declare function bindAuthored(binding: string): Promise<void>;
157
158
  export declare function removeRuntimeStore(holder?: string): Promise<boolean>;
159
+ export declare function openDatabase(name: string, version: number, upgrade: (opening: IDBOpenDBRequest, oldVersion: number) => void): Promise<IDBDatabase>;
158
160
  export declare function openRuntimeStore(fenced: (locator: string) => boolean): Promise<RuntimeStore>;
@@ -8,38 +8,136 @@ export type RuntimePort = {
8
8
  start(): void;
9
9
  };
10
10
  export type WireReach = 'within' | 'app' | 'runtime' | 'actions' | 'whole';
11
+ export type WireEffect = 'read' | 'write' | 'action';
12
+ export type WireCall = {
13
+ readonly reach: WireReach;
14
+ readonly effect: WireEffect;
15
+ };
11
16
  declare const MEMBERS: {
12
- readonly rooms: 'within';
13
- readonly query: 'within';
14
- readonly search: 'within';
15
- readonly types: 'within';
16
- readonly save: 'within';
17
- readonly history: 'within';
18
- readonly 'binaryFile.read': 'within';
19
- readonly 'binaryFile.readReference': 'within';
20
- readonly call: 'actions';
21
- readonly reached: 'within';
22
- readonly log: 'whole';
23
- readonly persistence: 'runtime';
24
- readonly retry: 'runtime';
25
- readonly reauthorize: 'runtime';
26
- readonly 'roomDraft.list': 'within';
27
- readonly 'roomDraft.read': 'within';
28
- readonly 'roomDraft.write': 'within';
29
- readonly 'roomDraft.discard': 'within';
30
- readonly 'presentation.read': 'app';
31
- readonly 'presentation.write': 'app';
32
- readonly 'conversation.list': 'within';
33
- readonly 'conversation.read': 'within';
34
- readonly 'conversation.create': 'within';
35
- readonly 'conversation.send': 'within';
36
- readonly 'conversation.answer': 'within';
37
- readonly 'conversation.interrupt': 'within';
38
- readonly 'conversation.kill': 'within';
39
- readonly 'conversation.cancel': 'within';
40
- readonly 'conversation.delete': 'within';
41
- readonly 'chatDraft.write': 'within';
42
- readonly 'chatDraft.discard': 'within';
17
+ readonly rooms: {
18
+ readonly reach: 'within';
19
+ readonly effect: 'read';
20
+ };
21
+ readonly query: {
22
+ readonly reach: 'within';
23
+ readonly effect: 'read';
24
+ };
25
+ readonly search: {
26
+ readonly reach: 'within';
27
+ readonly effect: 'read';
28
+ };
29
+ readonly types: {
30
+ readonly reach: 'within';
31
+ readonly effect: 'read';
32
+ };
33
+ readonly save: {
34
+ readonly reach: 'within';
35
+ readonly effect: 'write';
36
+ };
37
+ readonly history: {
38
+ readonly reach: 'within';
39
+ readonly effect: 'read';
40
+ };
41
+ readonly 'binaryFile.read': {
42
+ readonly reach: 'within';
43
+ readonly effect: 'read';
44
+ };
45
+ readonly 'binaryFile.readReference': {
46
+ readonly reach: 'within';
47
+ readonly effect: 'read';
48
+ };
49
+ readonly call: {
50
+ readonly reach: 'actions';
51
+ readonly effect: 'action';
52
+ };
53
+ readonly reached: {
54
+ readonly reach: 'within';
55
+ readonly effect: 'read';
56
+ };
57
+ readonly log: {
58
+ readonly reach: 'whole';
59
+ readonly effect: 'read';
60
+ };
61
+ readonly persistence: {
62
+ readonly reach: 'runtime';
63
+ readonly effect: 'read';
64
+ };
65
+ readonly retry: {
66
+ readonly reach: 'runtime';
67
+ readonly effect: 'write';
68
+ };
69
+ readonly reauthorize: {
70
+ readonly reach: 'runtime';
71
+ readonly effect: 'write';
72
+ };
73
+ readonly 'roomDraft.list': {
74
+ readonly reach: 'within';
75
+ readonly effect: 'read';
76
+ };
77
+ readonly 'roomDraft.read': {
78
+ readonly reach: 'within';
79
+ readonly effect: 'read';
80
+ };
81
+ readonly 'roomDraft.write': {
82
+ readonly reach: 'within';
83
+ readonly effect: 'write';
84
+ };
85
+ readonly 'roomDraft.discard': {
86
+ readonly reach: 'within';
87
+ readonly effect: 'write';
88
+ };
89
+ readonly 'presentation.read': {
90
+ readonly reach: 'app';
91
+ readonly effect: 'read';
92
+ };
93
+ readonly 'presentation.write': {
94
+ readonly reach: 'app';
95
+ readonly effect: 'write';
96
+ };
97
+ readonly 'conversation.list': {
98
+ readonly reach: 'within';
99
+ readonly effect: 'read';
100
+ };
101
+ readonly 'conversation.read': {
102
+ readonly reach: 'within';
103
+ readonly effect: 'read';
104
+ };
105
+ readonly 'conversation.create': {
106
+ readonly reach: 'within';
107
+ readonly effect: 'action';
108
+ };
109
+ readonly 'conversation.send': {
110
+ readonly reach: 'within';
111
+ readonly effect: 'action';
112
+ };
113
+ readonly 'conversation.answer': {
114
+ readonly reach: 'within';
115
+ readonly effect: 'action';
116
+ };
117
+ readonly 'conversation.interrupt': {
118
+ readonly reach: 'within';
119
+ readonly effect: 'action';
120
+ };
121
+ readonly 'conversation.kill': {
122
+ readonly reach: 'within';
123
+ readonly effect: 'action';
124
+ };
125
+ readonly 'conversation.cancel': {
126
+ readonly reach: 'within';
127
+ readonly effect: 'action';
128
+ };
129
+ readonly 'conversation.delete': {
130
+ readonly reach: 'within';
131
+ readonly effect: 'action';
132
+ };
133
+ readonly 'chatDraft.write': {
134
+ readonly reach: 'within';
135
+ readonly effect: 'write';
136
+ };
137
+ readonly 'chatDraft.discard': {
138
+ readonly reach: 'within';
139
+ readonly effect: 'write';
140
+ };
43
141
  };
44
142
  declare const TOPICS: {
45
143
  readonly rooms: 'within';
@@ -58,8 +156,8 @@ declare const TOPICS: {
58
156
  readonly conversations: 'within';
59
157
  readonly agents: 'within';
60
158
  };
61
- export declare const WIRE_REACH: {
62
- readonly members: Readonly<Record<string, WireReach>>;
159
+ export declare const WIRE: {
160
+ readonly members: Readonly<Record<string, WireCall>>;
63
161
  readonly topics: Readonly<Record<string, WireReach>>;
64
162
  };
65
163
  export type WireMember = keyof typeof MEMBERS;
@@ -97,6 +195,10 @@ export type WireAnswer = {
97
195
  } | {
98
196
  kind: 'ended';
99
197
  };
198
+ export declare function unanswered(call: {
199
+ readonly id: number;
200
+ readonly member: WireMember;
201
+ }): WireAnswer | null;
100
202
  export declare const AUTHORIZATION_ENDED = "the host authorization ended";
101
203
  export declare const PORT_CLOSED = "the port closed";
102
204
  export declare function request(message: unknown): WireRequest | null;
@@ -1,5 +1,5 @@
1
1
  import type { MdRuntimeClient } from './mdruntime/client.js';
2
- import type { HouseAnalyticsChoice, HouseAppMessage, HouseExternalAnswer, HouseExternalRequest, HouseNavigationMode, HouseRefusal, HouseSourceDownload, HouseSourceTop, HouseStatus, HouseTab } from './index.js';
2
+ import type { HouseActivation, HouseAnalyticsChoice, HouseAppMessage, HouseExternalAnswer, HouseExternalRequest, HouseNavigationMode, HouseRefusal, HouseSourceDownload, HouseSourceTop, HouseStatus, HouseTab, HouseTask } from './index.js';
3
3
  export interface HouseBridgeNavigation {
4
4
  readonly mode: HouseNavigationMode;
5
5
  readonly address: string;
@@ -15,6 +15,9 @@ export interface HouseBridgeTestHost {
15
15
  publish(status: HouseStatus): void;
16
16
  message(message: HouseAppMessage): void;
17
17
  attachRuntime(runtime: MdRuntimeClient): void;
18
+ checkpoint(): Promise<HouseTask>;
19
+ restore(task: HouseTask | null): Promise<boolean>;
20
+ settle(activation: HouseActivation): void;
18
21
  }
19
22
  export interface HouseBridgeTestHostOptions {
20
23
  readonly address?: string;
package/dist/test-host.js CHANGED
@@ -57,6 +57,7 @@ export function createHouseBridgeTestHost(options = {}) {
57
57
  let published = options.status ?? null;
58
58
  let shown = null;
59
59
  let seen = null;
60
+ let cooperation = null;
60
61
  let attached = () => { };
61
62
  const runtime = new Promise((resolve) => {
62
63
  attached = resolve;
@@ -144,7 +145,19 @@ export function createHouseBridgeTestHost(options = {}) {
144
145
  consents.push(choice);
145
146
  publish({ ...published, analytics: { measures: true, choice } });
146
147
  },
148
+ cooperate(given) {
149
+ cooperation = given;
150
+ return () => {
151
+ if (cooperation === given)
152
+ cooperation = null;
153
+ };
154
+ },
147
155
  };
156
+ function cooperating() {
157
+ if (cooperation === null)
158
+ throw new Error('the App registered no cooperation');
159
+ return cooperation;
160
+ }
148
161
  Object.defineProperty(globalThis, 'houseBridge', { value: bridge, configurable: true });
149
162
  if (published !== null)
150
163
  publish(published);
@@ -169,5 +182,19 @@ export function createHouseBridgeTestHost(options = {}) {
169
182
  attachRuntime(client) {
170
183
  attached(client);
171
184
  },
185
+ async checkpoint() {
186
+ const held = cooperating();
187
+ await held.checkpoint();
188
+ await held.persist();
189
+ return JSON.parse(JSON.stringify((await held.capture()) ?? null));
190
+ },
191
+ async restore(task) {
192
+ const held = cooperating();
193
+ await held.restore(task);
194
+ return (await held.ready()) === true;
195
+ },
196
+ settle(activation) {
197
+ cooperating().settle(activation);
198
+ },
172
199
  };
173
200
  }
package/docs/families.md CHANGED
@@ -45,6 +45,15 @@
45
45
  The App reports the visitor's choice with `analyticsConsent('accept')` or
46
46
  `analyticsConsent('decline')`; the shell hands it to House's analytics loader, and
47
47
  the choice never reaches House.
48
+ - **Handoff** - when House hands its runtime over to a newer build, the App keeps
49
+ running, its `runtime` client and observations included: a call it makes
50
+ meanwhile waits for the newer runtime, however long that takes, and is
51
+ answered once, by it; each observation goes on with the newer runtime's
52
+ values; and the status and the address arrive again. A runtime that ended
53
+ before answering has its reads answered by the next one; since it may have
54
+ applied any other call, an act on House is answered the outcome
55
+ `transport-uncertainty`, and every other call, a Save or a draft write among
56
+ them, fails with the retryable code `transport-uncertainty`.
48
57
  - **Site image read** - given a Published Room and an original attachment
49
58
  reference, it answers the web copy's path and metadata, or `null` when no
50
59
  copy exists. The original bytes never cross this bridge at a Site.
@@ -84,6 +93,31 @@
84
93
  after its latest release, as a JPEG `Blob`, or `null` when House holds none
85
94
  or does not answer. A Site's own renderer reads none.
86
95
 
96
+ - **Cooperation** - `cooperate({ checkpoint, persist, capture, restore,
97
+ ready, settle })` lets House activate a newer release of the App while it
98
+ runs, keeping the User's task; the function it returns withdraws it.
99
+ Register it in the entry script, before the first draw, or House treats the
100
+ page as not cooperating and the newer release waits for the App's next
101
+ open. When a newer release is stored, House calls `checkpoint()`, which
102
+ resolves at the App's next safe checkpoint and may never resolve, then
103
+ `persist()`, which makes authored work durable, then `capture()`, which
104
+ answers the meaningful task the App owns, such as the selected record,
105
+ filters, cursor, selection and scroll position, as JSON of at most 65,536
106
+ characters; House keeps the address itself. In the newer release House
107
+ calls `restore(task)` once its document has drawn, so the scroll position
108
+ and focus it sets hold, or `restore(null)` when it opens the release without
109
+ a task, then `ready()`, the App's own check that its local data and
110
+ capabilities work, and finally `settle('committed')` once the release is
111
+ active or `settle('abandoned')` when House returns to the release before
112
+ it, restoring the same task there. A release House opens already active is
113
+ settled `'committed'` at once, after `restore(task)` when House hands it a
114
+ task, and a cooperation registered after House settled is settled at its
115
+ registration. Until `settle('committed')` House fails the release's outside
116
+ calls at once, as offline, those of the workers it starts included, and
117
+ refuses its Saves, write acts and external calls with the retryable code
118
+ `house_trial_withheld`, so a start-up effect is not repeated; run start-up
119
+ effects after `settle('committed')`.
120
+
87
121
  ## The test host
88
122
 
89
123
  `createHouseBridgeTestHost(options)` plays House in one page: it installs a
@@ -106,4 +140,7 @@ given by Site identifier, answers every `install()` `'manual'`, as a browser
106
140
  with no install prompt does, answers every `notifications()` `'unavailable'`,
107
141
  as a browser without push does, settles every `setBadge`, delivers each App
108
142
  message `message({ from, body })` plays, and hands `bridge.runtime` the MD
109
- Runtime client `attachRuntime(client)` gives it.
143
+ Runtime client `attachRuntime(client)` gives it. `checkpoint()` runs the
144
+ registered cooperation's checkpoint, persist and capture and answers the task,
145
+ `restore(task)` runs its restore and answers its readiness check, and
146
+ `settle(activation)` hands it the outcome.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentshouse/app-bridge",
3
- "version": "0.1.0-ce42efd6bfc0c",
3
+ "version": "0.1.0-cedebf3bb1f58",
4
4
  "description": "Types of the agents.house App bridge House hands every App: navigation, the tab title and icon, ceremonies, installation, notifications and the icon badge, status, Site image reads, App release code, App and Site screenshot reads, and App messages, with an accessor and an in-page test host.",
5
5
  "license": "MIT",
6
6
  "type": "module",