@agentshouse/mdruntime 0.1.0-alpha.8 → 0.1.0-c436a939f2119

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 (121) hide show
  1. package/AGENTS.md +210 -0
  2. package/README.md +44 -0
  3. package/dist/adapter.d.ts +223 -25
  4. package/dist/adapter.d.ts.map +1 -1
  5. package/dist/adapter.js +2 -3
  6. package/dist/attachment.d.ts +3 -0
  7. package/dist/attachment.d.ts.map +1 -0
  8. package/dist/attachment.js +8 -0
  9. package/dist/bootstrap.d.ts +6 -7
  10. package/dist/bootstrap.d.ts.map +1 -1
  11. package/dist/bootstrap.js +32 -28
  12. package/dist/change-set.d.ts +33 -0
  13. package/dist/change-set.d.ts.map +1 -0
  14. package/dist/change-set.js +87 -0
  15. package/dist/change.d.ts +9 -0
  16. package/dist/change.d.ts.map +1 -1
  17. package/dist/change.js +6 -5
  18. package/dist/client.d.ts +75 -0
  19. package/dist/client.d.ts.map +1 -0
  20. package/dist/client.js +171 -0
  21. package/dist/collection.d.ts +24 -15
  22. package/dist/collection.d.ts.map +1 -1
  23. package/dist/collection.js +69 -77
  24. package/dist/connection.d.ts +79 -36
  25. package/dist/connection.d.ts.map +1 -1
  26. package/dist/connection.js +1699 -953
  27. package/dist/conversation.d.ts +79 -0
  28. package/dist/conversation.d.ts.map +1 -0
  29. package/dist/conversation.js +557 -0
  30. package/dist/durability.d.ts +8 -3
  31. package/dist/durability.d.ts.map +1 -1
  32. package/dist/durability.js +7 -61
  33. package/dist/file.d.ts +20 -0
  34. package/dist/file.d.ts.map +1 -0
  35. package/dist/file.js +126 -0
  36. package/dist/host.d.ts +14 -0
  37. package/dist/host.d.ts.map +1 -0
  38. package/dist/host.js +198 -0
  39. package/dist/identity.d.ts +1 -0
  40. package/dist/identity.d.ts.map +1 -1
  41. package/dist/identity.js +10 -9
  42. package/dist/index.d.ts +14 -9
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +1 -2
  45. package/dist/log.js +5 -6
  46. package/dist/memory.d.ts +17 -2
  47. package/dist/memory.d.ts.map +1 -1
  48. package/dist/memory.js +269 -113
  49. package/dist/model.d.ts +10 -25
  50. package/dist/model.d.ts.map +1 -1
  51. package/dist/model.js +18 -21
  52. package/dist/observation.d.ts +25 -0
  53. package/dist/observation.d.ts.map +1 -0
  54. package/dist/observation.js +85 -0
  55. package/dist/outcome.d.ts +22 -18
  56. package/dist/outcome.d.ts.map +1 -1
  57. package/dist/outcome.js +1 -13
  58. package/dist/overlay.d.ts +9 -2
  59. package/dist/overlay.d.ts.map +1 -1
  60. package/dist/overlay.js +187 -43
  61. package/dist/paths.d.ts +0 -1
  62. package/dist/paths.d.ts.map +1 -1
  63. package/dist/paths.js +12 -19
  64. package/dist/query.d.ts +210 -9
  65. package/dist/query.d.ts.map +1 -1
  66. package/dist/query.js +420 -15
  67. package/dist/read-side.d.ts +14 -0
  68. package/dist/read-side.d.ts.map +1 -0
  69. package/dist/read-side.js +371 -0
  70. package/dist/refresh.d.ts +4 -5
  71. package/dist/refresh.d.ts.map +1 -1
  72. package/dist/refresh.js +45 -30
  73. package/dist/replica.d.ts +27 -6
  74. package/dist/replica.d.ts.map +1 -1
  75. package/dist/replica.js +237 -30
  76. package/dist/room-draft.d.ts +124 -0
  77. package/dist/room-draft.d.ts.map +1 -0
  78. package/dist/room-draft.js +327 -0
  79. package/dist/rows.d.ts +41 -0
  80. package/dist/rows.d.ts.map +1 -0
  81. package/dist/rows.js +100 -0
  82. package/dist/save.d.ts +10 -6
  83. package/dist/save.d.ts.map +1 -1
  84. package/dist/save.js +267 -333
  85. package/dist/search.d.ts +61 -0
  86. package/dist/search.d.ts.map +1 -0
  87. package/dist/search.js +125 -0
  88. package/dist/status.d.ts +9 -2
  89. package/dist/status.d.ts.map +1 -1
  90. package/dist/status.js +11 -14
  91. package/dist/store.d.ts +85 -18
  92. package/dist/store.d.ts.map +1 -1
  93. package/dist/store.js +380 -97
  94. package/dist/transfer.d.ts +5 -0
  95. package/dist/transfer.d.ts.map +1 -1
  96. package/dist/transfer.js +1 -5
  97. package/dist/wire.d.ts +64 -0
  98. package/dist/wire.d.ts.map +1 -0
  99. package/dist/wire.js +124 -0
  100. package/package.json +15 -13
  101. package/dist/adapter.js.map +0 -1
  102. package/dist/bootstrap.js.map +0 -1
  103. package/dist/change.js.map +0 -1
  104. package/dist/collection.js.map +0 -1
  105. package/dist/connection.js.map +0 -1
  106. package/dist/durability.js.map +0 -1
  107. package/dist/identity.js.map +0 -1
  108. package/dist/index.js.map +0 -1
  109. package/dist/log.js.map +0 -1
  110. package/dist/memory.js.map +0 -1
  111. package/dist/model.js.map +0 -1
  112. package/dist/outcome.js.map +0 -1
  113. package/dist/overlay.js.map +0 -1
  114. package/dist/paths.js.map +0 -1
  115. package/dist/query.js.map +0 -1
  116. package/dist/refresh.js.map +0 -1
  117. package/dist/replica.js.map +0 -1
  118. package/dist/save.js.map +0 -1
  119. package/dist/status.js.map +0 -1
  120. package/dist/store.js.map +0 -1
  121. package/dist/transfer.js.map +0 -1
