@agentshouse/mdruntime 0.1.0-cd50a4607cb7d → 0.1.0-cd574a610e9c8

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 (401) hide show
  1. package/README.md +24 -19
  2. package/dist/acceptance.d.ts +3 -0
  3. package/dist/acceptance.d.ts.map +1 -0
  4. package/dist/acceptance.js +299 -0
  5. package/dist/act-lanes.d.ts +30 -0
  6. package/dist/act-lanes.d.ts.map +1 -0
  7. package/dist/act-lanes.js +225 -0
  8. package/dist/actions.d.ts +26 -0
  9. package/dist/actions.d.ts.map +1 -0
  10. package/dist/actions.js +302 -0
  11. package/dist/adapter.d.ts +226 -117
  12. package/dist/adapter.d.ts.map +1 -1
  13. package/dist/attached-model.d.ts +6 -10
  14. package/dist/attached-model.d.ts.map +1 -1
  15. package/dist/attached-model.js +35 -41
  16. package/dist/authored.d.ts +88 -0
  17. package/dist/authored.d.ts.map +1 -0
  18. package/dist/authored.js +345 -0
  19. package/dist/binary.d.ts +0 -5
  20. package/dist/binary.d.ts.map +1 -1
  21. package/dist/binary.js +1 -1
  22. package/dist/bootstrap.d.ts +31 -11
  23. package/dist/bootstrap.d.ts.map +1 -1
  24. package/dist/bootstrap.js +309 -109
  25. package/dist/cell.d.ts +6 -0
  26. package/dist/cell.d.ts.map +1 -0
  27. package/dist/cell.js +9 -0
  28. package/dist/chain.d.ts +12 -0
  29. package/dist/chain.d.ts.map +1 -0
  30. package/dist/chain.js +64 -0
  31. package/dist/change-set.d.ts +13 -16
  32. package/dist/change-set.d.ts.map +1 -1
  33. package/dist/change-set.js +17 -88
  34. package/dist/change.d.ts +36 -1
  35. package/dist/change.d.ts.map +1 -1
  36. package/dist/change.js +17 -3
  37. package/dist/chat-types.d.ts +3 -0
  38. package/dist/chat-types.d.ts.map +1 -0
  39. package/dist/chat-types.js +11 -0
  40. package/dist/client.d.ts +45 -25
  41. package/dist/client.d.ts.map +1 -1
  42. package/dist/client.js +132 -27
  43. package/dist/collection.d.ts +32 -21
  44. package/dist/collection.d.ts.map +1 -1
  45. package/dist/collection.js +53 -288
  46. package/dist/column.d.ts +5 -0
  47. package/dist/column.d.ts.map +1 -0
  48. package/dist/column.js +9 -0
  49. package/dist/compile.d.ts +18 -0
  50. package/dist/compile.d.ts.map +1 -0
  51. package/dist/compile.js +723 -0
  52. package/dist/connection.d.ts +7 -118
  53. package/dist/connection.d.ts.map +1 -1
  54. package/dist/connection.js +111 -2975
  55. package/dist/context.d.ts +2 -0
  56. package/dist/context.d.ts.map +1 -0
  57. package/dist/{chat/context.js → context.js} +2 -5
  58. package/dist/conversation-live.d.ts +150 -0
  59. package/dist/conversation-live.d.ts.map +1 -0
  60. package/dist/conversation-live.js +259 -0
  61. package/dist/conversation-row.d.ts +40 -0
  62. package/dist/conversation-row.d.ts.map +1 -0
  63. package/dist/conversation-row.js +66 -0
  64. package/dist/conversation.d.ts +85 -48
  65. package/dist/conversation.d.ts.map +1 -1
  66. package/dist/conversation.js +1095 -688
  67. package/dist/declaration.d.ts +10 -0
  68. package/dist/declaration.d.ts.map +1 -0
  69. package/dist/declaration.js +24 -0
  70. package/dist/digest.d.ts +7 -0
  71. package/dist/digest.d.ts.map +1 -0
  72. package/dist/digest.js +113 -0
  73. package/dist/discovery.d.ts +10 -0
  74. package/dist/discovery.d.ts.map +1 -0
  75. package/dist/discovery.js +150 -0
  76. package/dist/durability.d.ts +23 -1
  77. package/dist/durability.d.ts.map +1 -1
  78. package/dist/durability.js +9 -0
  79. package/dist/entry.d.ts +5 -2
  80. package/dist/entry.d.ts.map +1 -1
  81. package/dist/entry.js +33 -28
  82. package/dist/expectation.d.ts +26 -13
  83. package/dist/expectation.d.ts.map +1 -1
  84. package/dist/expectation.js +72 -19
  85. package/dist/failure.d.ts +3 -0
  86. package/dist/failure.d.ts.map +1 -1
  87. package/dist/failure.js +6 -0
  88. package/dist/figures.d.ts +49 -0
  89. package/dist/figures.d.ts.map +1 -0
  90. package/dist/figures.js +199 -0
  91. package/dist/file.d.ts +2 -19
  92. package/dist/file.d.ts.map +1 -1
  93. package/dist/file.js +8 -122
  94. package/dist/holdings.d.ts +29 -0
  95. package/dist/holdings.d.ts.map +1 -0
  96. package/dist/holdings.js +95 -0
  97. package/dist/host.d.ts +27 -4
  98. package/dist/host.d.ts.map +1 -1
  99. package/dist/host.js +177 -27
  100. package/dist/identity.d.ts +0 -2
  101. package/dist/identity.d.ts.map +1 -1
  102. package/dist/identity.js +0 -6
  103. package/dist/index.d.ts +41 -14
  104. package/dist/index.d.ts.map +1 -1
  105. package/dist/index.js +19 -3
  106. package/dist/install.d.ts +60 -0
  107. package/dist/install.d.ts.map +1 -0
  108. package/dist/install.js +537 -0
  109. package/dist/lifecycle.d.ts +7 -0
  110. package/dist/lifecycle.d.ts.map +1 -0
  111. package/dist/lifecycle.js +282 -0
  112. package/dist/listing.d.ts +52 -0
  113. package/dist/listing.d.ts.map +1 -0
  114. package/dist/listing.js +152 -0
  115. package/dist/live.d.ts +25 -0
  116. package/dist/live.d.ts.map +1 -0
  117. package/dist/live.js +206 -0
  118. package/dist/loaded.d.ts +5 -0
  119. package/dist/loaded.d.ts.map +1 -0
  120. package/dist/loaded.js +40 -0
  121. package/dist/locks.d.ts +7 -0
  122. package/dist/locks.d.ts.map +1 -0
  123. package/dist/locks.js +32 -0
  124. package/dist/lost.d.ts +5 -0
  125. package/dist/lost.d.ts.map +1 -0
  126. package/dist/lost.js +49 -0
  127. package/dist/media.d.ts +13 -0
  128. package/dist/media.d.ts.map +1 -0
  129. package/dist/media.js +269 -0
  130. package/dist/memory.d.ts +25 -7
  131. package/dist/memory.d.ts.map +1 -1
  132. package/dist/memory.js +320 -110
  133. package/dist/messages.d.ts +58 -0
  134. package/dist/messages.d.ts.map +1 -0
  135. package/dist/messages.js +160 -0
  136. package/dist/model.d.ts +11 -13
  137. package/dist/model.d.ts.map +1 -1
  138. package/dist/model.js +7 -24
  139. package/dist/models.d.ts +10 -0
  140. package/dist/models.d.ts.map +1 -0
  141. package/dist/models.js +153 -0
  142. package/dist/name.d.ts +4 -0
  143. package/dist/name.d.ts.map +1 -0
  144. package/dist/name.js +122 -0
  145. package/dist/observation.d.ts +40 -10
  146. package/dist/observation.d.ts.map +1 -1
  147. package/dist/observation.js +233 -39
  148. package/dist/opened.d.ts +22 -0
  149. package/dist/opened.d.ts.map +1 -0
  150. package/dist/opened.js +107 -0
  151. package/dist/opening.d.ts +4 -0
  152. package/dist/opening.d.ts.map +1 -0
  153. package/dist/opening.js +53 -0
  154. package/dist/outcome.d.ts +62 -9
  155. package/dist/outcome.d.ts.map +1 -1
  156. package/dist/outcome.js +72 -0
  157. package/dist/overlay.d.ts +9 -6
  158. package/dist/overlay.d.ts.map +1 -1
  159. package/dist/overlay.js +129 -166
  160. package/dist/page-metadata.d.ts +8 -0
  161. package/dist/page-metadata.d.ts.map +1 -0
  162. package/dist/page-metadata.js +19 -0
  163. package/dist/parts.d.ts +4 -0
  164. package/dist/parts.d.ts.map +1 -0
  165. package/dist/parts.js +13 -0
  166. package/dist/paths.d.ts +2 -0
  167. package/dist/paths.d.ts.map +1 -1
  168. package/dist/paths.js +26 -0
  169. package/dist/preparation.d.ts +41 -0
  170. package/dist/preparation.d.ts.map +1 -0
  171. package/dist/preparation.js +314 -0
  172. package/dist/presence.d.ts +58 -0
  173. package/dist/presence.d.ts.map +1 -0
  174. package/dist/presence.js +231 -0
  175. package/dist/queries.d.ts +16 -0
  176. package/dist/queries.d.ts.map +1 -0
  177. package/dist/queries.js +308 -0
  178. package/dist/query.d.ts +27 -186
  179. package/dist/query.d.ts.map +1 -1
  180. package/dist/query.js +3 -464
  181. package/dist/{chat/questions.d.ts → questions.d.ts} +6 -7
  182. package/dist/questions.d.ts.map +1 -0
  183. package/dist/{chat/questions.js → questions.js} +12 -7
  184. package/dist/queue.d.ts +40 -0
  185. package/dist/queue.d.ts.map +1 -0
  186. package/dist/queue.js +480 -0
  187. package/dist/reach.d.ts +20 -7
  188. package/dist/reach.d.ts.map +1 -1
  189. package/dist/reach.js +360 -98
  190. package/dist/react.d.ts +22 -5
  191. package/dist/react.d.ts.map +1 -1
  192. package/dist/react.js +40 -18
  193. package/dist/{chat/read.d.ts → read.d.ts} +2 -0
  194. package/dist/read.d.ts.map +1 -0
  195. package/dist/{chat/read.js → read.js} +4 -0
  196. package/dist/readiness.d.ts +55 -0
  197. package/dist/readiness.d.ts.map +1 -0
  198. package/dist/readiness.js +25 -0
  199. package/dist/reads.d.ts +39 -0
  200. package/dist/reads.d.ts.map +1 -0
  201. package/dist/reads.js +178 -0
  202. package/dist/recovery.d.ts +5 -2
  203. package/dist/recovery.d.ts.map +1 -1
  204. package/dist/recovery.js +65 -8
  205. package/dist/refresh.d.ts +17 -14
  206. package/dist/refresh.d.ts.map +1 -1
  207. package/dist/refresh.js +145 -54
  208. package/dist/refusal.d.ts +5 -0
  209. package/dist/refusal.d.ts.map +1 -0
  210. package/dist/refusal.js +55 -0
  211. package/dist/replica.d.ts +50 -33
  212. package/dist/replica.d.ts.map +1 -1
  213. package/dist/replica.js +340 -191
  214. package/dist/retried.d.ts +30 -0
  215. package/dist/retried.d.ts.map +1 -0
  216. package/dist/retried.js +72 -0
  217. package/dist/room-draft.d.ts +66 -12
  218. package/dist/room-draft.d.ts.map +1 -1
  219. package/dist/room-draft.js +416 -122
  220. package/dist/room-drafts.d.ts +24 -0
  221. package/dist/room-drafts.d.ts.map +1 -0
  222. package/dist/room-drafts.js +220 -0
  223. package/dist/room-list.d.ts +10 -0
  224. package/dist/room-list.d.ts.map +1 -0
  225. package/dist/room-list.js +45 -0
  226. package/dist/rows.d.ts +32 -29
  227. package/dist/rows.d.ts.map +1 -1
  228. package/dist/rows.js +60 -118
  229. package/dist/runtime.d.ts +34 -0
  230. package/dist/runtime.d.ts.map +1 -0
  231. package/dist/runtime.js +1 -0
  232. package/dist/save.d.ts +43 -48
  233. package/dist/save.d.ts.map +1 -1
  234. package/dist/save.js +751 -303
  235. package/dist/saves.d.ts +34 -0
  236. package/dist/saves.d.ts.map +1 -0
  237. package/dist/saves.js +437 -0
  238. package/dist/search.d.ts +15 -77
  239. package/dist/search.d.ts.map +1 -1
  240. package/dist/search.js +10 -186
  241. package/dist/seed.d.ts +25 -0
  242. package/dist/seed.d.ts.map +1 -0
  243. package/dist/seed.js +145 -0
  244. package/dist/shared.d.ts +6 -7
  245. package/dist/shared.d.ts.map +1 -1
  246. package/dist/shared.js +75 -104
  247. package/dist/shown.d.ts +33 -0
  248. package/dist/shown.d.ts.map +1 -0
  249. package/dist/shown.js +309 -0
  250. package/dist/socket/admission.d.ts +7 -0
  251. package/dist/socket/admission.d.ts.map +1 -0
  252. package/dist/socket/admission.js +39 -0
  253. package/dist/socket/answers.d.ts +6 -0
  254. package/dist/socket/answers.d.ts.map +1 -0
  255. package/dist/socket/answers.js +20 -0
  256. package/dist/socket/lanes.d.ts +28 -0
  257. package/dist/socket/lanes.d.ts.map +1 -0
  258. package/dist/socket/lanes.js +103 -0
  259. package/dist/socket/reads.d.ts +10 -0
  260. package/dist/socket/reads.d.ts.map +1 -0
  261. package/dist/socket/reads.js +31 -0
  262. package/dist/socket/session.d.ts +38 -0
  263. package/dist/socket/session.d.ts.map +1 -0
  264. package/dist/socket/session.js +23 -0
  265. package/dist/socket.d.ts +67 -0
  266. package/dist/socket.d.ts.map +1 -0
  267. package/dist/socket.js +164 -0
  268. package/dist/source.d.ts +59 -0
  269. package/dist/source.d.ts.map +1 -0
  270. package/dist/source.js +93 -0
  271. package/dist/status.d.ts +4 -5
  272. package/dist/status.d.ts.map +1 -1
  273. package/dist/status.js +3 -7
  274. package/dist/store/installs.d.ts +30 -0
  275. package/dist/store/installs.d.ts.map +1 -0
  276. package/dist/store/installs.js +40 -0
  277. package/dist/store/readiness.d.ts +11 -0
  278. package/dist/store/readiness.d.ts.map +1 -0
  279. package/dist/store/readiness.js +68 -0
  280. package/dist/store/records.d.ts +223 -0
  281. package/dist/store/records.d.ts.map +1 -0
  282. package/dist/store/records.js +465 -0
  283. package/dist/store/room-held.d.ts +8 -0
  284. package/dist/store/room-held.d.ts.map +1 -0
  285. package/dist/store/room-held.js +17 -0
  286. package/dist/store/rooms-shown.d.ts +8 -0
  287. package/dist/store/rooms-shown.d.ts.map +1 -0
  288. package/dist/store/rooms-shown.js +13 -0
  289. package/dist/store/space.d.ts +11 -0
  290. package/dist/store/space.d.ts.map +1 -0
  291. package/dist/store/space.js +41 -0
  292. package/dist/store/watchers.d.ts +17 -0
  293. package/dist/store/watchers.d.ts.map +1 -0
  294. package/dist/store/watchers.js +56 -0
  295. package/dist/store/writes.d.ts +24 -0
  296. package/dist/store/writes.d.ts.map +1 -0
  297. package/dist/store/writes.js +125 -0
  298. package/dist/store.d.ts +114 -141
  299. package/dist/store.d.ts.map +1 -1
  300. package/dist/store.js +163 -687
  301. package/dist/stored.d.ts +167 -0
  302. package/dist/stored.d.ts.map +1 -0
  303. package/dist/stored.js +700 -0
  304. package/dist/surface.d.ts +90 -0
  305. package/dist/surface.d.ts.map +1 -0
  306. package/dist/surface.js +225 -0
  307. package/dist/sync.d.ts +19 -0
  308. package/dist/sync.d.ts.map +1 -0
  309. package/dist/sync.js +536 -0
  310. package/dist/tables.d.ts +376 -0
  311. package/dist/tables.d.ts.map +1 -0
  312. package/dist/tables.js +499 -0
  313. package/dist/tallies.d.ts +18 -0
  314. package/dist/tallies.d.ts.map +1 -0
  315. package/dist/tallies.js +128 -0
  316. package/dist/transfer.d.ts +0 -5
  317. package/dist/transfer.d.ts.map +1 -1
  318. package/dist/turn.d.ts +5 -3
  319. package/dist/turn.d.ts.map +1 -1
  320. package/dist/turn.js +51 -21
  321. package/dist/validation.d.ts +29 -0
  322. package/dist/validation.d.ts.map +1 -0
  323. package/dist/validation.js +98 -0
  324. package/dist/windows.d.ts +11 -0
  325. package/dist/windows.d.ts.map +1 -0
  326. package/dist/windows.js +16 -0
  327. package/dist/wire.d.ts +230 -36
  328. package/dist/wire.d.ts.map +1 -1
  329. package/dist/wire.js +77 -33
  330. package/package.json +19 -19
  331. package/AGENTS.md +0 -296
  332. package/dist/attempt.d.ts +0 -16
  333. package/dist/attempt.d.ts.map +0 -1
  334. package/dist/attempt.js +0 -70
  335. package/dist/chat/acts.d.ts +0 -33
  336. package/dist/chat/acts.d.ts.map +0 -1
  337. package/dist/chat/acts.js +0 -36
  338. package/dist/chat/context.d.ts +0 -3
  339. package/dist/chat/context.d.ts.map +0 -1
  340. package/dist/chat/input.d.ts +0 -9
  341. package/dist/chat/input.d.ts.map +0 -1
  342. package/dist/chat/input.js +0 -99
  343. package/dist/chat/kit.d.ts +0 -60
  344. package/dist/chat/kit.d.ts.map +0 -1
  345. package/dist/chat/kit.js +0 -123
  346. package/dist/chat/lasting.d.ts +0 -6
  347. package/dist/chat/lasting.d.ts.map +0 -1
  348. package/dist/chat/lasting.js +0 -19
  349. package/dist/chat/list.d.ts +0 -26
  350. package/dist/chat/list.d.ts.map +0 -1
  351. package/dist/chat/list.js +0 -62
  352. package/dist/chat/markdown.d.ts +0 -20
  353. package/dist/chat/markdown.d.ts.map +0 -1
  354. package/dist/chat/markdown.js +0 -188
  355. package/dist/chat/message.d.ts +0 -6
  356. package/dist/chat/message.d.ts.map +0 -1
  357. package/dist/chat/message.js +0 -192
  358. package/dist/chat/messages.d.ts +0 -21
  359. package/dist/chat/messages.d.ts.map +0 -1
  360. package/dist/chat/messages.js +0 -151
  361. package/dist/chat/name.d.ts +0 -6
  362. package/dist/chat/name.d.ts.map +0 -1
  363. package/dist/chat/name.js +0 -37
  364. package/dist/chat/opened.d.ts +0 -11
  365. package/dist/chat/opened.d.ts.map +0 -1
  366. package/dist/chat/opened.js +0 -17
  367. package/dist/chat/overlay.d.ts +0 -31
  368. package/dist/chat/overlay.d.ts.map +0 -1
  369. package/dist/chat/overlay.js +0 -97
  370. package/dist/chat/parts.d.ts +0 -57
  371. package/dist/chat/parts.d.ts.map +0 -1
  372. package/dist/chat/parts.js +0 -119
  373. package/dist/chat/process.d.ts +0 -20
  374. package/dist/chat/process.d.ts.map +0 -1
  375. package/dist/chat/process.js +0 -32
  376. package/dist/chat/provider.d.ts +0 -63
  377. package/dist/chat/provider.d.ts.map +0 -1
  378. package/dist/chat/provider.js +0 -620
  379. package/dist/chat/question.d.ts +0 -9
  380. package/dist/chat/question.d.ts.map +0 -1
  381. package/dist/chat/question.js +0 -80
  382. package/dist/chat/questions.d.ts.map +0 -1
  383. package/dist/chat/read.d.ts.map +0 -1
  384. package/dist/chat/ready.d.ts +0 -33
  385. package/dist/chat/ready.d.ts.map +0 -1
  386. package/dist/chat/ready.js +0 -91
  387. package/dist/chat/style.d.ts +0 -4
  388. package/dist/chat/style.d.ts.map +0 -1
  389. package/dist/chat/style.js +0 -302
  390. package/dist/chat.d.ts +0 -14
  391. package/dist/chat.d.ts.map +0 -1
  392. package/dist/chat.js +0 -8
  393. package/dist/fence.d.ts +0 -6
  394. package/dist/fence.d.ts.map +0 -1
  395. package/dist/fence.js +0 -10
  396. package/dist/log.d.ts +0 -5
  397. package/dist/log.d.ts.map +0 -1
  398. package/dist/log.js +0 -14
  399. package/dist/read-side.d.ts +0 -25
  400. package/dist/read-side.d.ts.map +0 -1
  401. package/dist/read-side.js +0 -639
