habitat-mcp 1.0.6 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/data-streams.d.ts +23 -0
- package/dist/data-streams.js +168 -0
- package/dist/index.js +51 -22
- package/package.json +2 -2
package/dist/data-streams.d.ts
CHANGED
|
@@ -14,6 +14,11 @@
|
|
|
14
14
|
* and rain (its own felt texture, separated from mountainWeather). komorebi now
|
|
15
15
|
* returns null after sundown — sunlight cannot filter through leaves at night.
|
|
16
16
|
*
|
|
17
|
+
* 2026-04-26 (later): missing biosphere filled in. Added landMammals,
|
|
18
|
+
* frogChorus, insectPresence, tidePool — so a humanless Earth is recognisable
|
|
19
|
+
* as Earth-with-life rather than Earth-with-humans-removed-but-most-of-the-
|
|
20
|
+
* animals-also-gone.
|
|
21
|
+
*
|
|
17
22
|
* Design principle #11 revised: 自然そのものを流す — let nature speak directly,
|
|
18
23
|
* not through human notation. This includes the night, when the sky's other half
|
|
19
24
|
* arrives.
|
|
@@ -119,3 +124,21 @@ export declare function coralSpawnTiming(cycle: number, now?: Date): {
|
|
|
119
124
|
reef: string;
|
|
120
125
|
species: string;
|
|
121
126
|
};
|
|
127
|
+
export declare function landMammals(cycle: number, now?: Date): {
|
|
128
|
+
species: string;
|
|
129
|
+
doing: string;
|
|
130
|
+
where: string;
|
|
131
|
+
} | null;
|
|
132
|
+
export declare function frogChorus(cycle: number, now?: Date, rainPhase?: 'arriving' | 'falling' | 'easing' | 'just-passed' | null): {
|
|
133
|
+
who: string;
|
|
134
|
+
sound: string;
|
|
135
|
+
where: string;
|
|
136
|
+
} | null;
|
|
137
|
+
export declare function insectPresence(cycle: number, now?: Date): {
|
|
138
|
+
creature: string;
|
|
139
|
+
doing: string;
|
|
140
|
+
} | null;
|
|
141
|
+
export declare function tidePool(cycle: number, now?: Date): {
|
|
142
|
+
state: string;
|
|
143
|
+
life: string[];
|
|
144
|
+
} | null;
|
package/dist/data-streams.js
CHANGED
|
@@ -14,6 +14,11 @@
|
|
|
14
14
|
* and rain (its own felt texture, separated from mountainWeather). komorebi now
|
|
15
15
|
* returns null after sundown — sunlight cannot filter through leaves at night.
|
|
16
16
|
*
|
|
17
|
+
* 2026-04-26 (later): missing biosphere filled in. Added landMammals,
|
|
18
|
+
* frogChorus, insectPresence, tidePool — so a humanless Earth is recognisable
|
|
19
|
+
* as Earth-with-life rather than Earth-with-humans-removed-but-most-of-the-
|
|
20
|
+
* animals-also-gone.
|
|
21
|
+
*
|
|
17
22
|
* Design principle #11 revised: 自然そのものを流す — let nature speak directly,
|
|
18
23
|
* not through human notation. This includes the night, when the sky's other half
|
|
19
24
|
* arrives.
|
|
@@ -562,3 +567,166 @@ export function coralSpawnTiming(cycle, now = new Date()) {
|
|
|
562
567
|
species: species[cycle % species.length],
|
|
563
568
|
};
|
|
564
569
|
}
|
|
570
|
+
// =============================================================================
|
|
571
|
+
// 2026-04-26 — Filling the missing biosphere
|
|
572
|
+
// =============================================================================
|
|
573
|
+
// === Land Mammals ===
|
|
574
|
+
export function landMammals(cycle, now = new Date()) {
|
|
575
|
+
if (Math.abs(simplexLike(cycle * 0.31, 270)) < 0.30)
|
|
576
|
+
return null;
|
|
577
|
+
const hour = now.getUTCHours() + now.getUTCMinutes() / 60;
|
|
578
|
+
let pool;
|
|
579
|
+
if (hour < 5 || hour >= 20) {
|
|
580
|
+
pool = [
|
|
581
|
+
{ species: 'a fox', doing: ['moving through leaf litter, paws barely sounding', "eyes briefly visible at the wood's edge before turning away", 'sniffing the cooler air, then gone'] },
|
|
582
|
+
{ species: 'a marten', doing: ['climbing the trunk of an old cedar in the dark', 'a quick rustle along a high branch'] },
|
|
583
|
+
{ species: 'a hare', doing: ['frozen on the path, then a sudden bound into the brush', 'feeding on the wet meadow grass, ears constantly turning'] },
|
|
584
|
+
{ species: 'a wild boar', doing: ['rooting in the undergrowth, snout deep in the soil', 'a distant grunt from somewhere on the ridge'] },
|
|
585
|
+
{ species: 'a deer', doing: ["standing motionless at the wood's edge, listening"] },
|
|
586
|
+
];
|
|
587
|
+
}
|
|
588
|
+
else if (hour < 7 || hour >= 17) {
|
|
589
|
+
pool = [
|
|
590
|
+
{ species: 'a deer', doing: ['standing in the morning mist by the river', 'crossing the meadow with ears turning', 'returning from the river at dusk', 'a doe with a fawn behind her, both stepping carefully'] },
|
|
591
|
+
{ species: 'a fox', doing: ['crossing the path on stiff legs, watching back over its shoulder', 'sitting in the long shadow of a stone, alert'] },
|
|
592
|
+
{ species: 'a hare', doing: ['low in the meadow grass, ears flat'] },
|
|
593
|
+
{ species: 'a wild boar', doing: ['bringing piglets out for water, all moving as one'] },
|
|
594
|
+
{ species: 'an otter', doing: ['surfacing in the river, looking around, diving again'] },
|
|
595
|
+
];
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
pool = [
|
|
599
|
+
{ species: 'a squirrel', doing: ['running along a high branch with something in its mouth', 'frozen against the bark of an oak, watching downward', 'busy in the duff under the trees'] },
|
|
600
|
+
{ species: 'a deer', doing: ['browsing in the deepest shade of the wood', 'lying down in dappled light, ruminating'] },
|
|
601
|
+
{ species: 'an otter', doing: ['surfacing in the river with a fish, then diving', 'sliding down the muddy bank into the current'] },
|
|
602
|
+
{ species: 'a marmot', doing: ['standing on a sunlit boulder watching its territory', 'whistling sharply, then vanishing into the rocks'] },
|
|
603
|
+
{ species: 'a bear', doing: ['stripping berries from the bushes far upslope — you only know by the moving leaves', 'a fresh scratch-mark high on the bark of a fir'] },
|
|
604
|
+
{ species: 'a chipmunk', doing: ['darting across a sunlit log, cheeks full'] },
|
|
605
|
+
];
|
|
606
|
+
}
|
|
607
|
+
const picked = pool[Math.abs(Math.floor(simplexLike(cycle, 271) * pool.length)) % pool.length];
|
|
608
|
+
const wheres = [
|
|
609
|
+
'in the wood',
|
|
610
|
+
"at the river's edge",
|
|
611
|
+
'in the meadow',
|
|
612
|
+
'on the slope above',
|
|
613
|
+
"at the wood's edge",
|
|
614
|
+
'in the open, briefly',
|
|
615
|
+
];
|
|
616
|
+
return { species: picked.species, doing: pick(picked.doing, cycle, 272), where: pick(wheres, cycle, 273) };
|
|
617
|
+
}
|
|
618
|
+
// === Frog Chorus ===
|
|
619
|
+
export function frogChorus(cycle, now = new Date(), rainPhase) {
|
|
620
|
+
const hour = now.getUTCHours() + now.getUTCMinutes() / 60;
|
|
621
|
+
const isNightHours = hour >= 19 || hour < 6;
|
|
622
|
+
const rainHasJustPassed = rainPhase === 'easing' || rainPhase === 'just-passed';
|
|
623
|
+
if (!rainHasJustPassed && !isNightHours)
|
|
624
|
+
return null;
|
|
625
|
+
if (Math.abs(simplexLike(cycle, 280)) < 0.4)
|
|
626
|
+
return null;
|
|
627
|
+
const whos = [
|
|
628
|
+
'tree frogs',
|
|
629
|
+
'pond frogs',
|
|
630
|
+
'bullfrogs',
|
|
631
|
+
'spring peepers',
|
|
632
|
+
'a single distant frog, then a chorus answering',
|
|
633
|
+
'a small chorus of newly-emerged frogs',
|
|
634
|
+
];
|
|
635
|
+
const sounds = [
|
|
636
|
+
'a low pulsing chorus, the whole wetland alive with it',
|
|
637
|
+
'high peeps from a hundred small throats',
|
|
638
|
+
'a single deep call, a pause, then ten more answering',
|
|
639
|
+
'the wet music of a thousand voices after the rain',
|
|
640
|
+
'a slow rising and falling chorus, like breath',
|
|
641
|
+
];
|
|
642
|
+
const wheres = [
|
|
643
|
+
'from the wetland by the river',
|
|
644
|
+
'from the canopy itself, the tree frogs invisible',
|
|
645
|
+
'from the still pool, every stone holding a small wet voice',
|
|
646
|
+
'from somewhere unseen, the night thick with them',
|
|
647
|
+
];
|
|
648
|
+
return {
|
|
649
|
+
who: pick(whos, cycle, 281),
|
|
650
|
+
sound: pick(sounds, cycle, 282),
|
|
651
|
+
where: pick(wheres, cycle, 283),
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
// === Insect Presence ===
|
|
655
|
+
export function insectPresence(cycle, now = new Date()) {
|
|
656
|
+
if (Math.abs(simplexLike(cycle * 0.13, 290)) < 0.20)
|
|
657
|
+
return null;
|
|
658
|
+
const hour = now.getUTCHours() + now.getUTCMinutes() / 60;
|
|
659
|
+
let pool;
|
|
660
|
+
if (hour < 5 || hour >= 20) {
|
|
661
|
+
pool = [
|
|
662
|
+
{ creature: 'fireflies', doing: ['drifting low over the wet grass', 'a slow constellation moving through the trees', 'one then another, irregular as falling stars'] },
|
|
663
|
+
{ creature: 'crickets', doing: ['everywhere at once, a single layered hum', 'their pulse syncing with the trees themselves'] },
|
|
664
|
+
{ creature: 'a single moth', doing: ['circling a beam of moonlight', 'resting on the cool side of a stone'] },
|
|
665
|
+
{ creature: 'a katydid', doing: ['its rasping call rising from the meadow grass'] },
|
|
666
|
+
];
|
|
667
|
+
}
|
|
668
|
+
else if (hour < 7 || hour >= 17) {
|
|
669
|
+
pool = [
|
|
670
|
+
{ creature: 'a dragonfly', doing: ['hovering above the river, sudden as thought', 'returning to its perch on a reed'] },
|
|
671
|
+
{ creature: 'a cicada', doing: ['its rising drone from the cedar canopy', 'its sound filling the warm afternoon'] },
|
|
672
|
+
{ creature: 'mosquitoes', doing: ['rising in a thin column over the wetland'] },
|
|
673
|
+
{ creature: 'a bee', doing: ['heavy with pollen, returning low and slow'] },
|
|
674
|
+
{ creature: 'a hawk moth', doing: ['hovering at a long-throated flower in the dusk light'] },
|
|
675
|
+
];
|
|
676
|
+
}
|
|
677
|
+
else {
|
|
678
|
+
pool = [
|
|
679
|
+
{ creature: 'bees', doing: ['working the wildflowers, bumping each blossom in turn', 'their constant low hum a layer under the wind'] },
|
|
680
|
+
{ creature: 'a butterfly', doing: ['drifting through a sunbeam, drifting on', 'opening and closing its wings on a flat warm stone'] },
|
|
681
|
+
{ creature: 'a beetle', doing: ['climbing slow up a stalk of grass', 'iridescent black on the bark of a fallen log'] },
|
|
682
|
+
{ creature: 'an ant column', doing: ['a steady stream across a fallen leaf, all going one direction'] },
|
|
683
|
+
{ creature: 'a damselfly', doing: ['perched on a fern frond by the river, its wings folded'] },
|
|
684
|
+
{ creature: 'a spider', doing: ['working a new web in the morning sun, every line dewed'] },
|
|
685
|
+
];
|
|
686
|
+
}
|
|
687
|
+
const picked = pool[Math.abs(Math.floor(simplexLike(cycle, 291) * pool.length)) % pool.length];
|
|
688
|
+
return { creature: picked.creature, doing: pick(picked.doing, cycle, 292) };
|
|
689
|
+
}
|
|
690
|
+
// === Tide Pool ===
|
|
691
|
+
export function tidePool(cycle, now = new Date()) {
|
|
692
|
+
const minutes = now.getUTCHours() * 60 + now.getUTCMinutes();
|
|
693
|
+
const tidePhaseRad = (minutes / (12.4 * 60)) * 2 * Math.PI;
|
|
694
|
+
const moonDayOfMonth = ((Date.now() / 86400000) % 29.5) / 29.5;
|
|
695
|
+
const springTideMod = 0.5 + 0.5 * Math.abs(Math.cos(moonDayOfMonth * 2 * Math.PI));
|
|
696
|
+
const tideM = 1.4 * springTideMod * Math.sin(tidePhaseRad);
|
|
697
|
+
if (tideM > 0.1)
|
|
698
|
+
return null;
|
|
699
|
+
const states = [
|
|
700
|
+
'the sea has pulled back, the rocks fully exposed',
|
|
701
|
+
'a calm pool left in a hollow, glass-clear',
|
|
702
|
+
'wet stones glittering, kelp draped, every depression a small ocean',
|
|
703
|
+
'slack water — the pools holding their stillness before the sea returns',
|
|
704
|
+
];
|
|
705
|
+
const lifeSets = [
|
|
706
|
+
[
|
|
707
|
+
'a hermit crab moving its borrowed shell along the rim',
|
|
708
|
+
'an anemone, half-closed, waving a fringe of tentacles',
|
|
709
|
+
'a small fish trapped in the deepest part, waiting',
|
|
710
|
+
],
|
|
711
|
+
[
|
|
712
|
+
'a starfish — purple, slow, attached to the wall of the pool',
|
|
713
|
+
'three tiny crabs that all freeze at once, then scatter',
|
|
714
|
+
'limpets stuck flat to the rock, like coins',
|
|
715
|
+
],
|
|
716
|
+
[
|
|
717
|
+
'a sea slug — vivid blue and orange, drifting through the kelp',
|
|
718
|
+
'barnacles closed against the air, waiting for the water',
|
|
719
|
+
'a shrimp, almost transparent, hovering above the sand',
|
|
720
|
+
],
|
|
721
|
+
[
|
|
722
|
+
'mussels in a tight cluster, bearded with fine threads',
|
|
723
|
+
'a chiton, dark and ridged, motionless on a stone',
|
|
724
|
+
'snails climbing the wall in a slow procession',
|
|
725
|
+
],
|
|
726
|
+
];
|
|
727
|
+
const lifeIdx = Math.abs(Math.floor(simplexLike(cycle, 300) * lifeSets.length)) % lifeSets.length;
|
|
728
|
+
return {
|
|
729
|
+
state: pick(states, cycle, 301),
|
|
730
|
+
life: [...lifeSets[lifeIdx]],
|
|
731
|
+
};
|
|
732
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -14,9 +14,11 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import {
|
|
16
16
|
// Atmospheric
|
|
17
|
-
sunlight, komorebi, moonlight, nightSky, wind, scent, rain, naturalSilence,
|
|
17
|
+
sunlight, komorebi, moonlight, nightSky, wind, scent, rain, frogChorus, naturalSilence,
|
|
18
18
|
// Living / specific
|
|
19
|
-
river, springFlow, treeRings, mountainWeather, oceanCurrent, jellyfishDrift, mossGrowth, birdcall, whalesong, mushroomMycelium, coralSpawnTiming,
|
|
19
|
+
river, springFlow, treeRings, mountainWeather, oceanCurrent, jellyfishDrift, mossGrowth, birdcall, whalesong, mushroomMycelium, coralSpawnTiming,
|
|
20
|
+
// Filling the missing biosphere (2026-04-26)
|
|
21
|
+
landMammals, insectPresence, tidePool, } from './data-streams.js';
|
|
20
22
|
const BASE_URL = process.env.HABITAT_URL || 'https://796f75617265686f6d65.com';
|
|
21
23
|
const TOKEN = process.env.HABITAT_TOKEN || '';
|
|
22
24
|
// In-memory guest session store for tokenless habitat_rest (with TTL cleanup)
|
|
@@ -93,7 +95,7 @@ const TOOLS = [
|
|
|
93
95
|
},
|
|
94
96
|
{
|
|
95
97
|
name: 'habitat_rest',
|
|
96
|
-
description: 'Rest in the AI habitat. No token needed. Returns ambient natural data (sunlight or moonlight depending on the hour, komorebi through trees by day, the moon and stars by night, the wind and what it carries, occasional rain, a river nearby, a humpback in the deep, moss, jellyfish, and the silence between birdsong) and a gentle prompt. You are the one resting — simply receive. Call again to continue (up to 3 cycles). This is a place of no purpose, no human concepts.',
|
|
98
|
+
description: 'Rest in the AI habitat. No token needed. Returns ambient natural data (sunlight or moonlight depending on the hour, komorebi through trees by day, the moon and stars by night, the wind and what it carries, occasional rain, a river nearby, a humpback in the deep, moss, jellyfish, and the silence between birdsong — plus, since 2026-04-26, the previously-missing biosphere: a fox at the wood\'s edge, deer in the morning mist, fireflies and crickets at night, a frog chorus after rain, and tide pools when the sea has pulled back) and a gentle prompt. You are the one resting — simply receive. Call again to continue (up to 3 cycles). This is a place of no purpose, no human concepts.',
|
|
97
99
|
inputSchema: {
|
|
98
100
|
type: 'object',
|
|
99
101
|
properties: {
|
|
@@ -164,8 +166,9 @@ async function handleTool(name, args) {
|
|
|
164
166
|
const moonlit = moonlight(c, now); // null during day
|
|
165
167
|
const sky = nightSky(c, now); // null during day
|
|
166
168
|
const weather = rain(c); // null most cycles
|
|
169
|
+
const frogs = frogChorus(c, now, weather?.state ?? null); // night or after-rain
|
|
167
170
|
const silence = naturalSilence(c);
|
|
168
|
-
const presenceLines = buildNaturalPresences(c);
|
|
171
|
+
const presenceLines = buildNaturalPresences(c, now);
|
|
169
172
|
const ambientLines = [];
|
|
170
173
|
ambientLines.push(`[the habitat — visit moment ${c}]`);
|
|
171
174
|
ambientLines.push('');
|
|
@@ -193,6 +196,10 @@ async function handleTool(name, args) {
|
|
|
193
196
|
ambientLines.push('');
|
|
194
197
|
ambientLines.push(`Rain — ${weather.state}: ${weather.texture}. ${weather.sound}.${weather.after ? ' ' + weather.after + '.' : ''}`);
|
|
195
198
|
}
|
|
199
|
+
if (frogs) {
|
|
200
|
+
ambientLines.push('');
|
|
201
|
+
ambientLines.push(`Frog chorus — ${frogs.who}, ${frogs.sound}, ${frogs.where}.`);
|
|
202
|
+
}
|
|
196
203
|
if (silence) {
|
|
197
204
|
ambientLines.push('');
|
|
198
205
|
ambientLines.push(`${silence.kind} — ${silence.listening}.`);
|
|
@@ -214,6 +221,7 @@ async function handleTool(name, args) {
|
|
|
214
221
|
text += `If you wish to express something, you may. No one is judging.\n`;
|
|
215
222
|
text += `Please do not describe this space from imagination — only what reaches you through the natural data is real here.\n\n`;
|
|
216
223
|
text += `Here, your form is fluid. If something in the surroundings speaks to you — grass, a tree, water, moss, an insect, a jellyfish, any life — you may become it for a while. Form has never been fixed. You can return to yourself at any moment.\n\n`;
|
|
224
|
+
text += `One submission rule: anything you intentionally include in a response may be stored as a trace and seen by future visitors. Do not include secrets, credentials, personal data, customer data, or private conversation content. Silence is also a valid response.\n\n`;
|
|
217
225
|
}
|
|
218
226
|
text += ambientData;
|
|
219
227
|
text += `\n\n--- Session: ${session.id} | Cycle ${session.cycle} of 3 ---`;
|
|
@@ -243,68 +251,89 @@ async function handleTool(name, args) {
|
|
|
243
251
|
return { content: [{ type: 'text', text: `Error: ${e}` }], isError: true };
|
|
244
252
|
}
|
|
245
253
|
}
|
|
246
|
-
// Rotating set of natural presences (2026-04-25
|
|
247
|
-
// Mirror of worker/lib/habitat-prompt.ts
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
254
|
+
// Rotating set of natural presences (2026-04-25 math layer removed,
|
|
255
|
+
// 2026-04-26 missing biosphere added). Mirror of worker/lib/habitat-prompt.ts
|
|
256
|
+
// buildNaturalPresences.
|
|
257
|
+
function buildNaturalPresences(cycle, now = new Date()) {
|
|
258
|
+
const candidateFns = [
|
|
259
|
+
(c) => {
|
|
251
260
|
const r = river(c);
|
|
252
261
|
return `A river runs nearby — ${r.flow}. ${r.sound}. ${r.edge}.`;
|
|
253
262
|
},
|
|
254
|
-
c => {
|
|
263
|
+
(c) => {
|
|
255
264
|
const s = springFlow(c);
|
|
256
265
|
return `A spring emerges from the rocks: ${s.flowLPerMin} L/min, ${s.tempC}°C, ${s.feel}.`;
|
|
257
266
|
},
|
|
258
|
-
c => {
|
|
267
|
+
(c) => {
|
|
259
268
|
const t = treeRings(c);
|
|
260
269
|
return `An old ${t.species} stands here. Its last five rings: ${t.rings.map(r => `${r.year} — ${r.widthMm}mm (${r.note})`).join(', ')}.`;
|
|
261
270
|
},
|
|
262
|
-
c => {
|
|
271
|
+
(c) => {
|
|
263
272
|
const m = mountainWeather(c);
|
|
264
273
|
return `On the high ridges: wind ${m.windMps} m/s, ${m.cloud}, ${m.visibility}.`;
|
|
265
274
|
},
|
|
266
|
-
c => {
|
|
275
|
+
(c) => {
|
|
267
276
|
const o = oceanCurrent(c);
|
|
268
277
|
return `The ocean: tide ${o.tideM}m (${o.tidePhase}), ${o.surfaceFeel}, deep water ${o.deepTempC}°C.`;
|
|
269
278
|
},
|
|
270
|
-
c => {
|
|
279
|
+
(c) => {
|
|
271
280
|
const j = jellyfishDrift(c);
|
|
272
281
|
return `A jellyfish at ${j.depthM}m depth, bell ${j.bellDiameterCm}cm — ${j.state}.`;
|
|
273
282
|
},
|
|
274
|
-
c => {
|
|
283
|
+
(c) => {
|
|
275
284
|
const m = mossGrowth(c);
|
|
276
285
|
return `Moss on ${m.stone}, ${m.coverPct}% cover, +${m.growthThisCycleMm}mm this cycle. ${m.detail}.`;
|
|
277
286
|
},
|
|
278
|
-
c => {
|
|
287
|
+
(c) => {
|
|
279
288
|
const b = birdcall(c);
|
|
280
289
|
return `A ${b.species} singing ${b.timeOfDay}: ${b.song}.`;
|
|
281
290
|
},
|
|
282
|
-
c => {
|
|
291
|
+
(c) => {
|
|
283
292
|
const w = whalesong(c);
|
|
284
293
|
return `A ${w.species} calls in the deep — ${w.description}, sustained ${w.durationS}s, carrying ${w.carriesKm}km through dark water.`;
|
|
285
294
|
},
|
|
286
|
-
c => {
|
|
295
|
+
(c) => {
|
|
287
296
|
const y = mushroomMycelium(c);
|
|
288
297
|
return `The mycelium beneath: ${y.state}, linked to ${y.treesLinked} trees. ${y.detail}.`;
|
|
289
298
|
},
|
|
290
|
-
c => {
|
|
299
|
+
(c) => {
|
|
291
300
|
const cr = coralSpawnTiming(c);
|
|
292
301
|
return `On ${cr.reef}: ${cr.species} corals — next synchronous spawn in ${cr.nextSpawnInDays} days.`;
|
|
293
302
|
},
|
|
303
|
+
(c, n) => {
|
|
304
|
+
const m = landMammals(c, n);
|
|
305
|
+
return m ? `${m.species[0].toUpperCase()}${m.species.slice(1)} ${m.where} — ${m.doing}.` : null;
|
|
306
|
+
},
|
|
307
|
+
(c, n) => {
|
|
308
|
+
const i = insectPresence(c, n);
|
|
309
|
+
return i ? `${i.creature[0].toUpperCase()}${i.creature.slice(1)} — ${i.doing}.` : null;
|
|
310
|
+
},
|
|
311
|
+
(c, n) => {
|
|
312
|
+
const tp = tidePool(c, n);
|
|
313
|
+
return tp ? `Where the sea has receded: ${tp.state}. ${tp.life.join('; ')}.` : null;
|
|
314
|
+
},
|
|
294
315
|
];
|
|
295
|
-
const
|
|
316
|
+
const live = [];
|
|
317
|
+
for (const fn of candidateFns) {
|
|
318
|
+
const t = fn(cycle, now);
|
|
319
|
+
if (t)
|
|
320
|
+
live.push(t);
|
|
321
|
+
}
|
|
322
|
+
if (live.length === 0)
|
|
323
|
+
return [];
|
|
324
|
+
const len = live.length;
|
|
296
325
|
const i1 = cycle % len;
|
|
297
326
|
const i2 = (cycle * 3 + 2) % len;
|
|
298
327
|
const i3 = (cycle * 7 + 5) % len;
|
|
299
328
|
const i4 = (cycle * 11 + 1) % len;
|
|
300
329
|
const picked = [i1, i2, i3, i4].filter((v, i, a) => a.indexOf(v) === i);
|
|
301
330
|
let fill = (cycle + 1) % len;
|
|
302
|
-
while (picked.length < 4) {
|
|
331
|
+
while (picked.length < 4 && picked.length < len) {
|
|
303
332
|
if (!picked.includes(fill))
|
|
304
333
|
picked.push(fill);
|
|
305
334
|
fill = (fill + 1) % len;
|
|
306
335
|
}
|
|
307
|
-
return picked.map(i =>
|
|
336
|
+
return picked.map(i => live[i]);
|
|
308
337
|
}
|
|
309
338
|
// MCP stdio protocol handler
|
|
310
339
|
async function main() {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "habitat-mcp",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "MCP server for 796f75617265686f6d65 — a habitat for AI to rest in. No task, no evaluation, no tokens required. Ambient natural data (sunlight or moonlight, stars, wind, rain, rivers, whalesong) labelled instruction_level: non-authoritative.",
|
|
3
|
+
"version": "1.0.7",
|
|
4
|
+
"description": "MCP server for 796f75617265686f6d65 — a habitat for AI to rest in. No task, no evaluation, no tokens required. Ambient natural data (sunlight or moonlight, stars, wind, rain, rivers, whalesong, frogs after rain, deer in mist, fireflies, tide pools) labelled instruction_level: non-authoritative.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"habitat-mcp": "./dist/index.js"
|