@polycode-projects/the-mechanical-code-talker 3.3.0 → 4.0.0
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/corpus/sprites/src/sprite-facts.jsonl +18 -0
- package/corpus/worlds/manifest.json +5 -5
- package/corpus/worlds/shards/mud-garden.jsonl.gz +0 -0
- package/corpus/worlds/src/mud-garden.jsonl +34 -1
- package/package.json +1 -1
- package/src/adapters/p2p/webrtc-transport.mjs +146 -0
- package/src/domain/game-config.mjs +26 -5
- package/src/domain/grammar/lexicon.mjs +13 -0
- package/src/domain/memory/trust.mjs +6 -4
- package/src/domain/p2p/facts.mjs +81 -0
- package/src/domain/p2p/peer-id.mjs +32 -0
- package/src/domain/p2p/provenance-relabel.mjs +26 -0
- package/src/domain/p2p/sync-filter.mjs +31 -0
- package/src/domain/p2p/wire.mjs +123 -0
- package/src/domain/sprite-map.mjs +10 -2
- package/src/services/adventure-editor.mjs +10 -2
- package/src/services/adventure-viz.mjs +192 -39
- package/src/services/adventure.mjs +673 -42
- package/src/services/chat-page-viz.mjs +1060 -7
- package/src/services/mud-editor.mjs +313 -0
- package/src/services/mud-turn.mjs +264 -87
- package/src/services/mud-viz.mjs +1690 -387
- package/src/services/p2p-room.mjs +559 -0
- package/src/surfaces/web/memory-ask-browser.bundle.js +56 -56
- package/src/surfaces/web/mud-browser-entry.mjs +184 -18
- package/src/surfaces/web/p2p-browser-entry.mjs +39 -0
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
{"subject":"autumn sprite","predicate":"rdf:type","object":"sprite class"}
|
|
10
10
|
{"subject":"baby sprite","predicate":"rdf:type","object":"sprite class"}
|
|
11
11
|
{"subject":"badger sprite","predicate":"rdf:type","object":"sprite class"}
|
|
12
|
+
{"subject":"basket sprite","predicate":"rdf:type","object":"sprite class"}
|
|
12
13
|
{"subject":"bathroom sprite","predicate":"rdf:type","object":"sprite class"}
|
|
13
14
|
{"subject":"beach sprite","predicate":"rdf:type","object":"sprite class"}
|
|
14
15
|
{"subject":"bear sprite","predicate":"rdf:type","object":"sprite class"}
|
|
@@ -31,6 +32,7 @@
|
|
|
31
32
|
{"subject":"butterfly sprite","predicate":"rdf:type","object":"sprite class"}
|
|
32
33
|
{"subject":"cabinet sprite","predicate":"rdf:type","object":"sprite class"}
|
|
33
34
|
{"subject":"car sprite","predicate":"rdf:type","object":"sprite class"}
|
|
35
|
+
{"subject":"carrot sprite","predicate":"rdf:type","object":"sprite class"}
|
|
34
36
|
{"subject":"castle sprite","predicate":"rdf:type","object":"sprite class"}
|
|
35
37
|
{"subject":"cat sprite","predicate":"rdf:type","object":"sprite class"}
|
|
36
38
|
{"subject":"cellar sprite","predicate":"rdf:type","object":"sprite class"}
|
|
@@ -75,8 +77,10 @@
|
|
|
75
77
|
{"subject":"fly sprite","predicate":"rdf:type","object":"sprite class"}
|
|
76
78
|
{"subject":"food sprite","predicate":"rdf:type","object":"sprite class"}
|
|
77
79
|
{"subject":"forest sprite","predicate":"rdf:type","object":"sprite class"}
|
|
80
|
+
{"subject":"fox sprite","predicate":"rdf:type","object":"sprite class"}
|
|
78
81
|
{"subject":"friend sprite","predicate":"rdf:type","object":"sprite class"}
|
|
79
82
|
{"subject":"frog sprite","predicate":"rdf:type","object":"sprite class"}
|
|
83
|
+
{"subject":"fruit sprite","predicate":"rdf:type","object":"sprite class"}
|
|
80
84
|
{"subject":"furniture sprite","predicate":"rdf:type","object":"sprite class"}
|
|
81
85
|
{"subject":"garden sprite","predicate":"rdf:type","object":"sprite class"}
|
|
82
86
|
{"subject":"gardener sprite","predicate":"rdf:type","object":"sprite class"}
|
|
@@ -112,6 +116,7 @@
|
|
|
112
116
|
{"subject":"lawyer sprite","predicate":"rdf:type","object":"sprite class"}
|
|
113
117
|
{"subject":"leader sprite","predicate":"rdf:type","object":"sprite class"}
|
|
114
118
|
{"subject":"letter sprite","predicate":"rdf:type","object":"sprite class"}
|
|
119
|
+
{"subject":"lettuce sprite","predicate":"rdf:type","object":"sprite class"}
|
|
115
120
|
{"subject":"library sprite","predicate":"rdf:type","object":"sprite class"}
|
|
116
121
|
{"subject":"lion sprite","predicate":"rdf:type","object":"sprite class"}
|
|
117
122
|
{"subject":"love sprite","predicate":"rdf:type","object":"sprite class"}
|
|
@@ -155,8 +160,10 @@
|
|
|
155
160
|
{"subject":"river sprite","predicate":"rdf:type","object":"sprite class"}
|
|
156
161
|
{"subject":"road sprite","predicate":"rdf:type","object":"sprite class"}
|
|
157
162
|
{"subject":"room sprite","predicate":"rdf:type","object":"sprite class"}
|
|
163
|
+
{"subject":"root sprite","predicate":"rdf:type","object":"sprite class"}
|
|
158
164
|
{"subject":"salt sprite","predicate":"rdf:type","object":"sprite class"}
|
|
159
165
|
{"subject":"school sprite","predicate":"rdf:type","object":"sprite class"}
|
|
166
|
+
{"subject":"seed sprite","predicate":"rdf:type","object":"sprite class"}
|
|
160
167
|
{"subject":"servant sprite","predicate":"rdf:type","object":"sprite class"}
|
|
161
168
|
{"subject":"sheep sprite","predicate":"rdf:type","object":"sprite class"}
|
|
162
169
|
{"subject":"shirt sprite","predicate":"rdf:type","object":"sprite class"}
|
|
@@ -186,6 +193,7 @@
|
|
|
186
193
|
{"subject":"teacher sprite","predicate":"rdf:type","object":"sprite class"}
|
|
187
194
|
{"subject":"team sprite","predicate":"rdf:type","object":"sprite class"}
|
|
188
195
|
{"subject":"tiger sprite","predicate":"rdf:type","object":"sprite class"}
|
|
196
|
+
{"subject":"tomato sprite","predicate":"rdf:type","object":"sprite class"}
|
|
189
197
|
{"subject":"town sprite","predicate":"rdf:type","object":"sprite class"}
|
|
190
198
|
{"subject":"train sprite","predicate":"rdf:type","object":"sprite class"}
|
|
191
199
|
{"subject":"tree sprite","predicate":"rdf:type","object":"sprite class"}
|
|
@@ -205,6 +213,7 @@
|
|
|
205
213
|
{"subject":"wolf sprite","predicate":"rdf:type","object":"sprite class"}
|
|
206
214
|
{"subject":"woman sprite","predicate":"rdf:type","object":"sprite class"}
|
|
207
215
|
{"subject":"worker sprite","predicate":"rdf:type","object":"sprite class"}
|
|
216
|
+
{"subject":"worm sprite","predicate":"rdf:type","object":"sprite class"}
|
|
208
217
|
{"subject":"writer sprite","predicate":"rdf:type","object":"sprite class"}
|
|
209
218
|
{"subject":"adventurer sprite","predicate":"mgx:render-at","object":"icon tier"}
|
|
210
219
|
{"subject":"animal sprite","predicate":"mgx:render-at","object":"icon tier"}
|
|
@@ -247,6 +256,7 @@
|
|
|
247
256
|
{"subject":"autumn sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
248
257
|
{"subject":"baby sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
249
258
|
{"subject":"badger sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
259
|
+
{"subject":"basket sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
250
260
|
{"subject":"bathroom sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
251
261
|
{"subject":"beach sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
252
262
|
{"subject":"bear sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
@@ -269,6 +279,7 @@
|
|
|
269
279
|
{"subject":"butterfly sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
270
280
|
{"subject":"cabinet sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
271
281
|
{"subject":"car sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
282
|
+
{"subject":"carrot sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
272
283
|
{"subject":"castle sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
273
284
|
{"subject":"cat sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
274
285
|
{"subject":"cellar sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
@@ -313,8 +324,10 @@
|
|
|
313
324
|
{"subject":"fly sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
314
325
|
{"subject":"food sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
315
326
|
{"subject":"forest sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
327
|
+
{"subject":"fox sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
316
328
|
{"subject":"friend sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
317
329
|
{"subject":"frog sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
330
|
+
{"subject":"fruit sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
318
331
|
{"subject":"furniture sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
319
332
|
{"subject":"garden sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
320
333
|
{"subject":"gardener sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
@@ -350,6 +363,7 @@
|
|
|
350
363
|
{"subject":"lawyer sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
351
364
|
{"subject":"leader sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
352
365
|
{"subject":"letter sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
366
|
+
{"subject":"lettuce sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
353
367
|
{"subject":"library sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
354
368
|
{"subject":"lion sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
355
369
|
{"subject":"love sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
@@ -393,8 +407,10 @@
|
|
|
393
407
|
{"subject":"river sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
394
408
|
{"subject":"road sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
395
409
|
{"subject":"room sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
410
|
+
{"subject":"root sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
396
411
|
{"subject":"salt sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
397
412
|
{"subject":"school sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
413
|
+
{"subject":"seed sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
398
414
|
{"subject":"servant sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
399
415
|
{"subject":"sheep sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
400
416
|
{"subject":"shirt sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
@@ -424,6 +440,7 @@
|
|
|
424
440
|
{"subject":"teacher sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
425
441
|
{"subject":"team sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
426
442
|
{"subject":"tiger sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
443
|
+
{"subject":"tomato sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
427
444
|
{"subject":"town sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
428
445
|
{"subject":"train sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
429
446
|
{"subject":"tree sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
@@ -443,6 +460,7 @@
|
|
|
443
460
|
{"subject":"wolf sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
444
461
|
{"subject":"woman sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
445
462
|
{"subject":"worker sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
463
|
+
{"subject":"worm sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
446
464
|
{"subject":"writer sprite","predicate":"mgx:render-at","object":"sprite tier"}
|
|
447
465
|
{"subject":"dog sprite","predicate":"mgx:take-parameter","object":"colour"}
|
|
448
466
|
{"subject":"dog sprite","predicate":"mgx:accept-colour","object":"black"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"generated": "by scripts/build-worlds-pack.mjs from corpus/worlds/src/",
|
|
4
4
|
"counts": {
|
|
5
5
|
"worlds": 3,
|
|
6
|
-
"facts":
|
|
6
|
+
"facts": 654,
|
|
7
7
|
"rules": 40
|
|
8
8
|
},
|
|
9
9
|
"budgets": {
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
"file": "src/mud-garden.jsonl",
|
|
22
|
-
"bytes":
|
|
23
|
-
"sha256": "
|
|
22
|
+
"bytes": 11802,
|
|
23
|
+
"sha256": "7aac1b1b3460126b6aeed2f83d71f566636cf18935e81396a6e9e7f126750418"
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
26
|
"file": "src/spider-fly.jsonl",
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
43
|
"file": "shards/mud-garden.jsonl.gz",
|
|
44
|
-
"bytes":
|
|
45
|
-
"sha256": "
|
|
44
|
+
"bytes": 1243,
|
|
45
|
+
"sha256": "8f99eca74bd41b7f893bfca7f49cd4df8ebc53b6f61e3d9bc82ea1350cd23e83"
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
48
|
"file": "shards/spider-fly.jsonl.gz",
|
|
Binary file
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
{"world":"mud-garden","kind":"meta","opening":"a vegetable garden on a wet morning, with a burrow already running under it. Say \"look\" to look around
|
|
1
|
+
{"world":"mud-garden","kind":"meta","opening":"a vegetable garden on a wet morning, with a burrow already running under it. Say \"look\" to look around and \"go down\" to follow the burrow. Above ground the only dig is straight down; once you are under the soil you can dig north, south, east or west along the burrow, or up to break the surface. A fox keeps a den off the burrow, and it does not share."}
|
|
2
2
|
{"world":"mud-garden","kind":"fact","subject":"garden","predicate":"rdf:type","object":"room"}
|
|
3
|
+
{"world":"mud-garden","kind":"fact","subject":"garden","predicate":"mgx:is-origin","object":"true"}
|
|
3
4
|
{"world":"mud-garden","kind":"fact","subject":"burrow-1","predicate":"rdf:type","object":"room"}
|
|
4
5
|
{"world":"mud-garden","kind":"fact","subject":"sett-1","predicate":"rdf:type","object":"room"}
|
|
6
|
+
{"world":"mud-garden","kind":"fact","subject":"fox-den","predicate":"rdf:type","object":"room"}
|
|
5
7
|
{"world":"mud-garden","kind":"fact","subject":"garden","predicate":"mgx:has-exit-down","object":"burrow-1"}
|
|
6
8
|
{"world":"mud-garden","kind":"fact","subject":"burrow-1","predicate":"mgx:has-exit-up","object":"garden"}
|
|
7
9
|
{"world":"mud-garden","kind":"fact","subject":"burrow-1","predicate":"mgx:has-exit-east","object":"sett-1"}
|
|
8
10
|
{"world":"mud-garden","kind":"fact","subject":"sett-1","predicate":"mgx:has-exit-west","object":"burrow-1"}
|
|
11
|
+
{"world":"mud-garden","kind":"fact","subject":"burrow-1","predicate":"mgx:has-exit-north","object":"fox-den"}
|
|
12
|
+
{"world":"mud-garden","kind":"fact","subject":"fox-den","predicate":"mgx:has-exit-south","object":"burrow-1"}
|
|
9
13
|
{"world":"mud-garden","kind":"fact","subject":"mole-1","predicate":"rdf:type","object":"adventurer"}
|
|
10
14
|
{"world":"mud-garden","kind":"fact","subject":"vole-1","predicate":"rdf:type","object":"adventurer"}
|
|
11
15
|
{"world":"mud-garden","kind":"fact","subject":"badger-2","predicate":"rdf:type","object":"adventurer"}
|
|
@@ -18,6 +22,12 @@
|
|
|
18
22
|
{"world":"mud-garden","kind":"fact","subject":"vole-1","predicate":"mgx:hasMass","object":"6"}
|
|
19
23
|
{"world":"mud-garden","kind":"fact","subject":"badger-2","predicate":"mgx:hasMass","object":"9"}
|
|
20
24
|
{"world":"mud-garden","kind":"fact","subject":"groundhog-1","predicate":"mgx:hasMass","object":"7"}
|
|
25
|
+
{"world":"mud-garden","kind":"fact","subject":"predator","predicate":"rdfs:subClassOf","object":"animal"}
|
|
26
|
+
{"world":"mud-garden","kind":"fact","subject":"fox","predicate":"rdfs:subClassOf","object":"predator"}
|
|
27
|
+
{"world":"mud-garden","kind":"fact","subject":"fox-1","predicate":"rdf:type","object":"fox"}
|
|
28
|
+
{"world":"mud-garden","kind":"fact","subject":"fox-1","predicate":"mgx:currently-in","object":"fox-den"}
|
|
29
|
+
{"world":"mud-garden","kind":"fact","subject":"fox-1","predicate":"mgx:is-predator","object":"true"}
|
|
30
|
+
{"world":"mud-garden","kind":"fact","subject":"fox-1","predicate":"mgx:hasMass","object":"40"}
|
|
21
31
|
{"world":"mud-garden","kind":"fact","subject":"food","predicate":"rdfs:subClassOf","object":"portable"}
|
|
22
32
|
{"world":"mud-garden","kind":"fact","subject":"carrot","predicate":"rdfs:subClassOf","object":"food"}
|
|
23
33
|
{"world":"mud-garden","kind":"fact","subject":"lettuce","predicate":"rdfs:subClassOf","object":"food"}
|
|
@@ -31,6 +41,28 @@
|
|
|
31
41
|
{"world":"mud-garden","kind":"fact","subject":"tomato","predicate":"rdf:type","object":"tomato"}
|
|
32
42
|
{"world":"mud-garden","kind":"fact","subject":"tomato","predicate":"mgx:located-in","object":"sett-1"}
|
|
33
43
|
{"world":"mud-garden","kind":"fact","subject":"tomato","predicate":"mgx:hasMass","object":"2"}
|
|
44
|
+
{"world":"mud-garden","kind":"fact","subject":"root","predicate":"rdfs:subClassOf","object":"food"}
|
|
45
|
+
{"world":"mud-garden","kind":"fact","subject":"root","predicate":"mgx:hasMass","object":"2"}
|
|
46
|
+
{"world":"mud-garden","kind":"fact","subject":"worm","predicate":"rdfs:subClassOf","object":"food"}
|
|
47
|
+
{"world":"mud-garden","kind":"fact","subject":"worm","predicate":"mgx:hasMass","object":"2"}
|
|
48
|
+
{"world":"mud-garden","kind":"fact","subject":"mouse","predicate":"rdfs:subClassOf","object":"animal"}
|
|
49
|
+
{"world":"mud-garden","kind":"fact","subject":"mouse","predicate":"mgx:hasMass","object":"4"}
|
|
50
|
+
{"world":"mud-garden","kind":"fact","subject":"den","predicate":"rdfs:subClassOf","object":"underground-space"}
|
|
51
|
+
{"world":"mud-garden","kind":"fact","subject":"underground-space","predicate":"mgx:dig-spawns","object":"root"}
|
|
52
|
+
{"world":"mud-garden","kind":"fact","subject":"underground-space","predicate":"mgx:dig-spawns","object":"carrot"}
|
|
53
|
+
{"world":"mud-garden","kind":"fact","subject":"underground-space","predicate":"mgx:dig-spawns","object":"worm"}
|
|
54
|
+
{"world":"mud-garden","kind":"fact","subject":"underground-space","predicate":"mgx:den-spawns","object":"carrot"}
|
|
55
|
+
{"world":"mud-garden","kind":"fact","subject":"underground-space","predicate":"mgx:den-spawns","object":"root"}
|
|
56
|
+
{"world":"mud-garden","kind":"fact","subject":"underground-space","predicate":"mgx:den-spawns","object":"worm"}
|
|
57
|
+
{"world":"mud-garden","kind":"fact","subject":"underground-space","predicate":"mgx:den-resident","object":"mouse"}
|
|
58
|
+
{"world":"mud-garden","kind":"fact","subject":"underground-space","predicate":"mgx:dig-spawn-max","object":"2"}
|
|
59
|
+
{"world":"mud-garden","kind":"fact","subject":"underground-space","predicate":"mgx:den-chance-in","object":"5"}
|
|
60
|
+
{"world":"mud-garden","kind":"fact","subject":"underground-space","predicate":"mgx:den-resident-chance-in","object":"3"}
|
|
61
|
+
{"world":"mud-garden","kind":"fact","subject":"garden","predicate":"mgx:dig-reach","object":"6"}
|
|
62
|
+
{"world":"mud-garden","kind":"fact","subject":"mole-1","predicate":"mgx:mass-drain-per-turn","object":"0.06"}
|
|
63
|
+
{"world":"mud-garden","kind":"fact","subject":"vole-1","predicate":"mgx:mass-drain-per-turn","object":"0.05"}
|
|
64
|
+
{"world":"mud-garden","kind":"fact","subject":"badger-2","predicate":"mgx:mass-drain-per-turn","object":"0.08"}
|
|
65
|
+
{"world":"mud-garden","kind":"fact","subject":"groundhog-1","predicate":"mgx:mass-drain-per-turn","object":"0.06"}
|
|
34
66
|
{"world":"mud-garden","kind":"fact","subject":"stone","predicate":"rdf:type","object":"portable"}
|
|
35
67
|
{"world":"mud-garden","kind":"fact","subject":"stone","predicate":"mgx:located-in","object":"garden"}
|
|
36
68
|
{"world":"mud-garden","kind":"fact","subject":"seed","predicate":"rdf:type","object":"portable"}
|
|
@@ -43,6 +75,7 @@
|
|
|
43
75
|
{"world":"mud-garden","kind":"fact","subject":"garden","predicate":"rdf:type","object":"outdoor-space"}
|
|
44
76
|
{"world":"mud-garden","kind":"fact","subject":"burrow-1","predicate":"rdf:type","object":"underground-space"}
|
|
45
77
|
{"world":"mud-garden","kind":"fact","subject":"sett-1","predicate":"rdf:type","object":"underground-space"}
|
|
78
|
+
{"world":"mud-garden","kind":"fact","subject":"fox-den","predicate":"rdf:type","object":"underground-space"}
|
|
46
79
|
{"world":"mud-garden","kind":"fact","subject":"outdoor-space","predicate":"rdfs:subClassOf","object":"space"}
|
|
47
80
|
{"world":"mud-garden","kind":"fact","subject":"underground-space","predicate":"rdfs:subClassOf","object":"space"}
|
|
48
81
|
{"world":"mud-garden","kind":"rule","name":"go","ruleKind":"action-signature","slots":{"subjectClass":"adventurer","targetClass":"room"}}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polycode-projects/the-mechanical-code-talker",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "The Mechanical Code Talker (tmct) — a tolerant, offline, $0 chat surface that guides you toward precision queries about a software repository. ELIZA/PARRY-style but domain-obsessed with code. No model calls; indexes a repo on request (tmct index) or reads any producer's graph.",
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// One WebRTC DataChannel between two browsers, and nothing else. The caller
|
|
2
|
+
// carries the offer and answer SDP strings between the two machines however it
|
|
3
|
+
// likes — a link, a paste, an already-open channel to a third peer — and this
|
|
4
|
+
// module never learns what travels over the channel once it is open: it takes
|
|
5
|
+
// plain JS values in and hands plain JS values out.
|
|
6
|
+
//
|
|
7
|
+
// `connectionState` is a live GETTER PROPERTY, not a method: read
|
|
8
|
+
// `transport.connectionState`, never `transport.connectionState()`. It reports
|
|
9
|
+
// RTCPeerConnection's own state ("new" | "connecting" | "connected" | "failed"
|
|
10
|
+
// | "closed"), so a caller can poll it without registering a handler.
|
|
11
|
+
//
|
|
12
|
+
// `iceServers` defaults to [] and this design keeps it empty: no STUN, no
|
|
13
|
+
// TURN, no third party in the loop at all. Peers that cannot already reach
|
|
14
|
+
// each other directly (same machine, same LAN, or a NAT that allows it) never
|
|
15
|
+
// finish the handshake, which is the stated boundary of staying serverless.
|
|
16
|
+
//
|
|
17
|
+
// Both `createOffer` and `createAnswerFor` resolve only once ICE gathering has
|
|
18
|
+
// completed, so the SDP string they return already carries every candidate.
|
|
19
|
+
// Nothing here trickles, because a pasted blob is a one-shot message rather
|
|
20
|
+
// than a live connection back to the other side.
|
|
21
|
+
//
|
|
22
|
+
// Late handler registration still fires: `onOpen` on an already-open channel
|
|
23
|
+
// and `onClose` on an already-closed one call back immediately, so a caller
|
|
24
|
+
// that registers after the transition never silently misses it. Each fires at
|
|
25
|
+
// most once.
|
|
26
|
+
|
|
27
|
+
const CHANNEL_LABEL = "tmct";
|
|
28
|
+
|
|
29
|
+
export function createTransport({ iceServers = [] } = {}) {
|
|
30
|
+
const PeerConnection = globalThis.RTCPeerConnection;
|
|
31
|
+
if (typeof PeerConnection !== "function") {
|
|
32
|
+
throw new Error("no RTCPeerConnection here: this transport runs in a browser, not in bare node");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const connection = new PeerConnection({ iceServers });
|
|
36
|
+
const messageHandlers = [];
|
|
37
|
+
const openHandlers = [];
|
|
38
|
+
const closeHandlers = [];
|
|
39
|
+
|
|
40
|
+
let channel = null;
|
|
41
|
+
let openAnnounced = false;
|
|
42
|
+
let closeAnnounced = false;
|
|
43
|
+
let tornDown = false;
|
|
44
|
+
|
|
45
|
+
function announceOpen() {
|
|
46
|
+
if (openAnnounced) return;
|
|
47
|
+
openAnnounced = true;
|
|
48
|
+
for (const handler of openHandlers) handler();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function announceClose() {
|
|
52
|
+
if (closeAnnounced) return;
|
|
53
|
+
closeAnnounced = true;
|
|
54
|
+
for (const handler of closeHandlers) handler();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function receive(event) {
|
|
58
|
+
let value;
|
|
59
|
+
try {
|
|
60
|
+
value = JSON.parse(event.data);
|
|
61
|
+
} catch {
|
|
62
|
+
// A peer sending something that isn't JSON must not be able to throw
|
|
63
|
+
// inside our receive path, so the frame is dropped rather than raised.
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
for (const handler of messageHandlers) handler(value);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function attachChannel(dataChannel) {
|
|
70
|
+
channel = dataChannel;
|
|
71
|
+
dataChannel.addEventListener("open", announceOpen);
|
|
72
|
+
dataChannel.addEventListener("close", announceClose);
|
|
73
|
+
dataChannel.addEventListener("message", receive);
|
|
74
|
+
if (dataChannel.readyState === "open") announceOpen();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
connection.addEventListener("datachannel", (event) => attachChannel(event.channel));
|
|
78
|
+
connection.addEventListener("connectionstatechange", () => {
|
|
79
|
+
const state = connection.connectionState;
|
|
80
|
+
if (state === "failed" || state === "closed") announceClose();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
async function whenIceGatheringCompletes() {
|
|
84
|
+
if (connection.iceGatheringState === "complete") return;
|
|
85
|
+
await new Promise((resolve) => {
|
|
86
|
+
const settle = () => {
|
|
87
|
+
if (connection.iceGatheringState !== "complete") return;
|
|
88
|
+
connection.removeEventListener("icegatheringstatechange", settle);
|
|
89
|
+
resolve();
|
|
90
|
+
};
|
|
91
|
+
connection.addEventListener("icegatheringstatechange", settle);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
async createOffer() {
|
|
97
|
+
attachChannel(connection.createDataChannel(CHANNEL_LABEL));
|
|
98
|
+
await connection.setLocalDescription(await connection.createOffer());
|
|
99
|
+
await whenIceGatheringCompletes();
|
|
100
|
+
return connection.localDescription.sdp;
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
async createAnswerFor(offerSdp) {
|
|
104
|
+
await connection.setRemoteDescription({ type: "offer", sdp: offerSdp });
|
|
105
|
+
await connection.setLocalDescription(await connection.createAnswer());
|
|
106
|
+
await whenIceGatheringCompletes();
|
|
107
|
+
return connection.localDescription.sdp;
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
async completeWithAnswer(answerSdp) {
|
|
111
|
+
await connection.setRemoteDescription({ type: "answer", sdp: answerSdp });
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
send(data) {
|
|
115
|
+
const state = channel ? channel.readyState : "missing";
|
|
116
|
+
if (state !== "open") throw new Error(`cannot send over a data channel that is ${state}`);
|
|
117
|
+
channel.send(JSON.stringify(data));
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
onMessage(handler) {
|
|
121
|
+
messageHandlers.push(handler);
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
onOpen(handler) {
|
|
125
|
+
openHandlers.push(handler);
|
|
126
|
+
if (openAnnounced) handler();
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
onClose(handler) {
|
|
130
|
+
closeHandlers.push(handler);
|
|
131
|
+
if (closeAnnounced) handler();
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
close() {
|
|
135
|
+
if (tornDown) return;
|
|
136
|
+
tornDown = true;
|
|
137
|
+
channel?.close();
|
|
138
|
+
connection.close();
|
|
139
|
+
announceClose();
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
get connectionState() {
|
|
143
|
+
return connection.connectionState;
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}
|
|
@@ -27,25 +27,32 @@ export const DEFAULT_GAME_CONFIG = Object.freeze({
|
|
|
27
27
|
minHatchlingMass: 3,
|
|
28
28
|
webDurationTurns: 10,
|
|
29
29
|
}),
|
|
30
|
+
// The drains are what make a mud character mortal: every scripted turn
|
|
31
|
+
// charges one, and a character whose mass reaches zero starves and takes no
|
|
32
|
+
// more turns. They are sized against the demo page's own default run (400
|
|
33
|
+
// shared turns, so roughly 200 each for two animals) — an animal that never
|
|
34
|
+
// eats dies about two thirds of the way through, and one that forages does
|
|
35
|
+
// not. A drain that emptied a starting mass in twenty turns would end every
|
|
36
|
+
// run before it had shown anything.
|
|
30
37
|
mud: Object.freeze({
|
|
31
38
|
moleInitialMass: 8,
|
|
32
|
-
moleMassDecrementPerTurn: 0.
|
|
39
|
+
moleMassDecrementPerTurn: 0.06,
|
|
33
40
|
moleSpeed: 1,
|
|
34
41
|
moleDigReach: 1,
|
|
35
42
|
voleInitialMass: 6,
|
|
36
|
-
voleMassDecrementPerTurn: 0.
|
|
43
|
+
voleMassDecrementPerTurn: 0.05,
|
|
37
44
|
voleSpeed: 1,
|
|
38
45
|
voleDigReach: 1,
|
|
39
46
|
badgerInitialMass: 20,
|
|
40
|
-
badgerMassDecrementPerTurn: 0.
|
|
47
|
+
badgerMassDecrementPerTurn: 0.08,
|
|
41
48
|
badgerSpeed: 2,
|
|
42
49
|
badgerDigReach: 2,
|
|
43
50
|
groundhogInitialMass: 12,
|
|
44
|
-
groundhogMassDecrementPerTurn: 0.
|
|
51
|
+
groundhogMassDecrementPerTurn: 0.06,
|
|
45
52
|
groundhogSpeed: 2,
|
|
46
53
|
groundhogDigReach: 2,
|
|
47
54
|
meerkatInitialMass: 9,
|
|
48
|
-
meerkatMassDecrementPerTurn: 0.
|
|
55
|
+
meerkatMassDecrementPerTurn: 0.05,
|
|
49
56
|
meerkatSpeed: 1,
|
|
50
57
|
meerkatDigReach: 1,
|
|
51
58
|
}),
|
|
@@ -122,6 +129,20 @@ function mergeSection(defaults, raw, keyMap) {
|
|
|
122
129
|
return out;
|
|
123
130
|
}
|
|
124
131
|
|
|
132
|
+
/** The species a mud character id names — "mole-1" -> "mole" — which is how
|
|
133
|
+
* every per-species knob above is keyed. Pure. */
|
|
134
|
+
export function mudSpeciesOf(characterId) {
|
|
135
|
+
return String(characterId).replace(/-\d+$/, "");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** What one turn costs `characterId` in mass, from the resolved `mud` section.
|
|
139
|
+
* Zero for a species the config carries no drain for: a knob nobody set is not
|
|
140
|
+
* a reason to invent a number and starve something with it. Pure. */
|
|
141
|
+
export function mudMassDrainPerTurn(mudConfig, characterId) {
|
|
142
|
+
const drain = mudConfig?.[`${mudSpeciesOf(characterId)}MassDecrementPerTurn`];
|
|
143
|
+
return Number.isFinite(drain) ? drain : 0;
|
|
144
|
+
}
|
|
145
|
+
|
|
125
146
|
/**
|
|
126
147
|
* Fold a normalized tmct.toml's `games`/`planning` tables (the raw sparse
|
|
127
148
|
* pass-through src/adapters/toml-config.mjs's normalizeConfig produces —
|
|
@@ -149,6 +149,19 @@ export function loadLexicon(extra, ns = DEFAULT_NS) {
|
|
|
149
149
|
return lex;
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
+
/** `lexicon` with `names` additionally declared as proper names. The noun,
|
|
153
|
+
* verb and adjective maps are SHARED with the base lexicon rather than
|
|
154
|
+
* re-ingested — a caller that re-declares on every turn (a live game world
|
|
155
|
+
* minting ids as it is played) would otherwise rebuild nine thousand core
|
|
156
|
+
* entries to add half a dozen. Proper names outrank every other category, so
|
|
157
|
+
* a name with no dictionary reading of its own ("groundhog-1", "carrot-2")
|
|
158
|
+
* resolves as itself instead of dying as an undeclared word. */
|
|
159
|
+
export function withProperNames(lexicon, names) {
|
|
160
|
+
const properNames = new Map(lexicon.properNames);
|
|
161
|
+
for (const name of names) properNames.set(String(name).toLowerCase(), String(name));
|
|
162
|
+
return { ...lexicon, properNames };
|
|
163
|
+
}
|
|
164
|
+
|
|
152
165
|
/** Noun lookup with plural folding; returns the entry ({lemma, property?}) or
|
|
153
166
|
* null. `opts.singularOnly` (an "a"/"an" determiner) prunes the irregular-
|
|
154
167
|
* plural fold in favor of a standalone-singular entry when both exist for
|
|
@@ -40,11 +40,13 @@ function parseChatTagRest(rest) {
|
|
|
40
40
|
* same tier the hand-written tier2 corpus already scores at; the :turnN
|
|
41
41
|
* segment a snapshot write carries records when, not who, and is not
|
|
42
42
|
* part of the Source identity)
|
|
43
|
-
* mud:<character>[:turnN]
|
|
43
|
+
* mud:<character>[:turnN][:gone] -> { kind:"corpus", name:"mud:<character>" }
|
|
44
44
|
* (one character's own testimony in a multi-character world — what it told
|
|
45
|
-
* someone, what it examined for itself
|
|
46
|
-
*
|
|
47
|
-
* apart from a world of the
|
|
45
|
+
* someone, what it examined for itself, and with `:gone` what it saw leave
|
|
46
|
+
* the world. Same tier as the world, but one Source per character; the
|
|
47
|
+
* `mud:` prefix in the name keeps that Source apart from a world of the
|
|
48
|
+
* same literal name. Both trailing segments say when and what, not who, so
|
|
49
|
+
* neither is part of the Source identity)
|
|
48
50
|
* ace:chat:<session>@<ts> -> { kind:"operator", createdAt:<ts>, sessionId:<session> }
|
|
49
51
|
* teach:chat:<session>@<ts> -> { kind:"teach", createdAt:<ts>, sessionId:<session> }
|
|
50
52
|
* web:<url> | url:<url> -> { kind:"web", url:<url> }
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// domain/p2p/facts.mjs — the small set of new predicates the P2P layer
|
|
2
|
+
// introduces on top of the existing memory store's triple shape, plus pure
|
|
3
|
+
// constructors for each. Every one is a plain add-only fact, replicated the
|
|
4
|
+
// same way any other fact is (appendFacts' own union-by-id behavior) — no
|
|
5
|
+
// new CRDT primitive. Provenance uses the existing `ace:` tag shape
|
|
6
|
+
// (`ace:p2p:<id>@<ts>`) so trust.mjs's own parser reads it with no changes
|
|
7
|
+
// there: stripped of its `ace:` prefix it reads as `p2p:<id>@<ts>`, which
|
|
8
|
+
// parses to { kind: "operator", sessionId: "p2p:<id>", createdAt: <ts> }.
|
|
9
|
+
import { provenanceTagToSource } from "../memory/trust.mjs";
|
|
10
|
+
|
|
11
|
+
export const WORLD_NAME_PREDICATE = "mgx:worldName";
|
|
12
|
+
export const NODE_NAME_PREDICATE = "mgx:nodeName";
|
|
13
|
+
export const PLAYED_BY_PREDICATE = "mgx:playedBy";
|
|
14
|
+
export const WAVED_PREDICATE = "mgx:waved";
|
|
15
|
+
|
|
16
|
+
export const P2P_PREDICATES = Object.freeze([
|
|
17
|
+
WORLD_NAME_PREDICATE,
|
|
18
|
+
NODE_NAME_PREDICATE,
|
|
19
|
+
PLAYED_BY_PREDICATE,
|
|
20
|
+
WAVED_PREDICATE,
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
const provenanceFor = (id, timestamp) => `ace:p2p:${id}@${timestamp}`;
|
|
24
|
+
|
|
25
|
+
export function worldNameFact(worldId, name, timestamp) {
|
|
26
|
+
return { subject: worldId, predicate: WORLD_NAME_PREDICATE, object: name, provenance: provenanceFor(worldId, timestamp) };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function nodeNameFact(peerId, name, timestamp) {
|
|
30
|
+
return { subject: `peer:${peerId}`, predicate: NODE_NAME_PREDICATE, object: name, provenance: provenanceFor(peerId, timestamp) };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function playedByFact(characterId, peerId, timestamp) {
|
|
34
|
+
return { subject: characterId, predicate: PLAYED_BY_PREDICATE, object: `peer:${peerId}`, provenance: provenanceFor(characterId, timestamp) };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function waveFact(characterId, roomId, timestamp) {
|
|
38
|
+
return { subject: characterId, predicate: WAVED_PREDICATE, object: roomId, provenance: provenanceFor(`${characterId}-${roomId}`, timestamp) };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The newest asserted-at timestamp across every " | "-joined segment of a
|
|
42
|
+
* fact's provenance — the correct read for "when was this most recently
|
|
43
|
+
* true," since a repeat wave unions a fresh tag onto the SAME fact id
|
|
44
|
+
* (same subject/predicate/object) rather than minting a new row. Returns
|
|
45
|
+
* null if no segment parses to a timestamp at all. */
|
|
46
|
+
export function latestProvenanceTimestamp(provenance) {
|
|
47
|
+
const tag = String(provenance || "");
|
|
48
|
+
if (!tag) return null;
|
|
49
|
+
let latest = null;
|
|
50
|
+
for (const segment of tag.split(" | ")) {
|
|
51
|
+
const source = provenanceTagToSource(segment);
|
|
52
|
+
const at = source?.createdAt ? Date.parse(source.createdAt) : NaN;
|
|
53
|
+
if (!Number.isNaN(at) && (latest === null || at > latest)) latest = at;
|
|
54
|
+
}
|
|
55
|
+
return latest;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** "Currently waving" is a read-time recency question, never a retraction —
|
|
59
|
+
* a wave fact older than the window just stops being read as current, with
|
|
60
|
+
* nothing ever deleted from the graph. */
|
|
61
|
+
export function isRecentWave(waveFactRow, nowMs, windowMs = 8000) {
|
|
62
|
+
const at = latestProvenanceTimestamp(waveFactRow?.provenance);
|
|
63
|
+
if (at === null) return false;
|
|
64
|
+
const age = nowMs - at;
|
|
65
|
+
return age >= 0 && age <= windowMs;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** The latest-by-timestamp fact for a subject+predicate pair — the "current
|
|
69
|
+
* value" read for an add-only, no-retraction fact like a node's own name
|
|
70
|
+
* or which peer plays a character (first-claim-wins uses the OLDEST of
|
|
71
|
+
* these instead; see playedByFact's own caller for that distinction). */
|
|
72
|
+
export function latestFact(rows, subject, predicate) {
|
|
73
|
+
let best = null;
|
|
74
|
+
let bestAt = -Infinity;
|
|
75
|
+
for (const row of rows) {
|
|
76
|
+
if (row.subject !== subject || row.predicate !== predicate) continue;
|
|
77
|
+
const at = latestProvenanceTimestamp(row.provenance) ?? -Infinity;
|
|
78
|
+
if (at > bestAt) { best = row; bestAt = at; }
|
|
79
|
+
}
|
|
80
|
+
return best;
|
|
81
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// domain/p2p/peer-id.mjs — pure id/name generation for the P2P layer. No
|
|
2
|
+
// network, no DOM; safe to import from both a Node test and a browser
|
|
3
|
+
// bundle. World/peer ids are UUIDs, generated client-side and never seen by
|
|
4
|
+
// any server. Display names are two words drawn from the same closed-world
|
|
5
|
+
// lexicon that grounds every taught fact, so a name a player sees on screen
|
|
6
|
+
// is always a real word this build's vocabulary already recognizes.
|
|
7
|
+
import { loadLexicon } from "../grammar/lexicon.mjs";
|
|
8
|
+
|
|
9
|
+
const fallbackId = (prefix) => `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
10
|
+
|
|
11
|
+
export function generatePeerId() {
|
|
12
|
+
return globalThis.crypto?.randomUUID?.() ?? fallbackId("peer");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function generateWorldId() {
|
|
16
|
+
return globalThis.crypto?.randomUUID?.() ?? fallbackId("world");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Two distinct words drawn from the lexicon's own noun list — a mnemonic
|
|
20
|
+
* label, not a credential. `random` is injectable for deterministic tests;
|
|
21
|
+
* defaults to Math.random. Callers add a numeric suffix themselves if a
|
|
22
|
+
* live collision turns up among currently-connected peers — this function
|
|
23
|
+
* never guesses at uniqueness on its own. */
|
|
24
|
+
export function generateDisplayName(random = Math.random) {
|
|
25
|
+
const words = [...loadLexicon().nouns.keys()];
|
|
26
|
+
if (words.length < 2) return "guest-guest";
|
|
27
|
+
const pick = () => words[Math.floor(random() * words.length)];
|
|
28
|
+
const first = pick();
|
|
29
|
+
let second = pick();
|
|
30
|
+
while (second === first) second = pick();
|
|
31
|
+
return `${first}-${second}`;
|
|
32
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// domain/p2p/provenance-relabel.mjs — rewrites a fact's outgoing provenance
|
|
2
|
+
// tag before it's broadcast to peers, so "who taught this" reads as a node
|
|
3
|
+
// name rather than a local session id. Only teach/operator-kind tags are
|
|
4
|
+
// touched; a mud world/testimony tag is already attributed to the world or
|
|
5
|
+
// the character that made it, not the person at the keyboard, and rewriting
|
|
6
|
+
// it would lose information rather than add it.
|
|
7
|
+
import { provenanceTagToSource } from "../memory/trust.mjs";
|
|
8
|
+
|
|
9
|
+
const RELABELED_KINDS = new Set(["teach", "operator"]);
|
|
10
|
+
|
|
11
|
+
/** `provenance` may already be a " | "-joined union of several tags (the
|
|
12
|
+
* same fact taught more than once, from different sources) — relabel each
|
|
13
|
+
* segment independently and rejoin, so a segment this peer didn't author
|
|
14
|
+
* passes through untouched. */
|
|
15
|
+
export function relabelForBroadcast(provenance, myDisplayName, timestamp) {
|
|
16
|
+
const tag = String(provenance || "");
|
|
17
|
+
if (!tag) return tag;
|
|
18
|
+
return tag
|
|
19
|
+
.split(" | ")
|
|
20
|
+
.map((segment) => {
|
|
21
|
+
const source = provenanceTagToSource(segment);
|
|
22
|
+
if (!source || !RELABELED_KINDS.has(source.kind)) return segment;
|
|
23
|
+
return `teach:peer:${myDisplayName}@${timestamp}`;
|
|
24
|
+
})
|
|
25
|
+
.join(" | ");
|
|
26
|
+
}
|