package/dist/memory.js CHANGED
@@ -1,7 +1,20 @@
1
+ import { definitionAt, documentFacts, keyedDestination, keyedLink, parse, } from '@agentshouse/mdmodel';
2
+ import { NO_ROOM_TYPES, schemaTypes } from './attached-model.js';
3
+ import { selected } from './collection.js';
4
+ import { declaredType } from './outcome.js';
1
5
  import { sameChange } from './overlay.js';
6
+ const ATTACHMENTS = 'attachments';
7
+ const UNFOUND = {
8
+ kind: 'rejected',
9
+ code: 'not_found',
10
+ fields: {},
11
+ retryable: false,
12
+ };
13
+ function lastName(path) {
14
+ return path.slice(path.lastIndexOf('/') + 1);
15
+ }
2
16
  const FIXTURE_RETENTION = {
3
17
  history: { kind: 'duration', seconds: 1 },
4
- outcome: 604_800,
5
18
  refreshHorizon: 1,
6
19
  };
7
20
  function declaredRetention(value) {
@@ -15,12 +28,7 @@ function declaredRetention(value) {
15
28
  }
16
29
  const MODEL_ABSENT = { kind: 'model-unavailable', reason: 'not-readable' };
17
30
  function modelsOf(room) {
18
- return {
19
- collection: room.model ?? MODEL_ABSENT,
20
- ...(room.typedSelection === undefined
21
- ? {}
22
- : { selection: room.selectionModel ?? MODEL_ABSENT }),
23
- };
31
+ return { collection: room.model ?? MODEL_ABSENT };
24
32
  }
25
33
  function changedModels(room, from) {
26
34
  const now = modelsOf(room);
@@ -53,13 +61,18 @@ export function createMemoryAdapter(options = {}) {
53
61
  const listeners = new Set();
54
62
  const modeObservers = new Set();
55
63
  const scripted = new Map();
56
- const unsealed = new Map();
57
- const sealed = new Set();
58
- const sentTurns = new Map();
64
+ const repeats = new Map();
59
65
  const historyPageSize = options.historyPageSize;
60
- let held = [];
66
+ const attached = new Map();
67
+ const keyed = new Map();
68
+ const linked = new WeakMap();
69
+ const carried = new Map();
61
70
  let expiring = [];
71
+ let openings = 0;
72
+ const declaredRooms = new Set();
62
73
  let replicated = [];
74
+ let heldCursor = null;
75
+ let agents = structuredClone(options.agents ?? []);
63
76
  let issued = 0;
64
77
  function scriptConversations(entries) {
65
78
  scripted.clear();
@@ -87,8 +100,72 @@ export function createMemoryAdapter(options = {}) {
87
100
  const wanted = ordinals.get(`${locator}\u0000${target}`);
88
101
  return held !== undefined && wanted !== undefined && held >= wanted;
89
102
  }
103
+ function heldBlob(room, path) {
104
+ return attached.get(`${room.locator}\u0000${path}`) ?? room.blobs?.[path];
105
+ }
106
+ function linkTo(room, path) {
107
+ const blob = room === undefined ? undefined : heldBlob(room, path);
108
+ if (blob === undefined)
109
+ return UNFOUND;
110
+ const url = linked.get(blob) ?? URL.createObjectURL(blob);
111
+ linked.set(blob, url);
112
+ return { kind: 'link', url, name: lastName(path), mediaType: declaredType(blob) };
113
+ }
114
+ function attachedKeyed(request) {
115
+ const room = rooms.find((entry) => entry.locator === options.privateRoom);
116
+ if (room === undefined) {
117
+ return { kind: 'rejected', code: 'room_not_found', fields: {}, retryable: false };
118
+ }
119
+ const path = freePath(room, request.name);
120
+ attached.set(`${room.locator}\u0000${path}`, request.blob);
121
+ const key = `memory-${keyed.size + 1}`;
122
+ keyed.set(key, { locator: room.locator, path });
123
+ return { kind: 'linked', link: keyedDestination(`/rooms/${room.handle}/${path}`, key) };
124
+ }
125
+ function freePath(room, name) {
126
+ const dot = name.lastIndexOf('.');
127
+ const stem = dot > 0 ? name.slice(0, dot) : name;
128
+ const extension = dot > 0 ? name.slice(dot) : '';
129
+ for (let count = 1;; count += 1) {
130
+ const path = `${ATTACHMENTS}/${count === 1 ? stem : `${stem}-${count}`}${extension}`;
131
+ if (heldBlob(room, path) === undefined && room.documents?.[path] === undefined)
132
+ return path;
133
+ }
134
+ }
135
+ const saved = new WeakMap();
136
+ const roomTypes = options.roomTypes ?? NO_ROOM_TYPES;
137
+ function factsAt(room, path, content) {
138
+ const types = selected({ selection: room.typedSelection ?? null }, path)
139
+ ? roomTypes.types
140
+ : schemaTypes(room.model?.kind === 'model' ? room.model.snapshot.schema : null, roomTypes);
141
+ let held = saved.get(types);
142
+ if (held === undefined) {
143
+ held = new Map();
144
+ saved.set(types, held);
145
+ }
146
+ const key = `${path}\u0000${content}`;
147
+ if (!held.has(key)) {
148
+ const facts = documentFacts(path, parse(content, path, definitionAt({ types }, path, options.typedPaths)), {
149
+ types,
150
+ });
151
+ held.set(key, facts === null ? null : JSON.parse(JSON.stringify(facts)));
152
+ }
153
+ return held.get(key);
154
+ }
155
+ function factsIn(room, paths) {
156
+ const facts = {};
157
+ for (const path of paths) {
158
+ const content = room.documents?.[path];
159
+ const held = room.facts?.[path] ?? (content === undefined ? null : factsAt(room, path, content));
160
+ if (held !== null)
161
+ facts[path] = held;
162
+ }
163
+ return facts;
164
+ }
90
165
  function retain(entries) {
91
166
  for (const room of entries) {
167
+ for (const [path, content] of Object.entries(room.documents ?? {}))
168
+ factsAt(room, path, content);
92
169
  note(room.locator, room);
93
170
  const versions = served.get(room.locator) ?? new Map();
94
171
  versions.set(room.roomVersion, room);
@@ -104,8 +181,9 @@ export function createMemoryAdapter(options = {}) {
104
181
  roomVersion: room.roomVersion,
105
182
  operations: room.operations,
106
183
  replica: room.replica,
107
- ...(room.typedSelection === undefined ? {} : { typedSelection: room.typedSelection }),
184
+ typedSelection: room.typedSelection ?? null,
108
185
  providerOwnedPaths: room.providerOwnedPaths,
186
+ authoredPaths: room.authoredPaths ?? ['**'],
109
187
  };
110
188
  }
111
189
  function page(room, after) {
@@ -121,23 +199,43 @@ export function createMemoryAdapter(options = {}) {
121
199
  roomVersion: room.roomVersion,
122
200
  ...(opening ? { models: modelsOf(room) } : {}),
123
201
  documents: named(room.documents ?? {}, taken),
202
+ facts: factsIn(room, taken),
124
203
  revisions: named(room.revisions ?? {}, [...taken, ...targets]),
204
+ ...(room.fileIds === undefined
205
+ ? {}
206
+ : { fileIds: named(room.fileIds, [...taken, ...targets]) }),
207
+ ...(room.created === undefined
208
+ ? {}
209
+ : { created: named(room.created, [...taken, ...targets]) }),
125
210
  changes: named(room.changes ?? {}, [...taken, ...targets]),
126
211
  binaryFiles: named(room.binaryFiles ?? {}, targets),
212
+ ...(opening && room.folders !== undefined ? { folders: room.folders } : {}),
127
213
  ...(next < paths.length ? { pageToken: taken.at(-1) } : {}),
128
214
  };
129
215
  }
130
- function advancement(room, from, at) {
216
+ function foldersMoved(room, from) {
217
+ const now = new Set(room.folders ?? []);
218
+ const before = new Set(from.folders ?? []);
219
+ const folders = [...now].filter((folder) => !before.has(folder)).sort();
220
+ const folderRemovals = [...before].filter((folder) => !now.has(folder)).sort();
221
+ return {
222
+ ...(folders.length === 0 ? {} : { folders }),
223
+ ...(folderRemovals.length === 0 ? {} : { folderRemovals }),
224
+ };
225
+ }
226
+ function advancement(room, from, at, whole = false) {
131
227
  const current = room.documents ?? {};
132
228
  const previous = from.documents ?? {};
133
229
  const currentTargets = new Set(Object.keys(room.binaryFiles ?? {}));
134
230
  const previousTargets = new Set(Object.keys(from.binaryFiles ?? {}));
135
231
  const headMoved = (path) => room.revisions?.[path] !== from.revisions?.[path] ||
232
+ room.fileIds?.[path] !== from.fileIds?.[path] ||
233
+ room.created?.[path] !== from.created?.[path] ||
136
234
  !sameChange(room.changes?.[path], from.changes?.[path]);
137
235
  const changed = Object.keys(current)
138
236
  .filter((path) => current[path] !== previous[path] || headMoved(path))
139
237
  .sort();
140
- const size = pageSize ?? changed.length;
238
+ const size = whole ? changed.length : (pageSize ?? changed.length);
141
239
  const taken = changed.slice(at, at + size);
142
240
  const next = at + taken.length;
143
241
  const opening = at === 0;
@@ -153,9 +251,17 @@ export function createMemoryAdapter(options = {}) {
153
251
  roomVersion: room.roomVersion,
154
252
  ...(models === undefined ? {} : { models }),
155
253
  documents: named(current, taken),
254
+ facts: factsIn(room, taken),
156
255
  revisions: named(room.revisions ?? {}, [...taken, ...targets]),
256
+ ...(room.fileIds === undefined
257
+ ? {}
258
+ : { fileIds: named(room.fileIds, [...taken, ...targets]) }),
259
+ ...(room.created === undefined
260
+ ? {}
261
+ : { created: named(room.created, [...taken, ...targets]) }),
157
262
  changes: named(room.changes ?? {}, [...taken, ...targets]),
158
263
  binaryFiles: named(room.binaryFiles ?? {}, targets),
264
+ ...(opening ? foldersMoved(room, from) : {}),
159
265
  removals: opening
160
266
  ? [
161
267
  ...Object.keys(previous).filter((path) => !(path in current) && !currentTargets.has(path)),
@@ -165,30 +271,45 @@ export function createMemoryAdapter(options = {}) {
165
271
  ...(pageToken === undefined ? {} : { pageToken }),
166
272
  };
167
273
  }
274
+ function carry(locator) {
275
+ const at = carried.get(locator);
276
+ const current = rooms.find((entry) => entry.locator === locator);
277
+ if (at === undefined || current === undefined || current.roomVersion === at)
278
+ return;
279
+ const from = served.get(locator)?.get(at);
280
+ carried.set(locator, current.roomVersion);
281
+ if (from === undefined)
282
+ return;
283
+ const page = advancement(current, from, 0, true);
284
+ for (const observer of observers) {
285
+ observer({
286
+ kind: 'room-change',
287
+ locator,
288
+ from: at,
289
+ roomVersion: current.roomVersion,
290
+ read: async () => page,
291
+ });
292
+ }
293
+ }
168
294
  function pageOf(conversation, after) {
169
295
  const messages = conversation.messages ?? [];
170
- const first = typeof after === 'string' ? messages.findIndex((message) => message.position > after) : 0;
296
+ const first = typeof after === 'string' ? messages.findIndex((message) => message.change > after) : 0;
171
297
  const from = first === -1 ? messages.length : first;
172
298
  const page = messages.slice(from, from + (historyPageSize ?? messages.length));
173
299
  const last = page.at(-1);
174
300
  return {
175
301
  conversation: conversation.conversation,
176
302
  messages: page,
177
- ...(last !== undefined && from + page.length < messages.length
178
- ? { next: last.position }
179
- : {}),
303
+ ...(last !== undefined && from + page.length < messages.length ? { next: last.change } : {}),
180
304
  };
181
305
  }
182
- function historiesOf(input) {
183
- const asked = input?.conversations;
184
- if (!Array.isArray(asked))
185
- return { kind: 'rejected', code: 'invalid_request', fields: {}, retryable: false };
306
+ function historiesOf(request) {
186
307
  const histories = [];
187
- for (const entry of asked) {
188
- const conversation = typeof entry.conversation === 'string' ? scripted.get(entry.conversation) : undefined;
308
+ for (const entry of request.conversations) {
309
+ const conversation = scripted.get(entry.conversation);
189
310
  if (conversation === undefined)
190
311
  continue;
191
- const outcome = conversation.outcomes?.['conversation.history'];
312
+ const outcome = conversation.outcomes?.history;
192
313
  if (outcome !== undefined)
193
314
  return outcome;
194
315
  histories.push(pageOf(conversation, entry.after));
@@ -199,15 +320,22 @@ export function createMemoryAdapter(options = {}) {
199
320
  function conversationAction(request) {
200
321
  const name = request.name;
201
322
  const input = (request.input ?? {});
202
- if (name === 'conversation.history')
203
- return historiesOf(request.input);
204
- if (name === 'conversation.create') {
323
+ const operation = typeof input.operation_id === 'string' ? `${name}\u0000${input.operation_id}` : null;
324
+ const first = (answer) => {
325
+ if (operation !== null)
326
+ repeats.set(operation, answer);
327
+ return answer;
328
+ };
329
+ const repeated = operation === null ? undefined : repeats.get(operation);
330
+ if (name === 'conversations/create') {
331
+ if (repeated !== undefined)
332
+ return repeated;
205
333
  issued += 1;
206
334
  const conversation = `conversation-${issued}`;
207
335
  scripted.set(conversation, { conversation });
208
- return { kind: 'action', result: { conversation } };
336
+ return first({ kind: 'action', result: { conversation_id: conversation } });
209
337
  }
210
- const named = typeof input.conversation === 'string' ? input.conversation : '';
338
+ const named = typeof input.conversation_id === 'string' ? input.conversation_id : '';
211
339
  const conversation = scripted.get(named);
212
340
  if (conversation === undefined) {
213
341
  return {
@@ -220,34 +348,41 @@ export function createMemoryAdapter(options = {}) {
220
348
  const outcome = conversation.outcomes?.[name];
221
349
  if (outcome !== undefined)
222
350
  return outcome;
223
- if (name === 'conversation.send') {
224
- const operation = typeof input.operation_id === 'string' ? input.operation_id : null;
225
- const answered = operation === null ? undefined : sentTurns.get(operation);
226
- if (answered !== undefined)
227
- return { kind: 'action', result: { turn: answered } };
351
+ if (name === 'conversations/send') {
352
+ if (repeated !== undefined)
353
+ return repeated;
228
354
  issued += 1;
229
- const turn = `turn-${issued}`;
230
- unsealed.set(turn, named);
231
- if (operation !== null)
232
- sentTurns.set(operation, turn);
233
- return { kind: 'action', result: { turn } };
234
- }
235
- if (name === 'conversation.cancel') {
236
- const turn = typeof input.turn === 'string' ? input.turn : '';
237
- if (sealed.has(turn) || !unsealed.has(turn)) {
238
- return { kind: 'rejected', code: 'turn-sealed', fields: { turn }, retryable: false };
239
- }
240
- unsealed.delete(turn);
241
- return { kind: 'action', result: { turn, cancelled: true } };
355
+ return first({ kind: 'action', result: { message_id: `turn-${issued}` } });
242
356
  }
243
- if (name === 'conversation.delete')
357
+ if (name === 'conversations/delete')
244
358
  scripted.delete(named);
245
- return { kind: 'action', result: { conversation: named } };
359
+ return { kind: 'action', result: { conversation_id: named } };
360
+ }
361
+ function decided(request) {
362
+ const { request: asked, message, decision, } = (request.input ?? {});
363
+ return {
364
+ kind: 'action',
365
+ result: {
366
+ request: asked,
367
+ ...(message === undefined ? {} : { message }),
368
+ state: decision === 'approve' ? 'started' : 'declined',
369
+ },
370
+ };
246
371
  }
247
372
  function announceList() {
248
- const entries = [...scripted.values()].flatMap(({ conversation, listed }) => listed === undefined ? [] : [{ conversation, ...listed }]);
249
- for (const listener of listeners)
250
- listener({ kind: 'list', entries });
373
+ const entries = [...scripted.values()].flatMap(({ conversation, listed, messages = [] }) => listed === undefined
374
+ ? []
375
+ : [{ conversation, ...listed, newest: messages.at(-1)?.change ?? null }]);
376
+ for (const listener of listeners) {
377
+ listener({
378
+ kind: 'list',
379
+ agents: structuredClone(agents),
380
+ entries,
381
+ whole: true,
382
+ more: false,
383
+ cursor: null,
384
+ });
385
+ }
251
386
  }
252
387
  function discovery() {
253
388
  return {
@@ -259,8 +394,42 @@ export function createMemoryAdapter(options = {}) {
259
394
  ...(options.member === undefined ? {} : { member: options.member }),
260
395
  };
261
396
  }
397
+ function discovered() {
398
+ return resolveScript(scripts.discover, discovery, undefined).then((answer) => {
399
+ if (answer.kind === 'discovery')
400
+ for (const room of answer.rooms)
401
+ note(room.locator, room);
402
+ return answer;
403
+ });
404
+ }
405
+ function opened(notify) {
406
+ const opening = ++openings;
407
+ notify({ kind: 'opened' });
408
+ void discovered()
409
+ .catch(() => ({ kind: 'transport-uncertainty' }))
410
+ .then((answer) => {
411
+ if (opening !== openings)
412
+ return;
413
+ if (answer.kind !== 'discovery') {
414
+ openings += 1;
415
+ carried.clear();
416
+ const revoked = answer.kind === 'revoked-authorization';
417
+ notify({ kind: 'closed', revoked, lost: !revoked, authorityChanged: false });
418
+ return;
419
+ }
420
+ declaredRooms.clear();
421
+ for (const room of answer.rooms)
422
+ declaredRooms.add(room.locator);
423
+ notify({ kind: 'discovered', discovery: answer });
424
+ });
425
+ }
426
+ function emitAll(event) {
427
+ for (const observer of observers)
428
+ observer(event);
429
+ }
262
430
  return {
263
- ...(options.exhaustiveDiscovery === undefined ? {} : { exhaustiveDiscovery: true }),
431
+ ...(options.roomTypes === undefined ? {} : { roomTypes: options.roomTypes }),
432
+ ...(options.typedPaths === undefined ? {} : { typedPaths: options.typedPaths }),
264
433
  setRooms(next) {
265
434
  rooms = [...next];
266
435
  retain(rooms);
@@ -268,27 +437,41 @@ export function createMemoryAdapter(options = {}) {
268
437
  setConversations(next) {
269
438
  scriptConversations(next);
270
439
  },
440
+ setAgents(next) {
441
+ agents = structuredClone(next);
442
+ },
271
443
  open() {
444
+ carried.clear();
272
445
  announceList();
273
- for (const observer of observers)
274
- observer({ kind: 'opened' });
446
+ opened(emitAll);
275
447
  },
276
- close(revoked = false) {
277
- for (const observer of observers)
278
- observer({ kind: 'closed', revoked });
448
+ close({ revoked = false, lost = !revoked, authorityChanged = false } = {}) {
449
+ openings += 1;
450
+ carried.clear();
451
+ emitAll({ kind: 'closed', revoked, lost, authorityChanged });
279
452
  },
280
453
  expire(locators) {
281
454
  expiring = [...locators];
282
455
  },
283
- notifyRoomVersion({ locator, roomVersion }) {
284
- for (const observer of observers)
285
- observer({ kind: 'room-version', locator, roomVersion });
456
+ notifyRoomVersion({ locator }) {
457
+ carry(locator);
458
+ },
459
+ notifyAuthorityChanged(locators) {
460
+ const named = new Set(locators ?? [...declaredRooms, ...rooms.map((room) => room.locator)]);
461
+ const listed = rooms.filter((room) => named.has(room.locator));
462
+ const left = [...named].filter((locator) => !listed.some((room) => room.locator === locator));
463
+ for (const room of listed)
464
+ declaredRooms.add(room.locator);
465
+ for (const locator of left)
466
+ declaredRooms.delete(locator);
467
+ emitAll({
468
+ kind: 'authority-changed',
469
+ rooms: listed.map(declared),
470
+ left,
471
+ ...(options.member === undefined ? {} : { member: options.member }),
472
+ });
286
473
  },
287
474
  notifyConversation(event) {
288
- if (event.kind === 'accepted-message') {
289
- sealed.add(event.message.turn);
290
- unsealed.delete(event.message.turn);
291
- }
292
475
  for (const listener of listeners)
293
476
  listener(event);
294
477
  },
@@ -297,21 +480,28 @@ export function createMemoryAdapter(options = {}) {
297
480
  observer(mode);
298
481
  },
299
482
  heldRooms() {
300
- return held;
483
+ return [...carried.keys()].sort();
301
484
  },
302
485
  replicatedConversations() {
303
486
  return replicated;
304
487
  },
488
+ heldConversationCursor() {
489
+ return heldCursor;
490
+ },
305
491
  conversations: {
306
492
  replicate(next) {
307
493
  replicated = [...next];
308
494
  },
495
+ hold(cursor) {
496
+ heldCursor = cursor;
497
+ },
309
498
  observe(notify) {
310
499
  listeners.add(notify);
311
500
  return () => {
312
501
  listeners.delete(notify);
313
502
  };
314
503
  },
504
+ history: (request) => Promise.resolve(historiesOf(request)),
315
505
  },
316
506
  serviceMode: {
317
507
  observe(notify) {
@@ -323,43 +513,46 @@ export function createMemoryAdapter(options = {}) {
323
513
  },
324
514
  roomStream: {
325
515
  hold(asked) {
326
- held = asked.map((room) => room.locator);
327
- const expired = expiring.filter((locator) => held.includes(locator));
328
- expiring = [];
329
- const moved = asked.flatMap((room) => {
516
+ const named = asked.map((room) => room.locator);
517
+ const unknown = asked.flatMap((room) => {
330
518
  const current = rooms.find((entry) => entry.locator === room.locator);
519
+ const version = room.roomVersion;
331
520
  return current === undefined ||
332
- expired.includes(room.locator) ||
333
- current.roomVersion === room.roomVersion
521
+ (version !== null &&
522
+ (current.roomVersion === version || served.get(room.locator)?.has(version) === true))
334
523
  ? []
335
- : [{ locator: room.locator, roomVersion: current.roomVersion }];
524
+ : [room.locator];
336
525
  });
526
+ const expired = [
527
+ ...new Set([...expiring.filter((locator) => named.includes(locator)), ...unknown]),
528
+ ];
529
+ expiring = expiring.filter((locator) => !named.includes(locator));
337
530
  queueMicrotask(() => {
338
- for (const observer of observers) {
339
- for (const room of moved)
340
- observer({ kind: 'room-version', ...room });
341
- observer({ kind: 'opening', expired });
531
+ for (const room of asked) {
532
+ if (expired.includes(room.locator) || room.roomVersion === null)
533
+ carried.delete(room.locator);
534
+ else {
535
+ carried.set(room.locator, room.roomVersion);
536
+ carry(room.locator);
537
+ }
342
538
  }
539
+ emitAll({ kind: 'caught-up', expired });
343
540
  });
344
541
  },
345
- reopen() {
346
- queueMicrotask(() => {
347
- for (const observer of observers)
348
- observer({ kind: 'closed', revoked: false });
349
- });
350
- },
351
- published() { },
352
542
  observe(notify) {
353
543
  observers.add(notify);
354
544
  queueMicrotask(() => {
355
545
  if (!observers.has(notify))
356
546
  return;
357
547
  if (options.unopened === true) {
358
- notify({ kind: 'closed', revoked: false });
548
+ notify({ kind: 'closed', revoked: false, lost: true, authorityChanged: false });
359
549
  return;
360
550
  }
361
551
  announceList();
362
- notify({ kind: 'opened' });
552
+ opened((event) => {
553
+ if (observers.has(notify))
554
+ notify(event);
555
+ });
363
556
  });
364
557
  return () => {
365
558
  observers.delete(notify);
@@ -376,12 +569,7 @@ export function createMemoryAdapter(options = {}) {
376
569
  return resolveScript(scripts.authorize, () => ({}), undefined);
377
570
  },
378
571
  async discover(_authorization) {
379
- return resolveScript(scripts.discover, discovery, undefined).then((answer) => {
380
- if (answer.kind === 'discovery')
381
- for (const room of answer.rooms)
382
- note(room.locator, room);
383
- return answer;
384
- });
572
+ return discovered();
385
573
  },
386
574
  reached,
387
575
  follows(locator, roomVersion, next) {
@@ -438,7 +626,13 @@ export function createMemoryAdapter(options = {}) {
438
626
  retryable: false,
439
627
  affected: {},
440
628
  };
441
- }, request).then((answer) => note(request.locator, answer));
629
+ }, request).then((answer) => {
630
+ const destination = request.changeBatch.between?.destination.room;
631
+ if (answer.kind === 'accepted' && answer.destination !== undefined && destination) {
632
+ note(destination, answer.destination);
633
+ }
634
+ return note(request.locator, answer);
635
+ });
442
636
  },
443
637
  async history(_authorization, request) {
444
638
  return resolveScript(scripts.history, () => {
@@ -454,32 +648,48 @@ export function createMemoryAdapter(options = {}) {
454
648
  },
455
649
  async binary(_authorization, request) {
456
650
  return resolveScript(scripts.binary, () => {
457
- if (request.kind === 'reference') {
458
- return { kind: 'rejected', code: 'unscripted-binary', fields: {}, retryable: false };
459
- }
460
651
  const room = rooms.find((entry) => entry.locator === request.locator);
461
652
  if (room === undefined)
462
653
  return { kind: 'room-not-readable', locator: request.locator };
463
- const blob = room.blobs?.[request.path];
654
+ const blob = heldBlob(room, request.path);
464
655
  return blob === undefined
465
- ? { kind: 'rejected', code: 'file_not_found', fields: {}, retryable: false }
466
- : { kind: 'binary', blob };
656
+ ? { kind: 'rejected', code: 'not_found', fields: {}, retryable: false }
657
+ : { kind: 'binary', body: blob.stream(), mediaType: declaredType(blob) };
467
658
  }, request);
468
659
  },
469
- referrerScope(referrer) {
470
- if (typeof referrer !== 'object' || referrer === null)
471
- return null;
472
- const { room, path, message } = referrer;
473
- if (typeof message === 'string')
474
- return { kind: 'conversations' };
475
- if (typeof room !== 'string')
476
- return null;
477
- return { kind: 'room', locator: room, ...(typeof path === 'string' ? { path } : {}) };
660
+ async link(_authorization, request) {
661
+ return resolveScript(scripts.link, () => {
662
+ if (request.kind === 'key') {
663
+ const key = keyedLink(request.link)?.key;
664
+ const named = key === undefined ? undefined : keyed.get(key);
665
+ if (named === undefined)
666
+ return UNFOUND;
667
+ return linkTo(rooms.find((entry) => entry.locator === named.locator), named.path);
668
+ }
669
+ const room = rooms.find((entry) => entry.locator === request.locator);
670
+ if (room === undefined)
671
+ return { kind: 'room-not-readable', locator: request.locator };
672
+ return linkTo(room, request.path);
673
+ }, request);
674
+ },
675
+ async attach(_authorization, request) {
676
+ return resolveScript(scripts.attach, () => {
677
+ if (request.locator === undefined)
678
+ return attachedKeyed(request);
679
+ const room = rooms.find((entry) => entry.locator === request.locator);
680
+ if (room === undefined)
681
+ return { kind: 'room-not-readable', locator: request.locator };
682
+ const path = freePath(room, request.name);
683
+ attached.set(`${room.locator}\u0000${path}`, request.blob);
684
+ return { kind: 'attached', path, revision: `attached-${attached.size}` };
685
+ }, request);
478
686
  },
479
687
  async action(_authorization, request) {
480
688
  return resolveScript(scripts.action, () => {
481
- if (request.name.startsWith('conversation.'))
689
+ if (request.name.startsWith('conversations/'))
482
690
  return conversationAction(request);
691
+ if (request.name === 'agent-functions/decide')
692
+ return decided(request);
483
693
  return { kind: 'rejected', code: 'unscripted-action', fields: {}, retryable: false };
484
694
  }, request).then((answer) => {
485
695
  if (answer.kind === 'action' && answer.at !== undefined)