@@ -0,0 +1,79 @@
1
+ import type { ConversationAction, ConversationCards, ConversationEvent, ConversationMessage, ConversationStream, PlanStep, ProcessCommand, SessionOption, WorkingNote } from './adapter.js';
2
+ import { type ChangeSet } from './change-set.js';
3
+ import { type ChatDraftFile } from './file.js';
4
+ import type { CallResult } from './outcome.js';
5
+ import type { DocumentResult, QueryDocument } from './query.js';
6
+ import type { SearchedMessage } from './search.js';
7
+ import type { CycleProgress } from './status.js';
8
+ import type { ChatDraftRecord, HeldConversation, RuntimeStore } from './store.js';
9
+ export type ConversationEntry = {
10
+ conversation: string;
11
+ card: Required<QueryDocument>;
12
+ agent: Required<QueryDocument> | null;
13
+ };
14
+ export type GrowingText = {
15
+ turn: string;
16
+ text: string;
17
+ };
18
+ export type ChatDraft = {
19
+ text: string;
20
+ files: readonly ChatDraftFile[];
21
+ };
22
+ export type ConversationOptions = {
23
+ limit?: number;
24
+ };
25
+ export type ConversationView = {
26
+ conversation: string;
27
+ messages: readonly ConversationMessage[];
28
+ growingText: GrowingText | null;
29
+ workingNote: WorkingNote | null;
30
+ plan: readonly PlanStep[] | null;
31
+ questions: readonly unknown[];
32
+ process: {
33
+ running: boolean;
34
+ } | null;
35
+ commands: readonly ProcessCommand[];
36
+ options: readonly SessionOption[];
37
+ chatDraft: ChatDraft | null;
38
+ };
39
+ export type ConversationSurface = {
40
+ list(): Promise<readonly ConversationEntry[]>;
41
+ read(conversation: string, options?: ConversationOptions): Promise<ConversationView>;
42
+ create(input: unknown): Promise<CallResult>;
43
+ send(conversation: string): Promise<CallResult>;
44
+ answer(conversation: string, answer: unknown): Promise<CallResult>;
45
+ interrupt(conversation: string): Promise<CallResult>;
46
+ kill(conversation: string): Promise<CallResult>;
47
+ cancel(conversation: string, turn: string): Promise<CallResult>;
48
+ delete(conversation: string): Promise<CallResult>;
49
+ };
50
+ export type ChatDraftSurface = {
51
+ write(conversation: string, draft: ChatDraft): Promise<void>;
52
+ discard(conversation: string): Promise<void>;
53
+ };
54
+ export type ConversationDependencies = {
55
+ cards(): ConversationCards | undefined;
56
+ documents(locator: string): Promise<DocumentResult>;
57
+ call(name: ConversationAction, input: unknown): Promise<CallResult>;
58
+ durably<T>(work: (local: RuntimeStore) => Promise<T>): Promise<T>;
59
+ stream(): ConversationStream | undefined;
60
+ publish(set: ChangeSet): void;
61
+ closed(): boolean;
62
+ };
63
+ export type ConversationStore = {
64
+ surface: ConversationSurface;
65
+ chatDraft: ChatDraftSurface;
66
+ hydrate(held: readonly HeldConversation[], drafts: readonly ChatDraftRecord[]): void;
67
+ reconcile(): Promise<void>;
68
+ catchUp(progress: CycleProgress): Promise<void>;
69
+ current(): boolean;
70
+ reconnected(): void;
71
+ watch(conversation: string, options: ConversationOptions, deliver: (view: ConversationView) => void): () => void;
72
+ changed(set: ChangeSet): void;
73
+ receive(event: ConversationEvent): void;
74
+ messages(): readonly SearchedMessage[];
75
+ message(conversation: string, identity: string): SearchedMessage | null;
76
+ close(): void;
77
+ };
78
+ export declare function createConversationStore(deps: ConversationDependencies): ConversationStore;
79
+ //# sourceMappingURL=conversation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../src/conversation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EAEjB,mBAAmB,EACnB,kBAAkB,EAClB,QAAQ,EACR,cAAc,EACd,aAAa,EACb,WAAW,EACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EAAyB,KAAK,SAAS,EAAsB,MAAM,iBAAiB,CAAC;AAC5F,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAElF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC;IAC9B,KAAK,EAAE,QAAQ,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzD,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAErD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACzC,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IAChC,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IAChC,IAAI,EAAE,SAAS,QAAQ,EAAE,GAAG,IAAI,CAAC;IACjC,SAAS,EAAE,SAAS,OAAO,EAAE,CAAC;IAC9B,OAAO,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACrC,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAC;IACpC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;IAClC,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,IAAI,OAAO,CAAC,SAAS,iBAAiB,EAAE,CAAC,CAAC;IAC9C,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACrF,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAC5C,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAChD,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACnE,SAAS,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACrD,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAChD,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAChE,MAAM,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACnD,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9C,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,KAAK,IAAI,iBAAiB,GAAG,SAAS,CAAC;IACvC,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACpD,IAAI,CAAC,IAAI,EAAE,kBAAkB,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACpE,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAClE,MAAM,IAAI,kBAAkB,GAAG,SAAS,CAAC;IACzC,OAAO,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,CAAC;IAC9B,MAAM,IAAI,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,mBAAmB,CAAC;IAC7B,SAAS,EAAE,gBAAgB,CAAC;IAC5B,OAAO,CAAC,IAAI,EAAE,SAAS,gBAAgB,EAAE,EAAE,MAAM,EAAE,SAAS,eAAe,EAAE,GAAG,IAAI,CAAC;IACrF,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,OAAO,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,OAAO,IAAI,OAAO,CAAC;IACnB,WAAW,IAAI,IAAI,CAAC;IACpB,KAAK,CACH,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,mBAAmB,EAC5B,OAAO,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,GACxC,MAAM,IAAI,CAAC;IACd,OAAO,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,CAAC;IAC9B,OAAO,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACxC,QAAQ,IAAI,SAAS,eAAe,EAAE,CAAC;IACvC,OAAO,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAAC;IACxE,KAAK,IAAI,IAAI,CAAC;CACf,CAAC;AAwHF,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,wBAAwB,GAAG,iBAAiB,CA4dzF"}
@@ -0,0 +1,557 @@
1
+ import { conversationChangeSet } from './change-set.js';
2
+ import { declaredFile } from './file.js';
3
+ const QUIET = {
4
+ growingText: null,
5
+ plan: null,
6
+ workingNote: null,
7
+ questions: [],
8
+ process: null,
9
+ commands: [],
10
+ options: [],
11
+ };
12
+ function ordered(left, right) {
13
+ if (left.position !== right.position)
14
+ return left.position < right.position ? -1 : 1;
15
+ if (left.identity !== right.identity)
16
+ return left.identity < right.identity ? -1 : 1;
17
+ return 0;
18
+ }
19
+ function merged(held, arriving) {
20
+ const byIdentity = new Map(held.map((message) => [message.identity, message]));
21
+ for (const message of arriving)
22
+ byIdentity.set(message.identity, message);
23
+ return [...byIdentity.values()].sort(ordered);
24
+ }
25
+ function acceptedMessage(value) {
26
+ if (typeof value !== 'object' || value === null)
27
+ return null;
28
+ const record = value;
29
+ if (typeof record.identity !== 'string' ||
30
+ typeof record.turn !== 'string' ||
31
+ typeof record.position !== 'string' ||
32
+ typeof record.text !== 'string') {
33
+ return null;
34
+ }
35
+ return {
36
+ identity: record.identity,
37
+ turn: record.turn,
38
+ position: record.position,
39
+ text: record.text,
40
+ message: structuredClone(record.message),
41
+ };
42
+ }
43
+ function historyPage(value) {
44
+ if (typeof value !== 'object' || value === null)
45
+ return null;
46
+ const record = value;
47
+ if (typeof record.conversation !== 'string' || !Array.isArray(record.messages))
48
+ return null;
49
+ if (record.next !== undefined && typeof record.next !== 'string')
50
+ return null;
51
+ const messages = [];
52
+ for (const entry of record.messages) {
53
+ const message = acceptedMessage(entry);
54
+ if (message === null)
55
+ return null;
56
+ messages.push(message);
57
+ }
58
+ return {
59
+ conversation: record.conversation,
60
+ messages,
61
+ ...(record.next === undefined ? {} : { next: record.next }),
62
+ };
63
+ }
64
+ function historyPages(result) {
65
+ if (result.kind !== 'action')
66
+ return null;
67
+ const answer = result.result;
68
+ if (typeof answer !== 'object' || answer === null)
69
+ return null;
70
+ const { histories } = answer;
71
+ if (!Array.isArray(histories))
72
+ return null;
73
+ const pages = [];
74
+ for (const entry of histories) {
75
+ const page = historyPage(entry);
76
+ if (page === null)
77
+ return null;
78
+ pages.push(page);
79
+ }
80
+ return pages;
81
+ }
82
+ function turnOf(result) {
83
+ if (result.kind !== 'action')
84
+ return null;
85
+ const answer = result.result;
86
+ if (typeof answer !== 'object' || answer === null)
87
+ return null;
88
+ const record = answer;
89
+ return typeof record.turn === 'string' ? record.turn : null;
90
+ }
91
+ function limitOf(options) {
92
+ const { limit } = options;
93
+ if (limit === undefined)
94
+ return undefined;
95
+ if (!Number.isInteger(limit) || limit < 0) {
96
+ throw new Error(`conversation limit must be a non-negative integer, got ${String(limit)}`);
97
+ }
98
+ return limit;
99
+ }
100
+ export function createConversationStore(deps) {
101
+ const messages = new Map();
102
+ const reads = new Map();
103
+ const drafts = new Map();
104
+ const sending = new Map();
105
+ const behind = new Set();
106
+ const gaps = new Map();
107
+ const lives = new Map();
108
+ const views = new Map();
109
+ let replicated = new Set();
110
+ let handedReplicated;
111
+ function liveOf(conversation) {
112
+ return lives.get(conversation) ?? QUIET;
113
+ }
114
+ function viewOf(conversation, limit) {
115
+ const held = messages.get(conversation) ?? [];
116
+ const shown = limit === undefined ? held : held.slice(Math.max(0, held.length - limit));
117
+ const draft = drafts.get(conversation);
118
+ const facts = liveOf(conversation);
119
+ return {
120
+ conversation,
121
+ messages: shown.map(({ identity, turn, text, message }) => ({
122
+ identity,
123
+ turn,
124
+ text,
125
+ message: structuredClone(message),
126
+ })),
127
+ growingText: facts.growingText === null ? null : { ...facts.growingText },
128
+ workingNote: facts.workingNote === null ? null : { ...facts.workingNote },
129
+ plan: structuredClone(facts.plan?.plan ?? null),
130
+ questions: structuredClone(facts.questions),
131
+ process: facts.process === null ? null : { ...facts.process },
132
+ commands: structuredClone(facts.commands),
133
+ options: structuredClone(facts.options),
134
+ chatDraft: draft === undefined || draft.turn !== null || sending.get(conversation)?.draft === draft
135
+ ? null
136
+ : { text: draft.text, files: [...draft.files] },
137
+ };
138
+ }
139
+ function emit(conversation) {
140
+ if (deps.closed())
141
+ return;
142
+ for (const view of views.values()) {
143
+ if (view.conversation !== conversation)
144
+ continue;
145
+ const ready = [...view.watchers].filter((watcher) => watcher.ready);
146
+ if (ready.length === 0)
147
+ continue;
148
+ const value = viewOf(conversation, view.limit);
149
+ for (const watcher of ready)
150
+ watcher.notify(value);
151
+ }
152
+ }
153
+ function live(conversation, change) {
154
+ lives.set(conversation, { ...liveOf(conversation), ...change });
155
+ emit(conversation);
156
+ }
157
+ function sealTurns(conversation, turns) {
158
+ const held = lives.get(conversation);
159
+ if (held === undefined)
160
+ return false;
161
+ const growingText = held.growingText !== null && turns.has(held.growingText.turn) ? null : held.growingText;
162
+ const plan = held.plan !== null && turns.has(held.plan.turn) ? null : held.plan;
163
+ if (growingText === held.growingText && plan === held.plan)
164
+ return false;
165
+ lives.set(conversation, { ...held, growingText, plan });
166
+ return true;
167
+ }
168
+ function handReplicated() {
169
+ const stream = deps.stream();
170
+ if (stream === undefined)
171
+ return;
172
+ const held = [...replicated].sort();
173
+ const named = JSON.stringify(held);
174
+ if (named === handedReplicated)
175
+ return;
176
+ handedReplicated = named;
177
+ stream.replicate(held);
178
+ }
179
+ async function documented() {
180
+ const declaration = deps.cards();
181
+ if (declaration === undefined)
182
+ return [];
183
+ const { documents } = await deps.documents(declaration.locator);
184
+ return documents.filter((document) => typeof document.type === 'string');
185
+ }
186
+ function carded(documents) {
187
+ const declaration = deps.cards();
188
+ if (declaration === undefined)
189
+ return [];
190
+ const cards = [];
191
+ for (const card of documents) {
192
+ if (card.type !== declaration.type)
193
+ continue;
194
+ const identity = card.fields[declaration.identity];
195
+ if (typeof identity === 'string')
196
+ cards.push({ conversation: identity, card });
197
+ }
198
+ return cards;
199
+ }
200
+ async function remove(leaving) {
201
+ await deps.durably(async (local) => {
202
+ await local.dropConversations(leaving);
203
+ const removed = [];
204
+ const changed = [];
205
+ for (const conversation of leaving) {
206
+ for (const message of messages.get(conversation) ?? []) {
207
+ removed.push({ conversation, identity: message.identity });
208
+ }
209
+ if (messages.has(conversation) || drafts.has(conversation) || lives.has(conversation)) {
210
+ changed.push(conversation);
211
+ }
212
+ messages.delete(conversation);
213
+ reads.delete(conversation);
214
+ drafts.delete(conversation);
215
+ behind.delete(conversation);
216
+ lives.delete(conversation);
217
+ }
218
+ if (changed.length > 0)
219
+ deps.publish(conversationChangeSet(removed, changed));
220
+ });
221
+ }
222
+ function gap(conversation) {
223
+ gaps.set(conversation, (gaps.get(conversation) ?? 0) + 1);
224
+ behind.add(conversation);
225
+ }
226
+ function keep(conversation, message) {
227
+ return deps.durably(async (local) => {
228
+ if (!replicated.has(conversation))
229
+ return;
230
+ const read = reads.get(conversation);
231
+ const advances = read !== undefined &&
232
+ !behind.has(conversation) &&
233
+ (read === null || message.position > read);
234
+ const sealed = drafts.get(conversation)?.turn === message.turn;
235
+ await local.keepMessages({
236
+ conversation,
237
+ messages: [message],
238
+ ...(advances ? { read: message.position } : {}),
239
+ sealed,
240
+ });
241
+ messages.set(conversation, merged(messages.get(conversation) ?? [], [message]));
242
+ if (advances)
243
+ reads.set(conversation, message.position);
244
+ if (sealed)
245
+ drafts.delete(conversation);
246
+ sealTurns(conversation, new Set([message.turn]));
247
+ deps.publish(conversationChangeSet([{ conversation, identity: message.identity }], [conversation]));
248
+ });
249
+ }
250
+ async function readHistories(conversations) {
251
+ const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
252
+ const starts = new Map(conversations.map((one) => [one, reads.get(one)]));
253
+ const arriving = new Map();
254
+ const complete = new Set();
255
+ let asking = conversations.map((conversation) => ({
256
+ conversation,
257
+ after: starts.get(conversation) ?? undefined,
258
+ }));
259
+ while (asking.length > 0) {
260
+ const result = await deps.call('conversation.history', {
261
+ conversations: asking.map(({ conversation, after }) => ({
262
+ conversation,
263
+ ...(after === undefined ? {} : { after }),
264
+ })),
265
+ });
266
+ const pages = historyPages(result);
267
+ if (pages === null)
268
+ return result;
269
+ const asked = new Set(asking.map(({ conversation }) => conversation));
270
+ const following = [];
271
+ for (const page of pages) {
272
+ if (!asked.has(page.conversation))
273
+ continue;
274
+ arriving.set(page.conversation, [
275
+ ...(arriving.get(page.conversation) ?? []),
276
+ ...page.messages,
277
+ ]);
278
+ if (page.next === undefined)
279
+ complete.add(page.conversation);
280
+ else
281
+ following.push({ conversation: page.conversation, after: page.next });
282
+ }
283
+ asking = following;
284
+ }
285
+ for (const [conversation, held] of arriving) {
286
+ if (!complete.has(conversation))
287
+ continue;
288
+ await keepHistory(conversation, held, starts.get(conversation), generations.get(conversation) ?? 0);
289
+ }
290
+ return null;
291
+ }
292
+ function keepHistory(conversation, arriving, from, generation) {
293
+ return deps.durably(async (local) => {
294
+ if (!replicated.has(conversation))
295
+ return;
296
+ const newest = arriving.reduce((top, message) => (top === null || message.position > top ? message.position : top), from ?? null);
297
+ const turn = drafts.get(conversation)?.turn;
298
+ const sealed = arriving.some((message) => message.turn === turn);
299
+ await local.keepMessages({ conversation, messages: arriving, read: newest, sealed });
300
+ messages.set(conversation, merged(messages.get(conversation) ?? [], arriving));
301
+ reads.set(conversation, newest);
302
+ if (sealed)
303
+ drafts.delete(conversation);
304
+ sealTurns(conversation, new Set(arriving.map((message) => message.turn)));
305
+ if ((gaps.get(conversation) ?? 0) === generation)
306
+ behind.delete(conversation);
307
+ if (arriving.length > 0 || sealed) {
308
+ deps.publish(conversationChangeSet(arriving.map((message) => ({ conversation, identity: message.identity })), [conversation]));
309
+ }
310
+ });
311
+ }
312
+ function withDraft(conversation, next) {
313
+ return deps.durably(async (local) => {
314
+ const following = next(drafts.get(conversation));
315
+ if (following === undefined)
316
+ return;
317
+ if (following === null) {
318
+ await local.dropChatDraft(conversation);
319
+ drafts.delete(conversation);
320
+ }
321
+ else {
322
+ await local.keepChatDraft(following);
323
+ drafts.set(conversation, following);
324
+ }
325
+ deps.publish(conversationChangeSet([], [conversation]));
326
+ });
327
+ }
328
+ function invoke(name, conversation, extra = {}) {
329
+ return deps.call(name, { conversation, ...extra });
330
+ }
331
+ const surface = {
332
+ async list() {
333
+ const declaration = deps.cards();
334
+ if (declaration === undefined)
335
+ return [];
336
+ const documents = await documented();
337
+ const entries = carded(documents).map(({ conversation, card }) => ({
338
+ conversation,
339
+ card,
340
+ agent: documents.find((document) => document.backlinks.some((backlink) => backlink.path === card.path && backlink.field === declaration.agent)) ?? null,
341
+ }));
342
+ entries.sort((left, right) => (left.conversation < right.conversation ? -1 : 1));
343
+ return entries;
344
+ },
345
+ async read(conversation, options = {}) {
346
+ const limit = limitOf(options);
347
+ return deps.durably(() => Promise.resolve(viewOf(conversation, limit)));
348
+ },
349
+ create(input) {
350
+ return deps.call('conversation.create', input);
351
+ },
352
+ async send(conversation) {
353
+ const draft = drafts.get(conversation);
354
+ const pending = draft === undefined ? undefined : { draft };
355
+ if (pending !== undefined) {
356
+ sending.set(conversation, pending);
357
+ deps.publish(conversationChangeSet([], [conversation]));
358
+ }
359
+ try {
360
+ const files = await Promise.all((draft?.files ?? []).map(async (file) => ({
361
+ ...(await declaredFile(file)),
362
+ blob: file.blob,
363
+ })));
364
+ const result = await invoke('conversation.send', conversation, {
365
+ text: draft?.text ?? '',
366
+ files,
367
+ });
368
+ const turn = turnOf(result);
369
+ if (turn === null || draft === undefined)
370
+ return result;
371
+ await withDraft(conversation, (current) => {
372
+ if (current !== draft)
373
+ return undefined;
374
+ const sealed = (messages.get(conversation) ?? []).some((message) => message.turn === turn);
375
+ return sealed ? null : { ...draft, turn };
376
+ });
377
+ return result;
378
+ }
379
+ finally {
380
+ if (pending !== undefined && sending.get(conversation) === pending) {
381
+ sending.delete(conversation);
382
+ if (drafts.get(conversation) === draft) {
383
+ deps.publish(conversationChangeSet([], [conversation]));
384
+ }
385
+ }
386
+ }
387
+ },
388
+ answer(conversation, answer) {
389
+ return invoke('conversation.answer', conversation, { answer });
390
+ },
391
+ interrupt(conversation) {
392
+ return invoke('conversation.interrupt', conversation);
393
+ },
394
+ kill(conversation) {
395
+ return invoke('conversation.kill', conversation);
396
+ },
397
+ async cancel(conversation, turn) {
398
+ const result = await invoke('conversation.cancel', conversation, { turn });
399
+ if (result.kind !== 'action')
400
+ return result;
401
+ await withDraft(conversation, (current) => current === undefined || current.turn !== turn ? undefined : { ...current, turn: null });
402
+ return result;
403
+ },
404
+ delete(conversation) {
405
+ return invoke('conversation.delete', conversation);
406
+ },
407
+ };
408
+ const chatDraft = {
409
+ write(conversation, draft) {
410
+ return withDraft(conversation, () => ({
411
+ conversation,
412
+ text: draft.text,
413
+ files: [...draft.files],
414
+ turn: null,
415
+ }));
416
+ },
417
+ discard(conversation) {
418
+ return withDraft(conversation, (current) => (current === undefined ? undefined : null));
419
+ },
420
+ };
421
+ return {
422
+ surface,
423
+ chatDraft,
424
+ hydrate(held, stored) {
425
+ messages.clear();
426
+ reads.clear();
427
+ for (const entry of held) {
428
+ messages.set(entry.conversation, [...entry.messages].sort(ordered));
429
+ if (entry.read !== undefined)
430
+ reads.set(entry.conversation, entry.read);
431
+ }
432
+ drafts.clear();
433
+ for (const record of stored)
434
+ drafts.set(record.conversation, record);
435
+ },
436
+ async reconcile() {
437
+ const named = new Set(carded(await documented()).map((entry) => entry.conversation));
438
+ const leaving = [
439
+ ...new Set([...replicated, ...messages.keys(), ...reads.keys(), ...drafts.keys()]),
440
+ ].filter((conversation) => !named.has(conversation));
441
+ for (const conversation of named) {
442
+ if (!replicated.has(conversation))
443
+ behind.add(conversation);
444
+ }
445
+ replicated = named;
446
+ handReplicated();
447
+ if (leaving.length > 0)
448
+ await remove(leaving);
449
+ },
450
+ async catchUp(progress) {
451
+ const reading = [...behind].filter((conversation) => replicated.has(conversation)).sort();
452
+ if (reading.length > 0 && !deps.closed()) {
453
+ const failed = await readHistories(reading);
454
+ if (failed?.kind === 'transport-uncertainty')
455
+ progress.reachable = false;
456
+ }
457
+ if (behind.size > 0)
458
+ progress.complete = false;
459
+ },
460
+ current() {
461
+ return behind.size === 0;
462
+ },
463
+ reconnected() {
464
+ for (const conversation of replicated)
465
+ gap(conversation);
466
+ },
467
+ watch(conversation, options, deliver) {
468
+ const limit = limitOf(options);
469
+ const key = JSON.stringify([conversation, limit ?? null]);
470
+ const view = views.get(key) ?? { conversation, limit, watchers: new Set() };
471
+ views.set(key, view);
472
+ const watcher = { notify: deliver, ready: false };
473
+ view.watchers.add(watcher);
474
+ void deps
475
+ .durably(() => Promise.resolve())
476
+ .then(() => {
477
+ if (deps.closed() || !view.watchers.has(watcher))
478
+ return;
479
+ watcher.ready = true;
480
+ watcher.notify(viewOf(conversation, limit));
481
+ })
482
+ .catch(() => { });
483
+ return () => {
484
+ if (!view.watchers.delete(watcher))
485
+ return;
486
+ if (view.watchers.size === 0)
487
+ views.delete(key);
488
+ };
489
+ },
490
+ changed(set) {
491
+ for (const conversation of set.conversations)
492
+ emit(conversation);
493
+ },
494
+ receive(event) {
495
+ if (deps.closed())
496
+ return;
497
+ const { conversation } = event;
498
+ if (event.kind === 'gap') {
499
+ if (replicated.has(conversation))
500
+ gap(conversation);
501
+ if (lives.delete(conversation))
502
+ emit(conversation);
503
+ return;
504
+ }
505
+ if (event.kind === 'growing-text') {
506
+ live(conversation, { growingText: { turn: event.turn, text: event.text } });
507
+ return;
508
+ }
509
+ if (event.kind === 'plan') {
510
+ live(conversation, { plan: { turn: event.turn, plan: structuredClone(event.plan) } });
511
+ return;
512
+ }
513
+ if (event.kind === 'working-note') {
514
+ live(conversation, {
515
+ workingNote: event.note === null ? null : { noteId: event.note.noteId, text: event.note.text },
516
+ });
517
+ return;
518
+ }
519
+ if (event.kind === 'questions') {
520
+ live(conversation, { questions: structuredClone(event.questions) });
521
+ return;
522
+ }
523
+ if (event.kind === 'process') {
524
+ live(conversation, { process: { running: event.running } });
525
+ return;
526
+ }
527
+ if (event.kind === 'commands') {
528
+ live(conversation, { commands: structuredClone(event.commands) });
529
+ return;
530
+ }
531
+ if (event.kind === 'options') {
532
+ live(conversation, { options: structuredClone(event.options) });
533
+ return;
534
+ }
535
+ const arriving = acceptedMessage(event.message);
536
+ if (arriving === null)
537
+ return;
538
+ if (replicated.has(conversation)) {
539
+ void keep(conversation, arriving).catch(() => { });
540
+ return;
541
+ }
542
+ if (sealTurns(conversation, new Set([arriving.turn])))
543
+ emit(conversation);
544
+ },
545
+ messages() {
546
+ return [...messages].flatMap(([conversation, held]) => held.map(({ identity, text }) => ({ conversation, identity, text })));
547
+ },
548
+ message(conversation, identity) {
549
+ const held = messages.get(conversation)?.find((message) => message.identity === identity);
550
+ return held === undefined ? null : { conversation, identity, text: held.text };
551
+ },
552
+ close() {
553
+ views.clear();
554
+ lives.clear();
555
+ },
556
+ };
557
+ }
@@ -1,4 +1,9 @@
1
- export declare function requestPersistentStorage(): Promise<void>;
2
- export declare function detectStorageLoss(stored: string | null): string | null;
3
- export declare function nextGeneration(stored: string | null): string;
1
+ export type RuntimeShell = {
2
+ persistentStorage: boolean;
3
+ generation: string | null;
4
+ keepGeneration(value: string): void | Promise<void>;
5
+ dropGeneration(): void | Promise<void>;
6
+ };
7
+ export declare function detectStorageLoss(stored: string | null, reported: string | null): string | null;
8
+ export declare function nextGeneration(stored: string | null, reported: string | null): string;
4
9
  //# sourceMappingURL=durability.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"durability.d.ts","sourceRoot":"","sources":["../src/durability.ts"],"names":[],"mappings":"AAiCA,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,IAAI,CAAC,CAG9D;AAoBD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAItE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAS5D"}
1
+ {"version":3,"file":"durability.d.ts","sourceRoot":"","sources":["../src/durability.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,YAAY,GAAG;IACzB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,cAAc,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAG/F;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAGrF"}