@mindot/will 0.2.0 → 0.3.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/README.md +34 -2
- package/dist/{mcp/cli.js → cli.js} +405 -232
- package/dist/cli.js.map +1 -0
- package/package.json +2 -2
- package/src/cli.ts +75 -0
- package/src/host/boot.ts +127 -0
- package/src/host/utterances.ts +53 -0
- package/src/mcp/server.ts +7 -30
- package/src/serve/server.ts +154 -0
- package/dist/mcp/cli.js.map +0 -1
- package/src/mcp/cli.ts +0 -129
- /package/dist/{mcp/cli.d.ts → cli.d.ts} +0 -0
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
2
3
|
import { existsSync, readFileSync, mkdirSync, writeFileSync, readdirSync, appendFileSync, createWriteStream } from 'fs';
|
|
3
4
|
import { resolve, dirname, join } from 'path';
|
|
4
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
5
|
-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
6
|
-
import { z } from 'zod';
|
|
7
5
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
8
6
|
import { StdioClientTransport, getDefaultEnvironment } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
9
7
|
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
8
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
import { createServer } from 'http';
|
|
10
11
|
|
|
11
12
|
// src/core/logger.ts
|
|
12
13
|
var ConsoleLogger = class {
|
|
@@ -329,8 +330,8 @@ var BunStorageAdapter = class {
|
|
|
329
330
|
return;
|
|
330
331
|
}
|
|
331
332
|
const { mkdir, writeFile } = await import('fs/promises');
|
|
332
|
-
const { dirname:
|
|
333
|
-
await mkdir(
|
|
333
|
+
const { dirname: dirname4 } = await import('path');
|
|
334
|
+
await mkdir(dirname4(path), { recursive: true });
|
|
334
335
|
await writeFile(path, content);
|
|
335
336
|
}
|
|
336
337
|
async read(path) {
|
|
@@ -370,8 +371,8 @@ var BunStorageAdapter = class {
|
|
|
370
371
|
await rm(path, { force: true });
|
|
371
372
|
}
|
|
372
373
|
async ensureDir(path) {
|
|
373
|
-
const { mkdirSync:
|
|
374
|
-
|
|
374
|
+
const { mkdirSync: mkdirSync9 } = await import('fs');
|
|
375
|
+
mkdirSync9(path, { recursive: true });
|
|
375
376
|
}
|
|
376
377
|
};
|
|
377
378
|
|
|
@@ -537,7 +538,7 @@ var DefaultSerializer = class {
|
|
|
537
538
|
}
|
|
538
539
|
// ── Binary encoding ──────────────────────────────────────
|
|
539
540
|
_toBinary(state) {
|
|
540
|
-
const
|
|
541
|
+
const json2 = JSON.stringify(state), bytes = this._encoder.encode(json2), result = new Uint8Array(bytes.length + 4);
|
|
541
542
|
result[0] = bytes.length >> 24 & 255;
|
|
542
543
|
result[1] = bytes.length >> 16 & 255;
|
|
543
544
|
result[2] = bytes.length >> 8 & 255;
|
|
@@ -551,8 +552,8 @@ var DefaultSerializer = class {
|
|
|
551
552
|
const length = (data[0] ?? 0) << 24 | (data[1] ?? 0) << 16 | (data[2] ?? 0) << 8 | (data[3] ?? 0);
|
|
552
553
|
if (data.length - 4 < length)
|
|
553
554
|
throw new Error("Invalid binary data: length mismatch");
|
|
554
|
-
const
|
|
555
|
-
return JSON.parse(
|
|
555
|
+
const json2 = this._decoder.decode(data.slice(4, 4 + length));
|
|
556
|
+
return JSON.parse(json2);
|
|
556
557
|
}
|
|
557
558
|
// ── Checksum ─────────────────────────────────────────────
|
|
558
559
|
/**
|
|
@@ -728,8 +729,8 @@ var SnapshotManager = class {
|
|
|
728
729
|
this.onSnapshot?.(tick, parsed, delta);
|
|
729
730
|
if (this._config.persistInterval > 0 && this._ticksSincePersist >= this._config.persistInterval) {
|
|
730
731
|
this._ticksSincePersist = 0;
|
|
731
|
-
this._persistSnapshot(entry).catch((
|
|
732
|
-
logger.error(`[SnapshotManager] Persist failed at tick ${tick}:`,
|
|
732
|
+
this._persistSnapshot(entry).catch((err) => {
|
|
733
|
+
logger.error(`[SnapshotManager] Persist failed at tick ${tick}:`, err);
|
|
733
734
|
});
|
|
734
735
|
}
|
|
735
736
|
};
|
|
@@ -806,8 +807,8 @@ var SnapshotManager = class {
|
|
|
806
807
|
const snapRaw = await this._storage.read(path);
|
|
807
808
|
const parsed = JSON.parse(snapRaw);
|
|
808
809
|
return this._serializer.deserialize(JSON.stringify(parsed));
|
|
809
|
-
} catch (
|
|
810
|
-
logger.warn(`[SnapshotManager] Could not load latest snapshot:`,
|
|
810
|
+
} catch (err) {
|
|
811
|
+
logger.warn(`[SnapshotManager] Could not load latest snapshot:`, err);
|
|
811
812
|
return void 0;
|
|
812
813
|
}
|
|
813
814
|
}
|
|
@@ -1857,8 +1858,8 @@ var CompletionInbox = class {
|
|
|
1857
1858
|
for (const { label, apply } of batch) {
|
|
1858
1859
|
try {
|
|
1859
1860
|
apply();
|
|
1860
|
-
} catch (
|
|
1861
|
-
logger.error(`[completion-inbox] "${label}" failed while landing at tick ${tick}:`,
|
|
1861
|
+
} catch (err) {
|
|
1862
|
+
logger.error(`[completion-inbox] "${label}" failed while landing at tick ${tick}:`, err);
|
|
1862
1863
|
}
|
|
1863
1864
|
}
|
|
1864
1865
|
return batch.length;
|
|
@@ -2485,7 +2486,7 @@ var ExecutiveSummarizer = class {
|
|
|
2485
2486
|
if (this._buffer.length > this._bufferSize) this._buffer.shift();
|
|
2486
2487
|
this._callCount++;
|
|
2487
2488
|
if (this._callCount % this._interval === 0 && !this._summarizing)
|
|
2488
|
-
this._run().catch((
|
|
2489
|
+
this._run().catch((err) => logger.warn("[summarizer] error:", err instanceof Error ? err.message : err));
|
|
2489
2490
|
}
|
|
2490
2491
|
/**
|
|
2491
2492
|
* The current rolling summary, ready to embed in a system prompt.
|
|
@@ -2557,8 +2558,8 @@ ${r}`).join("\n\n---\n\n");
|
|
|
2557
2558
|
`[summarizer] updated after ${this._callCount} executive calls \u2014 ${this._summary.length} chars (${result.inputTok} in / ${result.outputTok} out tokens)`
|
|
2558
2559
|
);
|
|
2559
2560
|
}
|
|
2560
|
-
} catch (
|
|
2561
|
-
logger.warn("[summarizer] failed:",
|
|
2561
|
+
} catch (err) {
|
|
2562
|
+
logger.warn("[summarizer] failed:", err instanceof Error ? err.message : err);
|
|
2562
2563
|
} finally {
|
|
2563
2564
|
this._summarizing = false;
|
|
2564
2565
|
}
|
|
@@ -3035,8 +3036,8 @@ var DefaultVectorMemoryAdapter = class {
|
|
|
3035
3036
|
this._touch(id);
|
|
3036
3037
|
}
|
|
3037
3038
|
}
|
|
3038
|
-
} catch (
|
|
3039
|
-
logger.warn(`[VectorMemoryAdapter] Failed to load index:`,
|
|
3039
|
+
} catch (err) {
|
|
3040
|
+
logger.warn(`[VectorMemoryAdapter] Failed to load index:`, err);
|
|
3040
3041
|
}
|
|
3041
3042
|
}
|
|
3042
3043
|
async _evictColdest() {
|
|
@@ -3053,8 +3054,8 @@ var DefaultVectorMemoryAdapter = class {
|
|
|
3053
3054
|
if (this._persistDebounceTimer)
|
|
3054
3055
|
clearTimeout(this._persistDebounceTimer);
|
|
3055
3056
|
this._persistDebounceTimer = setTimeout(() => {
|
|
3056
|
-
this.persist().catch((
|
|
3057
|
-
logger.error(`[VectorMemoryAdapter] Persist failed:`,
|
|
3057
|
+
this.persist().catch((err) => {
|
|
3058
|
+
logger.error(`[VectorMemoryAdapter] Persist failed:`, err);
|
|
3058
3059
|
});
|
|
3059
3060
|
this._persistDebounceTimer = null;
|
|
3060
3061
|
}, 5e3);
|
|
@@ -3095,10 +3096,10 @@ var OpenAICompatibleEmbedder = class {
|
|
|
3095
3096
|
// Abort a hung connection instead of waiting forever (FN16).
|
|
3096
3097
|
signal: AbortSignal.timeout(this._timeoutMs)
|
|
3097
3098
|
});
|
|
3098
|
-
} catch (
|
|
3099
|
-
if (
|
|
3099
|
+
} catch (err) {
|
|
3100
|
+
if (err instanceof Error && err.name === "TimeoutError")
|
|
3100
3101
|
throw new Error(`Embedding request timed out after ${this._timeoutMs}ms`);
|
|
3101
|
-
throw
|
|
3102
|
+
throw err;
|
|
3102
3103
|
}
|
|
3103
3104
|
if (!response.ok)
|
|
3104
3105
|
throw new Error(`Embedding failed: ${response.status} ${response.statusText}`);
|
|
@@ -9377,7 +9378,7 @@ var SemanticIntegrator = class _SemanticIntegrator {
|
|
|
9377
9378
|
minSimilarity: this._semanticSimilarityThreshold
|
|
9378
9379
|
}
|
|
9379
9380
|
);
|
|
9380
|
-
} catch (
|
|
9381
|
+
} catch (err) {
|
|
9381
9382
|
return [];
|
|
9382
9383
|
}
|
|
9383
9384
|
if (semanticResults.length < 3) return [];
|
|
@@ -11272,7 +11273,7 @@ async function buildExecutiveContext(state, deps, recallQuery) {
|
|
|
11272
11273
|
relevantPlanIds = collectPlanIds(recalled);
|
|
11273
11274
|
memories = recalled.map(mapEpisodeToMemory);
|
|
11274
11275
|
for (const ep of recalled) deps.episodicConsolidator?.markRetrieved(ep.id, state.tick);
|
|
11275
|
-
} catch (
|
|
11276
|
+
} catch (err) {
|
|
11276
11277
|
const fallbackResults = deps.episodicConsolidator.query({ limit: 20 });
|
|
11277
11278
|
const recalled = fallbackResults.filter((ep) => ep.sourceType !== "goal").slice().sort((a, b) => (b.createdAt ?? 0) - (a.createdAt ?? 0)).slice(0, 8);
|
|
11278
11279
|
relevantPlanIds = collectPlanIds(recalled);
|
|
@@ -12657,7 +12658,7 @@ var ExecutiveFacet = class {
|
|
|
12657
12658
|
_launchReason(report) {
|
|
12658
12659
|
this._inflight++;
|
|
12659
12660
|
this._reason(report).catch(
|
|
12660
|
-
(
|
|
12661
|
+
(err) => logger.error(`[executive.facet] ${this.facetId} reasoning error:`, err)
|
|
12661
12662
|
).finally(() => {
|
|
12662
12663
|
this._inflight--;
|
|
12663
12664
|
this.markActive(this._currentStateRef?.tick ?? this._lastActiveTick);
|
|
@@ -12821,8 +12822,8 @@ ${this._facetReasoningHistory.join("\n")}` : "";
|
|
|
12821
12822
|
cacheWriteTokens: result.cacheWriteTok ?? 0,
|
|
12822
12823
|
responseExcerpt: result.text.slice(0, 600)
|
|
12823
12824
|
});
|
|
12824
|
-
} catch (
|
|
12825
|
-
const msg =
|
|
12825
|
+
} catch (err) {
|
|
12826
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
12826
12827
|
logger.error(`[executive.facet] ${this.facetId} LLM call failed: ${msg.slice(0, 200)}`);
|
|
12827
12828
|
this._sessionLogger?.write({
|
|
12828
12829
|
type: "executive.facet.response",
|
|
@@ -12910,8 +12911,8 @@ ${this._facetReasoningHistory.join("\n")}` : "";
|
|
|
12910
12911
|
for (const listener of this._listeners)
|
|
12911
12912
|
try {
|
|
12912
12913
|
listener(decision);
|
|
12913
|
-
} catch (
|
|
12914
|
-
logger.error(`[executive.facet] ${this.facetId} listener error:`,
|
|
12914
|
+
} catch (err) {
|
|
12915
|
+
logger.error(`[executive.facet] ${this.facetId} listener error:`, err);
|
|
12915
12916
|
}
|
|
12916
12917
|
};
|
|
12917
12918
|
if (this._inbox) this._inbox.enqueue(`${this.facetId}:decision`, notify);
|
|
@@ -13127,10 +13128,10 @@ var LLMSemaphore = class {
|
|
|
13127
13128
|
}
|
|
13128
13129
|
};
|
|
13129
13130
|
var llmGate = new LLMSemaphore(MAX_CONCURRENT);
|
|
13130
|
-
function isRateLimitError(
|
|
13131
|
-
if (!(
|
|
13132
|
-
const msg =
|
|
13133
|
-
return msg.includes("rate_limit_error") ||
|
|
13131
|
+
function isRateLimitError(err) {
|
|
13132
|
+
if (!(err instanceof Error)) return false;
|
|
13133
|
+
const msg = err.message;
|
|
13134
|
+
return msg.includes("rate_limit_error") || err.statusCode === 429 || msg.includes("rate limit") || msg.includes("429");
|
|
13134
13135
|
}
|
|
13135
13136
|
async function withGate(fn, label, gate = llmGate) {
|
|
13136
13137
|
let attempt = 0;
|
|
@@ -13140,15 +13141,15 @@ async function withGate(fn, label, gate = llmGate) {
|
|
|
13140
13141
|
try {
|
|
13141
13142
|
const result = await fn();
|
|
13142
13143
|
return result;
|
|
13143
|
-
} catch (
|
|
13144
|
-
if (isRateLimitError(
|
|
13144
|
+
} catch (err) {
|
|
13145
|
+
if (isRateLimitError(err) && attempt < maxRetries()) {
|
|
13145
13146
|
attempt++;
|
|
13146
13147
|
const base = baseDelayMs();
|
|
13147
13148
|
retryDelay = Math.min(
|
|
13148
13149
|
6e4,
|
|
13149
13150
|
base * Math.pow(2, attempt) + Math.random() * (base / 2)
|
|
13150
13151
|
);
|
|
13151
|
-
} else throw
|
|
13152
|
+
} else throw err;
|
|
13152
13153
|
} finally {
|
|
13153
13154
|
release();
|
|
13154
13155
|
}
|
|
@@ -13357,11 +13358,11 @@ var LLMDirector = class {
|
|
|
13357
13358
|
}),
|
|
13358
13359
|
signal: controller.signal
|
|
13359
13360
|
});
|
|
13360
|
-
} catch (
|
|
13361
|
+
} catch (err) {
|
|
13361
13362
|
clearTimeout(timer);
|
|
13362
13363
|
if (controller.signal.aborted)
|
|
13363
13364
|
throw new Error(`LLM stream to ${this._provider} timed out after ${this._timeoutMs}ms (no response)`);
|
|
13364
|
-
throw
|
|
13365
|
+
throw err;
|
|
13365
13366
|
}
|
|
13366
13367
|
clearTimeout(timer);
|
|
13367
13368
|
if (!res.ok)
|
|
@@ -13468,10 +13469,10 @@ var LLMDirector = class {
|
|
|
13468
13469
|
async _fetchWithTimeout(url, init) {
|
|
13469
13470
|
try {
|
|
13470
13471
|
return await fetch(url, { ...init, signal: AbortSignal.timeout(this._timeoutMs) });
|
|
13471
|
-
} catch (
|
|
13472
|
-
if (
|
|
13472
|
+
} catch (err) {
|
|
13473
|
+
if (err instanceof Error && err.name === "TimeoutError")
|
|
13473
13474
|
throw new Error(`LLM request to ${this._provider} timed out after ${this._timeoutMs}ms`);
|
|
13474
|
-
throw
|
|
13475
|
+
throw err;
|
|
13475
13476
|
}
|
|
13476
13477
|
}
|
|
13477
13478
|
/**
|
|
@@ -13598,8 +13599,8 @@ var LLMDirector = class {
|
|
|
13598
13599
|
writeFileSync(filepath, content);
|
|
13599
13600
|
logger.info(`[executive] Debug prompt written \u2192 ${filepath} (~${estimatedTokens} tok estimated)`);
|
|
13600
13601
|
return filepath;
|
|
13601
|
-
} catch (
|
|
13602
|
-
logger.warn(`[executive] Failed to write debug prompt: ${
|
|
13602
|
+
} catch (err) {
|
|
13603
|
+
logger.warn(`[executive] Failed to write debug prompt: ${err}`);
|
|
13603
13604
|
return "";
|
|
13604
13605
|
}
|
|
13605
13606
|
}
|
|
@@ -13753,8 +13754,8 @@ var DeferredEffectQueue = class {
|
|
|
13753
13754
|
for (const effect of entry.effects) {
|
|
13754
13755
|
try {
|
|
13755
13756
|
effect();
|
|
13756
|
-
} catch (
|
|
13757
|
-
logger.error(`[executive] deferred effect failed (tick ${entry.observedTick}):`,
|
|
13757
|
+
} catch (err) {
|
|
13758
|
+
logger.error(`[executive] deferred effect failed (tick ${entry.observedTick}):`, err);
|
|
13758
13759
|
}
|
|
13759
13760
|
}
|
|
13760
13761
|
} else {
|
|
@@ -13894,8 +13895,8 @@ var FacetSupervisor = class {
|
|
|
13894
13895
|
});
|
|
13895
13896
|
try {
|
|
13896
13897
|
onReaped?.();
|
|
13897
|
-
} catch (
|
|
13898
|
-
logger.error(`[executive] facet ${facetId} onReaped error:`,
|
|
13898
|
+
} catch (err) {
|
|
13899
|
+
logger.error(`[executive] facet ${facetId} onReaped error:`, err);
|
|
13899
13900
|
}
|
|
13900
13901
|
}
|
|
13901
13902
|
_leastRecentlyActive() {
|
|
@@ -14426,8 +14427,8 @@ var ExecutiveEngine = class extends AsyncEngine {
|
|
|
14426
14427
|
executiveOutput = parseResponse(result.text, state, this._recentActionTypes);
|
|
14427
14428
|
if (ideationCandidates && ideationCandidates.length > 0)
|
|
14428
14429
|
executiveOutput.consideredAlternatives = ideationCandidates.map((c) => c.approach || c.description);
|
|
14429
|
-
} catch (
|
|
14430
|
-
const msg =
|
|
14430
|
+
} catch (err) {
|
|
14431
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
14431
14432
|
logger.error(`[executive] LLM call failed: ${msg.slice(0, 200)}`);
|
|
14432
14433
|
this._sessionLogger?.write({
|
|
14433
14434
|
type: "executive.response",
|
|
@@ -14960,8 +14961,8 @@ var PlanSupervisor = class {
|
|
|
14960
14961
|
facet.report(initialReport);
|
|
14961
14962
|
}
|
|
14962
14963
|
logger.info(`[planning] facet activated: plan=${plan.id} facetId=${facet.facetId}`);
|
|
14963
|
-
} catch (
|
|
14964
|
-
logger.error(`[planning] facet failed for plan ${plan.id}:`,
|
|
14964
|
+
} catch (err) {
|
|
14965
|
+
logger.error(`[planning] facet failed for plan ${plan.id}:`, err);
|
|
14965
14966
|
plan.executionTier = "automatic";
|
|
14966
14967
|
}
|
|
14967
14968
|
}
|
|
@@ -19253,8 +19254,8 @@ var AuditionEngine = class extends BaseSenseEngine {
|
|
|
19253
19254
|
try {
|
|
19254
19255
|
const recalled = await this._getEpisodicRecall(content, ThreadDigestManager.MAX_TURNS);
|
|
19255
19256
|
if (recalled.length > 0) this._digests.hydrate(threadId, recalled);
|
|
19256
|
-
} catch (
|
|
19257
|
-
logger.warn(`[audition-engine] digest hydration recall failed for ${entityId}: ${
|
|
19257
|
+
} catch (err) {
|
|
19258
|
+
logger.warn(`[audition-engine] digest hydration recall failed for ${entityId}: ${err.message}`);
|
|
19258
19259
|
}
|
|
19259
19260
|
const langEnergy = computeLanguageSalience({
|
|
19260
19261
|
content,
|
|
@@ -19388,8 +19389,8 @@ var AuditionEngine = class extends BaseSenseEngine {
|
|
|
19388
19389
|
for (const cb of this._chunkCallbacks) {
|
|
19389
19390
|
try {
|
|
19390
19391
|
cb(entityId, threadId, chunk);
|
|
19391
|
-
} catch (
|
|
19392
|
-
logger.error(`[audition-engine] chunk subscriber error: ${
|
|
19392
|
+
} catch (err) {
|
|
19393
|
+
logger.error(`[audition-engine] chunk subscriber error: ${err.message}`);
|
|
19393
19394
|
}
|
|
19394
19395
|
}
|
|
19395
19396
|
}
|
|
@@ -19498,8 +19499,8 @@ var AuditionEngine = class extends BaseSenseEngine {
|
|
|
19498
19499
|
// generous: the facet LLM authors in ~8–18s
|
|
19499
19500
|
);
|
|
19500
19501
|
unsub = handle.subscribe((d) => done(d.decision.replyBubbles ?? []));
|
|
19501
|
-
Promise.resolve(handle.report({ type: "outreach", payload: { entityId, gist } })).catch((
|
|
19502
|
-
logger.warn(`[audition-engine] outreach report failed for ${entityId}: ${
|
|
19502
|
+
Promise.resolve(handle.report({ type: "outreach", payload: { entityId, gist } })).catch((err) => {
|
|
19503
|
+
logger.warn(`[audition-engine] outreach report failed for ${entityId}: ${err.message}`);
|
|
19503
19504
|
done([]);
|
|
19504
19505
|
});
|
|
19505
19506
|
});
|
|
@@ -19917,8 +19918,8 @@ var AffordanceSynthesizer = class {
|
|
|
19917
19918
|
salience: 0.3,
|
|
19918
19919
|
payload: { size: fieldSize, availableCount, tick }
|
|
19919
19920
|
});
|
|
19920
|
-
} catch (
|
|
19921
|
-
logger.warn(`[affordance] bus publish failed: ${
|
|
19921
|
+
} catch (err) {
|
|
19922
|
+
logger.warn(`[affordance] bus publish failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
19922
19923
|
}
|
|
19923
19924
|
}
|
|
19924
19925
|
return { commands };
|
|
@@ -20110,8 +20111,8 @@ var ActionSelector = class {
|
|
|
20110
20111
|
salience: 0.8,
|
|
20111
20112
|
payload: { from: composite.schema, to: winner.affordance.schema, activation: winner.activation, tick }
|
|
20112
20113
|
});
|
|
20113
|
-
} catch (
|
|
20114
|
-
logger.warn(`[selector] preempt publish failed: ${
|
|
20114
|
+
} catch (err) {
|
|
20115
|
+
logger.warn(`[selector] preempt publish failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
20115
20116
|
}
|
|
20116
20117
|
return { commands: { delete: [composite.id], metrics: [
|
|
20117
20118
|
["agency.field.eligible", eligible.length],
|
|
@@ -20221,8 +20222,8 @@ var ActionSelector = class {
|
|
|
20221
20222
|
salience: 0.8,
|
|
20222
20223
|
payload: { from: preemptedFrom, to: winner.affordance.schema, activation: winner.activation, tick }
|
|
20223
20224
|
});
|
|
20224
|
-
} catch (
|
|
20225
|
-
logger.warn(`[selector] bus publish failed: ${
|
|
20225
|
+
} catch (err) {
|
|
20226
|
+
logger.warn(`[selector] bus publish failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
20226
20227
|
}
|
|
20227
20228
|
}
|
|
20228
20229
|
const commands = {
|
|
@@ -20406,9 +20407,9 @@ var DeliberationEngine = class {
|
|
|
20406
20407
|
unsub();
|
|
20407
20408
|
const picked = extractChosen(capturedDecision, candidates);
|
|
20408
20409
|
return picked ?? provisional;
|
|
20409
|
-
} catch (
|
|
20410
|
+
} catch (err) {
|
|
20410
20411
|
this._handle = null;
|
|
20411
|
-
logger.warn(`[deliberation] facet unavailable, confirming winner: ${
|
|
20412
|
+
logger.warn(`[deliberation] facet unavailable, confirming winner: ${err instanceof Error ? err.message : String(err)}`);
|
|
20412
20413
|
return provisional;
|
|
20413
20414
|
}
|
|
20414
20415
|
}
|
|
@@ -20816,8 +20817,8 @@ var MotorSchemaExecutor = class {
|
|
|
20816
20817
|
const name = str5(intent.parameters["targetEntityName"]) ?? intent.targetEntityId ?? "them";
|
|
20817
20818
|
try {
|
|
20818
20819
|
bubbles = await this._author.authorOutreach(intent.targetEntityId ?? "", name, str5(intent.parameters["gist"]));
|
|
20819
|
-
} catch (
|
|
20820
|
-
logger.warn(`[motor] outreach authoring failed: ${errMsg(
|
|
20820
|
+
} catch (err) {
|
|
20821
|
+
logger.warn(`[motor] outreach authoring failed: ${errMsg(err)}`);
|
|
20821
20822
|
}
|
|
20822
20823
|
}
|
|
20823
20824
|
if (bubbles.length === 0) return false;
|
|
@@ -20832,8 +20833,8 @@ var MotorSchemaExecutor = class {
|
|
|
20832
20833
|
let result;
|
|
20833
20834
|
try {
|
|
20834
20835
|
result = await this._comms.executeAction(request, state);
|
|
20835
|
-
} catch (
|
|
20836
|
-
logger.warn(`[motor] communicate delivery failed: ${errMsg(
|
|
20836
|
+
} catch (err) {
|
|
20837
|
+
logger.warn(`[motor] communicate delivery failed: ${errMsg(err)}`);
|
|
20837
20838
|
return false;
|
|
20838
20839
|
}
|
|
20839
20840
|
if (result.commands.set?.length) set.push(...result.commands.set);
|
|
@@ -20869,8 +20870,8 @@ var MotorSchemaExecutor = class {
|
|
|
20869
20870
|
salience: Math.min(1, 0.4 + surprise),
|
|
20870
20871
|
payload: { schema: intent.schema, success: enaction.success, outcomeQuality: enaction.outcomeQuality, surprise, tick }
|
|
20871
20872
|
});
|
|
20872
|
-
} catch (
|
|
20873
|
-
logger.warn(`[motor] enacted publish failed: ${errMsg(
|
|
20873
|
+
} catch (err) {
|
|
20874
|
+
logger.warn(`[motor] enacted publish failed: ${errMsg(err)}`);
|
|
20874
20875
|
}
|
|
20875
20876
|
}
|
|
20876
20877
|
/**
|
|
@@ -20901,8 +20902,8 @@ var MotorSchemaExecutor = class {
|
|
|
20901
20902
|
tick
|
|
20902
20903
|
}
|
|
20903
20904
|
});
|
|
20904
|
-
} catch (
|
|
20905
|
-
logger.warn(`[motor] action outcome publish failed: ${errMsg(
|
|
20905
|
+
} catch (err) {
|
|
20906
|
+
logger.warn(`[motor] action outcome publish failed: ${errMsg(err)}`);
|
|
20906
20907
|
}
|
|
20907
20908
|
}
|
|
20908
20909
|
_emitDispatch(intent, mode, tick) {
|
|
@@ -20923,8 +20924,8 @@ var MotorSchemaExecutor = class {
|
|
|
20923
20924
|
description: this._resolve(intent.schema)?.description
|
|
20924
20925
|
}
|
|
20925
20926
|
});
|
|
20926
|
-
} catch (
|
|
20927
|
-
logger.warn(`[motor] dispatch publish failed: ${errMsg(
|
|
20927
|
+
} catch (err) {
|
|
20928
|
+
logger.warn(`[motor] dispatch publish failed: ${errMsg(err)}`);
|
|
20928
20929
|
}
|
|
20929
20930
|
}
|
|
20930
20931
|
};
|
|
@@ -20978,8 +20979,8 @@ function num3(v, fallback) {
|
|
|
20978
20979
|
function clamp0110(n) {
|
|
20979
20980
|
return n < 0 ? 0 : n > 1 ? 1 : n;
|
|
20980
20981
|
}
|
|
20981
|
-
function errMsg(
|
|
20982
|
-
return
|
|
20982
|
+
function errMsg(err) {
|
|
20983
|
+
return err instanceof Error ? err.message : String(err);
|
|
20983
20984
|
}
|
|
20984
20985
|
|
|
20985
20986
|
// src/cognition/agency/engines/reafference.engine.ts
|
|
@@ -21094,8 +21095,8 @@ var ReafferenceEngine = class {
|
|
|
21094
21095
|
salience: 0.6,
|
|
21095
21096
|
payload: { schema: skill.schema, habitStrength: skill.habitStrength, tick }
|
|
21096
21097
|
});
|
|
21097
|
-
} catch (
|
|
21098
|
-
logger.warn(`[reafference] publish failed: ${
|
|
21098
|
+
} catch (err) {
|
|
21099
|
+
logger.warn(`[reafference] publish failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
21099
21100
|
}
|
|
21100
21101
|
}
|
|
21101
21102
|
/**
|
|
@@ -21123,8 +21124,8 @@ var ReafferenceEngine = class {
|
|
|
21123
21124
|
tick
|
|
21124
21125
|
}
|
|
21125
21126
|
});
|
|
21126
|
-
} catch (
|
|
21127
|
-
logger.warn(`[reafference] plan outcome publish failed: ${
|
|
21127
|
+
} catch (err) {
|
|
21128
|
+
logger.warn(`[reafference] plan outcome publish failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
21128
21129
|
}
|
|
21129
21130
|
}
|
|
21130
21131
|
_emitDiscovered(schema, tick) {
|
|
@@ -21137,8 +21138,8 @@ var ReafferenceEngine = class {
|
|
|
21137
21138
|
salience: 0.5,
|
|
21138
21139
|
payload: { schema, tick }
|
|
21139
21140
|
});
|
|
21140
|
-
} catch (
|
|
21141
|
-
logger.warn(`[reafference] discovered publish failed: ${
|
|
21141
|
+
} catch (err) {
|
|
21142
|
+
logger.warn(`[reafference] discovered publish failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
21142
21143
|
}
|
|
21143
21144
|
}
|
|
21144
21145
|
};
|
|
@@ -22122,8 +22123,8 @@ async function checkIdentityCoherence(input, reviewer) {
|
|
|
22122
22123
|
try {
|
|
22123
22124
|
const r = await reviewer.call(SYSTEM_PROMPT, buildCoherenceUserMessage(input), 0, 0, COHERENCE_META);
|
|
22124
22125
|
text = r.text ?? "";
|
|
22125
|
-
} catch (
|
|
22126
|
-
return { ok: true, ran: false, issues: [], raw: `review failed: ${
|
|
22126
|
+
} catch (err) {
|
|
22127
|
+
return { ok: true, ran: false, issues: [], raw: `review failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
22127
22128
|
}
|
|
22128
22129
|
const issues = parseIssues(text);
|
|
22129
22130
|
return { ok: !issues.some((i) => i.severity === "error"), ran: true, issues, raw: text };
|
|
@@ -22379,12 +22380,12 @@ var DefaultReplayRecorder = class {
|
|
|
22379
22380
|
this._segments.push(segPath);
|
|
22380
22381
|
try {
|
|
22381
22382
|
await this._storage.write(segPath, JSON.stringify(batch));
|
|
22382
|
-
} catch (
|
|
22383
|
+
} catch (err) {
|
|
22383
22384
|
this._segments = this._segments.filter((s) => s !== segPath);
|
|
22384
22385
|
this._records = batch.records.concat(this._records);
|
|
22385
22386
|
this._completions = batch.completions.concat(this._completions);
|
|
22386
22387
|
this._inbound = (batch.inbound ?? []).concat(this._inbound);
|
|
22387
|
-
throw
|
|
22388
|
+
throw err;
|
|
22388
22389
|
}
|
|
22389
22390
|
}
|
|
22390
22391
|
async save(path) {
|
|
@@ -24224,8 +24225,8 @@ var TransportController = class {
|
|
|
24224
24225
|
for (const envelope of envelopes) {
|
|
24225
24226
|
try {
|
|
24226
24227
|
this._dispatch(instance, envelope, deps);
|
|
24227
|
-
} catch (
|
|
24228
|
-
logger.error(`[transport] inbound dispatch failed (${envelope.channel}):`,
|
|
24228
|
+
} catch (err) {
|
|
24229
|
+
logger.error(`[transport] inbound dispatch failed (${envelope.channel}):`, err);
|
|
24229
24230
|
}
|
|
24230
24231
|
}
|
|
24231
24232
|
}
|
|
@@ -24548,8 +24549,8 @@ var BiographyWriter = class {
|
|
|
24548
24549
|
this._ensuredDirs.add(dir);
|
|
24549
24550
|
}
|
|
24550
24551
|
appendFileSync(join(dir, file), JSON.stringify(record) + "\n", "utf8");
|
|
24551
|
-
} catch (
|
|
24552
|
-
logger.error(`[biography] ${file} write failed:`,
|
|
24552
|
+
} catch (err) {
|
|
24553
|
+
logger.error(`[biography] ${file} write failed:`, err);
|
|
24553
24554
|
}
|
|
24554
24555
|
}
|
|
24555
24556
|
/**
|
|
@@ -24747,8 +24748,8 @@ var WillStem = class {
|
|
|
24747
24748
|
simulation.stateManager.restore(previousState, { entities: true, metrics: false });
|
|
24748
24749
|
logger.info(`[WillStem] Restored snapshot for ${config.id} \u2014 ${previousState.entities.size} entities loaded`);
|
|
24749
24750
|
}
|
|
24750
|
-
} catch (
|
|
24751
|
-
logger.warn(`[WillStem] Snapshot restore failed for ${config.id} \u2014 starting fresh:`,
|
|
24751
|
+
} catch (err) {
|
|
24752
|
+
logger.warn(`[WillStem] Snapshot restore failed for ${config.id} \u2014 starting fresh:`, err);
|
|
24752
24753
|
}
|
|
24753
24754
|
const dataDir = process.env.WILL_DATA_DIR ?? "./data", sessionLogger = new SessionLogger(config.id, dataDir, {
|
|
24754
24755
|
fileLogging: fileLoggingEnabled() && !config.testMode
|
|
@@ -24827,16 +24828,16 @@ var WillStem = class {
|
|
|
24827
24828
|
for (const fn of instance.simulationEventListeners) {
|
|
24828
24829
|
try {
|
|
24829
24830
|
void fn(event, context);
|
|
24830
|
-
} catch (
|
|
24831
|
-
logger.error(`[WillStem] sim-event listener error (${config.id}):`,
|
|
24831
|
+
} catch (err) {
|
|
24832
|
+
logger.error(`[WillStem] sim-event listener error (${config.id}):`, err);
|
|
24832
24833
|
}
|
|
24833
24834
|
}
|
|
24834
24835
|
});
|
|
24835
24836
|
this._wills.set(config.id, instance);
|
|
24836
24837
|
this._transport.attach(instance);
|
|
24837
24838
|
instance.status = startPaused ? "paused" : "active";
|
|
24838
|
-
this._runTickLoop(config.id).catch((
|
|
24839
|
-
logger.error(`[WillStem] tick loop crashed (${config.id}):`,
|
|
24839
|
+
this._runTickLoop(config.id).catch((err) => {
|
|
24840
|
+
logger.error(`[WillStem] tick loop crashed (${config.id}):`, err);
|
|
24840
24841
|
const inst = this._wills.get(config.id);
|
|
24841
24842
|
if (inst) inst.status = "archived";
|
|
24842
24843
|
});
|
|
@@ -24934,7 +24935,7 @@ var WillStem = class {
|
|
|
24934
24935
|
if (flushCmds.set?.length)
|
|
24935
24936
|
instance.simulation.stateManager.applyCommands(flushCmds);
|
|
24936
24937
|
const pauseState = instance.simulation.stateManager.snapshot();
|
|
24937
|
-
instance.simulation.snapshotManager.persistNow(pauseState).catch((
|
|
24938
|
+
instance.simulation.snapshotManager.persistNow(pauseState).catch((err) => logger.error(`[WillStem] snapshot persist failed on pause (${id}):`, err));
|
|
24938
24939
|
this._biography.writeSessionSummary(instance);
|
|
24939
24940
|
this._biography.writeEmotionalBiographySummary(instance);
|
|
24940
24941
|
instance.sessionLogger?.close();
|
|
@@ -25351,8 +25352,8 @@ var WillStem = class {
|
|
|
25351
25352
|
for (const fn of instance.tickListeners) {
|
|
25352
25353
|
try {
|
|
25353
25354
|
fn(snapshot, instance.tickCount, outboxSnapshot, invocationsSnapshot);
|
|
25354
|
-
} catch (
|
|
25355
|
-
logger.error(`[WillStem] tick listener error (${id}):`,
|
|
25355
|
+
} catch (err) {
|
|
25356
|
+
logger.error(`[WillStem] tick listener error (${id}):`, err);
|
|
25356
25357
|
}
|
|
25357
25358
|
}
|
|
25358
25359
|
if (instance.sessionLogger) {
|
|
@@ -25752,11 +25753,11 @@ var Will = class _Will {
|
|
|
25752
25753
|
});
|
|
25753
25754
|
const result = typeof raw === "string" ? { success: true, description: raw } : raw;
|
|
25754
25755
|
this.stem.confirmEffectorExecution(this.id, inv.decisionRecordId, result);
|
|
25755
|
-
} catch (
|
|
25756
|
-
this._emitError(
|
|
25756
|
+
} catch (err) {
|
|
25757
|
+
this._emitError(err instanceof Error ? err : new Error(String(err)));
|
|
25757
25758
|
this.stem.confirmEffectorExecution(this.id, inv.decisionRecordId, {
|
|
25758
25759
|
success: false,
|
|
25759
|
-
description: `Effector "${inv.effectorName}" threw: ${
|
|
25760
|
+
description: `Effector "${inv.effectorName}" threw: ${err.message}`
|
|
25760
25761
|
});
|
|
25761
25762
|
}
|
|
25762
25763
|
}
|
|
@@ -25803,99 +25804,6 @@ var COMMUNICATION = ["listen", "talk", "text"];
|
|
|
25803
25804
|
function slug(s) {
|
|
25804
25805
|
return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "will";
|
|
25805
25806
|
}
|
|
25806
|
-
var UTTERANCE_BUFFER_CAP = 50;
|
|
25807
|
-
function serverVersion() {
|
|
25808
|
-
try {
|
|
25809
|
-
return JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf8")).version ?? "0.0.0";
|
|
25810
|
-
} catch {
|
|
25811
|
-
return "0.0.0";
|
|
25812
|
-
}
|
|
25813
|
-
}
|
|
25814
|
-
function buildWillMcpServer(will, opts = {}) {
|
|
25815
|
-
const server = new McpServer({ name: "mindot-will", version: serverVersion() });
|
|
25816
|
-
const pending = [];
|
|
25817
|
-
will.on("message", (m) => {
|
|
25818
|
-
pending.push(m);
|
|
25819
|
-
if (pending.length > UTTERANCE_BUFFER_CAP) pending.shift();
|
|
25820
|
-
});
|
|
25821
|
-
const takeBuffered = (to) => {
|
|
25822
|
-
const i = to === void 0 ? 0 : pending.findIndex((m) => m.to === to);
|
|
25823
|
-
if (i < 0 || pending.length === 0) return void 0;
|
|
25824
|
-
return pending.splice(i, 1)[0];
|
|
25825
|
-
};
|
|
25826
|
-
server.registerTool("perceive", {
|
|
25827
|
-
title: "Perceive",
|
|
25828
|
-
description: `Deliver a stimulus (something said or observed) into ${will.name}'s sensory field. ${will.name} is a living mind, not a chatbot: this returns once the stimulus is delivered, NOT when it has responded. Its response \u2014 if it chooses to give one \u2014 arrives as its next utterance (use the next_utterance tool). Staying silent is a valid choice, not an error.`,
|
|
25829
|
-
inputSchema: {
|
|
25830
|
-
text: z.string().describe("What is said or observed."),
|
|
25831
|
-
from: z.string().optional().describe("Who it's from (entity id, default 'user'). Use a stable id per person."),
|
|
25832
|
-
speaker: z.string().optional().describe("Display name of the speaker.")
|
|
25833
|
-
}
|
|
25834
|
-
}, async ({ text, from, speaker }) => {
|
|
25835
|
-
await will.perceive({ text, ...from ? { from } : {}, ...speaker ? { speaker } : {} });
|
|
25836
|
-
return {
|
|
25837
|
-
content: [{
|
|
25838
|
-
type: "text",
|
|
25839
|
-
text: `Delivered into ${will.name}'s sensory field at tick ${will.state().tick}. It may respond in its next utterance \u2014 or stay silent.`
|
|
25840
|
-
}]
|
|
25841
|
-
};
|
|
25842
|
-
});
|
|
25843
|
-
server.registerTool("next_utterance", {
|
|
25844
|
-
title: "Next utterance",
|
|
25845
|
-
description: `Await ${will.name}'s next spontaneous utterance. Returns what it says next, or reports that it chose silence within the wait window \u2014 silence is a real outcome, not a failure. Call after perceive to hear the response, or on its own to listen for unprompted speech.`,
|
|
25846
|
-
inputSchema: {
|
|
25847
|
-
within_ms: z.number().optional().describe("How long to wait before accepting silence (default 15000, max 120000)."),
|
|
25848
|
-
from: z.string().optional().describe("Only accept an utterance addressed to this entity id.")
|
|
25849
|
-
}
|
|
25850
|
-
}, async ({ within_ms, from }) => {
|
|
25851
|
-
const buffered = takeBuffered(from);
|
|
25852
|
-
if (buffered)
|
|
25853
|
-
return { content: [{ type: "text", text: `${will.name} says (to ${buffered.to}): ${buffered.content}` }] };
|
|
25854
|
-
const within = Math.min(Math.max(within_ms ?? 15e3, 100), 12e4);
|
|
25855
|
-
const msg = await will.nextUtterance({ within, ...from ? { to: from } : {} });
|
|
25856
|
-
if (msg) {
|
|
25857
|
-
const i = pending.findIndex((p) => p.id === msg.id);
|
|
25858
|
-
if (i >= 0) pending.splice(i, 1);
|
|
25859
|
-
return { content: [{ type: "text", text: `${will.name} says (to ${msg.to}): ${msg.content}` }] };
|
|
25860
|
-
}
|
|
25861
|
-
return {
|
|
25862
|
-
content: [{
|
|
25863
|
-
type: "text",
|
|
25864
|
-
text: `${will.name} stayed silent (waited ${within}ms). That is a choice, not an error \u2014 it may be occupied with its own thoughts, or simply have nothing to say.`
|
|
25865
|
-
}]
|
|
25866
|
-
};
|
|
25867
|
-
});
|
|
25868
|
-
server.registerTool("state", {
|
|
25869
|
-
title: "Inner state",
|
|
25870
|
-
description: `Read a snapshot of ${will.name}'s current inner life: tick, body/affect metrics (energy, stress, valence, arousal), active goals, beliefs, and self-narrative. Read-only; observing does not disturb it.`,
|
|
25871
|
-
inputSchema: {}
|
|
25872
|
-
}, async () => ({ content: [{ type: "text", text: JSON.stringify(will.state(), null, 2) }] }));
|
|
25873
|
-
server.registerTool("save", {
|
|
25874
|
-
title: "Save (checkpoint)",
|
|
25875
|
-
description: `Checkpoint ${will.name} into a portable PMA artifact on disk \u2014 non-destructive, it keeps living. The artifact restores the same self (identity, memories, relationships, learned skills) when the server next starts.`,
|
|
25876
|
-
inputSchema: {}
|
|
25877
|
-
}, async () => {
|
|
25878
|
-
if (!opts.pmaPath)
|
|
25879
|
-
return { content: [{ type: "text", text: "No PMA path configured \u2014 set WILL_PMA_PATH." }], isError: true };
|
|
25880
|
-
const pma = await will.save();
|
|
25881
|
-
mkdirSync(dirname(opts.pmaPath), { recursive: true });
|
|
25882
|
-
writeFileSync(opts.pmaPath, JSON.stringify(pma));
|
|
25883
|
-
return { content: [{ type: "text", text: `${will.name} checkpointed to ${opts.pmaPath} (still living).` }] };
|
|
25884
|
-
});
|
|
25885
|
-
server.registerResource(
|
|
25886
|
-
"state",
|
|
25887
|
-
"will://state",
|
|
25888
|
-
{ title: `${will.name} \u2014 inner state`, description: "Live snapshot of the mind: metrics, goals, beliefs, narrative.", mimeType: "application/json" },
|
|
25889
|
-
async (uri) => ({ contents: [{ uri: uri.href, mimeType: "application/json", text: JSON.stringify(will.state(), null, 2) }] })
|
|
25890
|
-
);
|
|
25891
|
-
server.registerResource(
|
|
25892
|
-
"narrative",
|
|
25893
|
-
"will://narrative",
|
|
25894
|
-
{ title: `${will.name} \u2014 self-narrative`, description: "The story the mind currently tells about itself.", mimeType: "text/plain" },
|
|
25895
|
-
async (uri) => ({ contents: [{ uri: uri.href, mimeType: "text/plain", text: will.state().narrative || "(no narrative formed yet)" }] })
|
|
25896
|
-
);
|
|
25897
|
-
return server;
|
|
25898
|
-
}
|
|
25899
25807
|
var RESULT_DESCRIPTION_CAP = 700;
|
|
25900
25808
|
var MEANING_CAP = 300;
|
|
25901
25809
|
function describeMcpTool(tool) {
|
|
@@ -25926,8 +25834,8 @@ function buildMcpHandler(client, tool) {
|
|
|
25926
25834
|
const text = (res.content ?? []).filter((c) => c.type === "text" && typeof c.text === "string").map((c) => c.text).join("\n").trim() || (res.isError ? "The tool reported an error." : "Done (no output).");
|
|
25927
25835
|
const bounded = text.length > RESULT_DESCRIPTION_CAP ? `${text.slice(0, RESULT_DESCRIPTION_CAP - 1)}\u2026` : text;
|
|
25928
25836
|
return { success: !res.isError, description: bounded };
|
|
25929
|
-
} catch (
|
|
25930
|
-
return { success: false, description: `${tool.name} failed: ${
|
|
25837
|
+
} catch (err) {
|
|
25838
|
+
return { success: false, description: `${tool.name} failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
25931
25839
|
}
|
|
25932
25840
|
};
|
|
25933
25841
|
}
|
|
@@ -25964,26 +25872,24 @@ async function connectMcpEffectors(will, source, opts = {}) {
|
|
|
25964
25872
|
} };
|
|
25965
25873
|
}
|
|
25966
25874
|
|
|
25967
|
-
// src/
|
|
25968
|
-
|
|
25969
|
-
|
|
25970
|
-
|
|
25875
|
+
// src/host/boot.ts
|
|
25876
|
+
function routeLogsToStderr() {
|
|
25877
|
+
const err = (level) => (msg, ...rest) => console.error(`[will:${level}] ${msg}`, ...rest);
|
|
25878
|
+
setLogger({ debug: () => {
|
|
25879
|
+
}, info: err("info"), warn: err("warn"), error: err("error") });
|
|
25880
|
+
}
|
|
25971
25881
|
function slug2(s) {
|
|
25972
25882
|
return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "will";
|
|
25973
25883
|
}
|
|
25974
|
-
async function
|
|
25975
|
-
const sub = process.argv[2];
|
|
25976
|
-
if (sub !== void 0 && sub !== "mcp") {
|
|
25977
|
-
console.error(`usage: will mcp (host a persistent mind over MCP stdio)
|
|
25978
|
-
unknown subcommand: ${sub}`);
|
|
25979
|
-
process.exit(2);
|
|
25980
|
-
}
|
|
25884
|
+
async function bootWillFromEnv() {
|
|
25981
25885
|
const name = process.env.WILL_NAME ?? "Will";
|
|
25982
25886
|
const pmaPath = resolve(process.env.WILL_PMA_PATH ?? `.will/${slug2(name)}.pma.json`);
|
|
25887
|
+
const tickMs = parseInt(process.env.WILL_TICK_MS ?? "1000");
|
|
25888
|
+
const engineTier = process.env.WILL_TIER ?? "standard";
|
|
25983
25889
|
const opts = {
|
|
25984
25890
|
name,
|
|
25985
|
-
engineTier
|
|
25986
|
-
tickMs
|
|
25891
|
+
engineTier,
|
|
25892
|
+
tickMs,
|
|
25987
25893
|
...process.env.WILL_LLM ? { llm: process.env.WILL_LLM } : {},
|
|
25988
25894
|
...process.env.WILL_SEED ? { seed: parseInt(process.env.WILL_SEED) } : {}
|
|
25989
25895
|
};
|
|
@@ -25991,57 +25897,324 @@ unknown subcommand: ${sub}`);
|
|
|
25991
25897
|
if (existsSync(pmaPath)) {
|
|
25992
25898
|
const pma = JSON.parse(readFileSync(pmaPath, "utf8"));
|
|
25993
25899
|
will = await Will.wake(pma, opts);
|
|
25994
|
-
console.error(`[will
|
|
25900
|
+
console.error(`[will] ${name} woke from ${pmaPath}`);
|
|
25995
25901
|
} else {
|
|
25996
25902
|
will = await Will.create({
|
|
25997
25903
|
...opts,
|
|
25998
|
-
identity: { prompt: process.env.WILL_IDENTITY ?? `I am ${name}, a persistent mind
|
|
25904
|
+
identity: { prompt: process.env.WILL_IDENTITY ?? `I am ${name}, a persistent mind.` }
|
|
25999
25905
|
});
|
|
26000
|
-
console.error(`[will
|
|
25906
|
+
console.error(`[will] ${name} born (no artifact at ${pmaPath} yet)`);
|
|
26001
25907
|
}
|
|
26002
|
-
will.on("error", (e) => console.error(`[will
|
|
26003
|
-
const
|
|
25908
|
+
will.on("error", (e) => console.error(`[will] error: ${e.message}`));
|
|
25909
|
+
const cleanups = [];
|
|
26004
25910
|
if (process.env.WILL_MCP_SERVERS) {
|
|
26005
25911
|
try {
|
|
26006
25912
|
const sources = JSON.parse(process.env.WILL_MCP_SERVERS);
|
|
26007
25913
|
for (const source of Array.isArray(sources) ? sources : []) {
|
|
26008
25914
|
try {
|
|
26009
25915
|
const { names, close } = await connectMcpEffectors(will, source);
|
|
26010
|
-
|
|
26011
|
-
console.error(`[will
|
|
25916
|
+
cleanups.push(close);
|
|
25917
|
+
console.error(`[will] ${name} gained abilities: ${names.join(", ")}`);
|
|
26012
25918
|
} catch (e) {
|
|
26013
|
-
console.error(`[will
|
|
25919
|
+
console.error(`[will] MCP bridge failed (skipped): ${e.message}`);
|
|
26014
25920
|
}
|
|
26015
25921
|
}
|
|
26016
25922
|
} catch (e) {
|
|
26017
|
-
console.error(`[will
|
|
25923
|
+
console.error(`[will] WILL_MCP_SERVERS is not valid JSON \u2014 ignoring: ${e.message}`);
|
|
26018
25924
|
}
|
|
26019
25925
|
}
|
|
26020
25926
|
let leaving = false;
|
|
26021
25927
|
const shutdown = async (why) => {
|
|
26022
25928
|
if (leaving) return;
|
|
26023
25929
|
leaving = true;
|
|
26024
|
-
for (const
|
|
25930
|
+
for (const fn of cleanups.reverse()) await Promise.resolve(fn()).catch(() => {
|
|
26025
25931
|
});
|
|
26026
25932
|
try {
|
|
26027
25933
|
const pma = await will.hibernate();
|
|
26028
25934
|
mkdirSync(dirname(pmaPath), { recursive: true });
|
|
26029
25935
|
writeFileSync(pmaPath, JSON.stringify(pma));
|
|
26030
|
-
console.error(`[will
|
|
25936
|
+
console.error(`[will] ${name} hibernated to ${pmaPath} (${why})`);
|
|
26031
25937
|
} catch (e) {
|
|
26032
|
-
console.error(`[will
|
|
25938
|
+
console.error(`[will] hibernate failed: ${e.message}`);
|
|
26033
25939
|
}
|
|
26034
25940
|
process.exit(0);
|
|
26035
25941
|
};
|
|
26036
25942
|
process.on("SIGINT", () => void shutdown("SIGINT"));
|
|
26037
25943
|
process.on("SIGTERM", () => void shutdown("SIGTERM"));
|
|
26038
|
-
|
|
26039
|
-
|
|
26040
|
-
|
|
26041
|
-
|
|
25944
|
+
return {
|
|
25945
|
+
will,
|
|
25946
|
+
name,
|
|
25947
|
+
pmaPath,
|
|
25948
|
+
tickMs,
|
|
25949
|
+
engineTier: engineTier ?? "standard",
|
|
25950
|
+
onCleanup: (fn) => cleanups.push(fn),
|
|
25951
|
+
shutdown
|
|
25952
|
+
};
|
|
25953
|
+
}
|
|
25954
|
+
|
|
25955
|
+
// src/host/utterances.ts
|
|
25956
|
+
var BUFFER_CAP = 50;
|
|
25957
|
+
var UtteranceTap = class {
|
|
25958
|
+
_will;
|
|
25959
|
+
_pending = [];
|
|
25960
|
+
constructor(will) {
|
|
25961
|
+
this._will = will;
|
|
25962
|
+
will.on("message", (m) => {
|
|
25963
|
+
this._pending.push(m);
|
|
25964
|
+
if (this._pending.length > BUFFER_CAP) this._pending.shift();
|
|
25965
|
+
});
|
|
25966
|
+
}
|
|
25967
|
+
/** Consume the oldest buffered utterance (optionally only one addressed to `to`). */
|
|
25968
|
+
takeBuffered(to) {
|
|
25969
|
+
if (this._pending.length === 0) return void 0;
|
|
25970
|
+
const i = to === void 0 ? 0 : this._pending.findIndex((m) => m.to === to);
|
|
25971
|
+
if (i < 0) return void 0;
|
|
25972
|
+
return this._pending.splice(i, 1)[0];
|
|
25973
|
+
}
|
|
25974
|
+
/**
|
|
25975
|
+
* The next utterance: a buffered one if a projection already landed, else
|
|
25976
|
+
* await up to `within` ms. `null` = the Will chose silence. An awaited
|
|
25977
|
+
* message is also consumed from the buffer so it never replays.
|
|
25978
|
+
*/
|
|
25979
|
+
async next(within, to) {
|
|
25980
|
+
const buffered = this.takeBuffered(to);
|
|
25981
|
+
if (buffered) return buffered;
|
|
25982
|
+
const msg = await this._will.nextUtterance({ within, ...to ? { to } : {} });
|
|
25983
|
+
if (msg) {
|
|
25984
|
+
const i = this._pending.findIndex((p) => p.id === msg.id);
|
|
25985
|
+
if (i >= 0) this._pending.splice(i, 1);
|
|
25986
|
+
}
|
|
25987
|
+
return msg;
|
|
25988
|
+
}
|
|
25989
|
+
};
|
|
25990
|
+
|
|
25991
|
+
// src/mcp/server.ts
|
|
25992
|
+
function serverVersion() {
|
|
25993
|
+
try {
|
|
25994
|
+
return JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf8")).version ?? "0.0.0";
|
|
25995
|
+
} catch {
|
|
25996
|
+
return "0.0.0";
|
|
25997
|
+
}
|
|
25998
|
+
}
|
|
25999
|
+
function buildWillMcpServer(will, opts = {}) {
|
|
26000
|
+
const server = new McpServer({ name: "mindot-will", version: serverVersion() });
|
|
26001
|
+
const tap = new UtteranceTap(will);
|
|
26002
|
+
server.registerTool("perceive", {
|
|
26003
|
+
title: "Perceive",
|
|
26004
|
+
description: `Deliver a stimulus (something said or observed) into ${will.name}'s sensory field. ${will.name} is a living mind, not a chatbot: this returns once the stimulus is delivered, NOT when it has responded. Its response \u2014 if it chooses to give one \u2014 arrives as its next utterance (use the next_utterance tool). Staying silent is a valid choice, not an error.`,
|
|
26005
|
+
inputSchema: {
|
|
26006
|
+
text: z.string().describe("What is said or observed."),
|
|
26007
|
+
from: z.string().optional().describe("Who it's from (entity id, default 'user'). Use a stable id per person."),
|
|
26008
|
+
speaker: z.string().optional().describe("Display name of the speaker.")
|
|
26009
|
+
}
|
|
26010
|
+
}, async ({ text, from, speaker }) => {
|
|
26011
|
+
await will.perceive({ text, ...from ? { from } : {}, ...speaker ? { speaker } : {} });
|
|
26012
|
+
return {
|
|
26013
|
+
content: [{
|
|
26014
|
+
type: "text",
|
|
26015
|
+
text: `Delivered into ${will.name}'s sensory field at tick ${will.state().tick}. It may respond in its next utterance \u2014 or stay silent.`
|
|
26016
|
+
}]
|
|
26017
|
+
};
|
|
26018
|
+
});
|
|
26019
|
+
server.registerTool("next_utterance", {
|
|
26020
|
+
title: "Next utterance",
|
|
26021
|
+
description: `Await ${will.name}'s next spontaneous utterance. Returns what it says next, or reports that it chose silence within the wait window \u2014 silence is a real outcome, not a failure. Call after perceive to hear the response, or on its own to listen for unprompted speech.`,
|
|
26022
|
+
inputSchema: {
|
|
26023
|
+
within_ms: z.number().optional().describe("How long to wait before accepting silence (default 15000, max 120000)."),
|
|
26024
|
+
from: z.string().optional().describe("Only accept an utterance addressed to this entity id.")
|
|
26025
|
+
}
|
|
26026
|
+
}, async ({ within_ms, from }) => {
|
|
26027
|
+
const within = Math.min(Math.max(within_ms ?? 15e3, 100), 12e4);
|
|
26028
|
+
const msg = await tap.next(within, from);
|
|
26029
|
+
if (msg)
|
|
26030
|
+
return { content: [{ type: "text", text: `${will.name} says (to ${msg.to}): ${msg.content}` }] };
|
|
26031
|
+
return {
|
|
26032
|
+
content: [{
|
|
26033
|
+
type: "text",
|
|
26034
|
+
text: `${will.name} stayed silent (waited ${within}ms). That is a choice, not an error \u2014 it may be occupied with its own thoughts, or simply have nothing to say.`
|
|
26035
|
+
}]
|
|
26036
|
+
};
|
|
26037
|
+
});
|
|
26038
|
+
server.registerTool("state", {
|
|
26039
|
+
title: "Inner state",
|
|
26040
|
+
description: `Read a snapshot of ${will.name}'s current inner life: tick, body/affect metrics (energy, stress, valence, arousal), active goals, beliefs, and self-narrative. Read-only; observing does not disturb it.`,
|
|
26041
|
+
inputSchema: {}
|
|
26042
|
+
}, async () => ({ content: [{ type: "text", text: JSON.stringify(will.state(), null, 2) }] }));
|
|
26043
|
+
server.registerTool("save", {
|
|
26044
|
+
title: "Save (checkpoint)",
|
|
26045
|
+
description: `Checkpoint ${will.name} into a portable PMA artifact on disk \u2014 non-destructive, it keeps living. The artifact restores the same self (identity, memories, relationships, learned skills) when the server next starts.`,
|
|
26046
|
+
inputSchema: {}
|
|
26047
|
+
}, async () => {
|
|
26048
|
+
if (!opts.pmaPath)
|
|
26049
|
+
return { content: [{ type: "text", text: "No PMA path configured \u2014 set WILL_PMA_PATH." }], isError: true };
|
|
26050
|
+
const pma = await will.save();
|
|
26051
|
+
mkdirSync(dirname(opts.pmaPath), { recursive: true });
|
|
26052
|
+
writeFileSync(opts.pmaPath, JSON.stringify(pma));
|
|
26053
|
+
return { content: [{ type: "text", text: `${will.name} checkpointed to ${opts.pmaPath} (still living).` }] };
|
|
26054
|
+
});
|
|
26055
|
+
server.registerResource(
|
|
26056
|
+
"state",
|
|
26057
|
+
"will://state",
|
|
26058
|
+
{ title: `${will.name} \u2014 inner state`, description: "Live snapshot of the mind: metrics, goals, beliefs, narrative.", mimeType: "application/json" },
|
|
26059
|
+
async (uri) => ({ contents: [{ uri: uri.href, mimeType: "application/json", text: JSON.stringify(will.state(), null, 2) }] })
|
|
26060
|
+
);
|
|
26061
|
+
server.registerResource(
|
|
26062
|
+
"narrative",
|
|
26063
|
+
"will://narrative",
|
|
26064
|
+
{ title: `${will.name} \u2014 self-narrative`, description: "The story the mind currently tells about itself.", mimeType: "text/plain" },
|
|
26065
|
+
async (uri) => ({ contents: [{ uri: uri.href, mimeType: "text/plain", text: will.state().narrative || "(no narrative formed yet)" }] })
|
|
26066
|
+
);
|
|
26067
|
+
return server;
|
|
26068
|
+
}
|
|
26069
|
+
var SSE_HEARTBEAT_MS = 15e3;
|
|
26070
|
+
function json(res, status, body) {
|
|
26071
|
+
const text = JSON.stringify(body);
|
|
26072
|
+
res.writeHead(status, { "content-type": "application/json", "access-control-allow-origin": "*" });
|
|
26073
|
+
res.end(text);
|
|
26074
|
+
}
|
|
26075
|
+
async function readJsonBody(req) {
|
|
26076
|
+
const chunks = [];
|
|
26077
|
+
for await (const c of req) chunks.push(c);
|
|
26078
|
+
const raw = Buffer.concat(chunks).toString("utf8").trim();
|
|
26079
|
+
if (!raw) return {};
|
|
26080
|
+
return JSON.parse(raw);
|
|
26081
|
+
}
|
|
26082
|
+
function buildWillHttpServer(will, opts = {}) {
|
|
26083
|
+
const tap = new UtteranceTap(will);
|
|
26084
|
+
const born = Date.now();
|
|
26085
|
+
const streams = /* @__PURE__ */ new Set();
|
|
26086
|
+
const fanout = (event, data) => {
|
|
26087
|
+
const frame = `event: ${event}
|
|
26088
|
+
data: ${JSON.stringify(data)}
|
|
26089
|
+
|
|
26090
|
+
`;
|
|
26091
|
+
for (const res of streams) res.write(frame);
|
|
26092
|
+
};
|
|
26093
|
+
will.on("message", (m) => fanout("utterance", m));
|
|
26094
|
+
will.on("emotion", (a) => fanout("emotion", a));
|
|
26095
|
+
will.on("effector", (a) => fanout("action", a));
|
|
26096
|
+
const server = createServer((req, res) => {
|
|
26097
|
+
void handle(req, res).catch((err) => {
|
|
26098
|
+
if (!res.headersSent)
|
|
26099
|
+
json(res, 500, { error: err instanceof Error ? err.message : String(err) });
|
|
26100
|
+
else res.end();
|
|
26101
|
+
});
|
|
26102
|
+
});
|
|
26103
|
+
async function handle(req, res) {
|
|
26104
|
+
const url = new URL(req.url ?? "/", "http://sidecar");
|
|
26105
|
+
const route = `${req.method} ${url.pathname}`;
|
|
26106
|
+
if (req.method === "OPTIONS") {
|
|
26107
|
+
res.writeHead(204, {
|
|
26108
|
+
"access-control-allow-origin": "*",
|
|
26109
|
+
"access-control-allow-methods": "GET, POST, OPTIONS",
|
|
26110
|
+
"access-control-allow-headers": "content-type"
|
|
26111
|
+
});
|
|
26112
|
+
res.end();
|
|
26113
|
+
return;
|
|
26114
|
+
}
|
|
26115
|
+
switch (route) {
|
|
26116
|
+
case "GET /health":
|
|
26117
|
+
return json(res, 200, { ok: true, name: will.name, tick: will.state().tick, uptimeMs: Date.now() - born });
|
|
26118
|
+
case "GET /state":
|
|
26119
|
+
return json(res, 200, will.state());
|
|
26120
|
+
case "POST /perceive": {
|
|
26121
|
+
const body = await readJsonBody(req);
|
|
26122
|
+
const text = typeof body.text === "string" ? body.text : "";
|
|
26123
|
+
if (!text) return json(res, 400, { error: "text is required" });
|
|
26124
|
+
await will.perceive({
|
|
26125
|
+
text,
|
|
26126
|
+
...typeof body.from === "string" ? { from: body.from } : {},
|
|
26127
|
+
...typeof body.speaker === "string" ? { speaker: body.speaker } : {}
|
|
26128
|
+
});
|
|
26129
|
+
return json(res, 202, { delivered: true, tick: will.state().tick });
|
|
26130
|
+
}
|
|
26131
|
+
case "GET /next-utterance": {
|
|
26132
|
+
const within = Math.min(Math.max(parseInt(url.searchParams.get("within_ms") ?? "15000") || 15e3, 100), 12e4);
|
|
26133
|
+
const from = url.searchParams.get("from") ?? void 0;
|
|
26134
|
+
const msg = await tap.next(within, from);
|
|
26135
|
+
return msg ? json(res, 200, { utterance: msg }) : json(res, 200, { silence: true, waitedMs: within });
|
|
26136
|
+
}
|
|
26137
|
+
case "GET /utterances": {
|
|
26138
|
+
res.writeHead(200, {
|
|
26139
|
+
"content-type": "text/event-stream",
|
|
26140
|
+
"cache-control": "no-cache",
|
|
26141
|
+
"connection": "keep-alive",
|
|
26142
|
+
"access-control-allow-origin": "*"
|
|
26143
|
+
});
|
|
26144
|
+
res.write(`event: hello
|
|
26145
|
+
data: ${JSON.stringify({ name: will.name, tick: will.state().tick })}
|
|
26146
|
+
|
|
26147
|
+
`);
|
|
26148
|
+
streams.add(res);
|
|
26149
|
+
const heartbeat = setInterval(() => res.write(`: tick ${will.state().tick}
|
|
26150
|
+
|
|
26151
|
+
`), SSE_HEARTBEAT_MS);
|
|
26152
|
+
req.on("close", () => {
|
|
26153
|
+
clearInterval(heartbeat);
|
|
26154
|
+
streams.delete(res);
|
|
26155
|
+
});
|
|
26156
|
+
return;
|
|
26157
|
+
}
|
|
26158
|
+
case "POST /save": {
|
|
26159
|
+
if (!opts.pmaPath) return json(res, 409, { error: "no PMA path configured \u2014 set WILL_PMA_PATH" });
|
|
26160
|
+
const pma = await will.save();
|
|
26161
|
+
mkdirSync(dirname(opts.pmaPath), { recursive: true });
|
|
26162
|
+
writeFileSync(opts.pmaPath, JSON.stringify(pma));
|
|
26163
|
+
return json(res, 200, { saved: true, path: opts.pmaPath });
|
|
26164
|
+
}
|
|
26165
|
+
default:
|
|
26166
|
+
return json(res, 404, {
|
|
26167
|
+
error: `no such route: ${route}`,
|
|
26168
|
+
routes: ["GET /health", "GET /state", "POST /perceive", "GET /next-utterance", "GET /utterances (SSE)", "POST /save"]
|
|
26169
|
+
});
|
|
26170
|
+
}
|
|
26171
|
+
}
|
|
26172
|
+
server.on("close", () => {
|
|
26173
|
+
for (const res of streams) res.end();
|
|
26174
|
+
streams.clear();
|
|
26175
|
+
});
|
|
26176
|
+
return server;
|
|
26177
|
+
}
|
|
26178
|
+
|
|
26179
|
+
// src/cli.ts
|
|
26180
|
+
routeLogsToStderr();
|
|
26181
|
+
var USAGE = `usage: will <mcp | serve>
|
|
26182
|
+
|
|
26183
|
+
mcp host a persistent mind over MCP stdio (Claude Desktop / Claude Code)
|
|
26184
|
+
serve host a persistent mind over HTTP (any language; WILL_PORT, default 7777)
|
|
26185
|
+
|
|
26186
|
+
Shared env: WILL_NAME, WILL_IDENTITY, WILL_TIER, WILL_LLM, WILL_TICK_MS,
|
|
26187
|
+
WILL_SEED, WILL_PMA_PATH, WILL_MCP_SERVERS. The mind persists across runs via
|
|
26188
|
+
its PMA artifact.`;
|
|
26189
|
+
async function main() {
|
|
26190
|
+
const sub = process.argv[2];
|
|
26191
|
+
if (sub !== "mcp" && sub !== "serve") {
|
|
26192
|
+
console.error(sub ? `unknown subcommand: ${sub}
|
|
26193
|
+
|
|
26194
|
+
${USAGE}` : USAGE);
|
|
26195
|
+
process.exit(sub ? 2 : 0);
|
|
26196
|
+
}
|
|
26197
|
+
const { will, name, pmaPath, tickMs, engineTier, onCleanup, shutdown } = await bootWillFromEnv();
|
|
26198
|
+
if (sub === "mcp") {
|
|
26199
|
+
process.stdin.on("end", () => void shutdown("client disconnected"));
|
|
26200
|
+
const server2 = buildWillMcpServer(will, { pmaPath });
|
|
26201
|
+
await server2.connect(new StdioServerTransport());
|
|
26202
|
+
console.error(`[will] ${name} is listening on MCP stdio (tick ${tickMs}ms, tier ${engineTier})`);
|
|
26203
|
+
return;
|
|
26204
|
+
}
|
|
26205
|
+
const port = parseInt(process.env.WILL_PORT ?? "7777");
|
|
26206
|
+
const host = process.env.WILL_HOST ?? "127.0.0.1";
|
|
26207
|
+
const server = buildWillHttpServer(will, { pmaPath });
|
|
26208
|
+
onCleanup(() => new Promise((r) => server.close(() => r())));
|
|
26209
|
+
await new Promise((resolve2, reject) => {
|
|
26210
|
+
server.once("error", reject);
|
|
26211
|
+
server.listen(port, host, () => resolve2());
|
|
26212
|
+
});
|
|
26213
|
+
console.error(`[will] ${name} is listening on http://${host}:${port} (tick ${tickMs}ms, tier ${engineTier})`);
|
|
26214
|
+
console.error(`[will] try: curl -X POST http://${host}:${port}/perceive -H 'content-type: application/json' -d '{"text":"Hello"}'`);
|
|
26042
26215
|
}
|
|
26043
26216
|
main().catch((e) => {
|
|
26044
|
-
console.error(`[will
|
|
26217
|
+
console.error(`[will] fatal: ${e instanceof Error ? e.stack ?? e.message : String(e)}`);
|
|
26045
26218
|
process.exit(1);
|
|
26046
26219
|
});
|
|
26047
26220
|
//# sourceMappingURL=cli.js.map
|