@mindot/will 0.4.0 → 0.5.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 +32 -12
- package/dist/channels/discord.d.ts +2 -11
- package/dist/channels/whatsapp.d.ts +72 -0
- package/dist/channels/whatsapp.js +252 -0
- package/dist/channels/whatsapp.js.map +1 -0
- package/dist/cli.js +270 -40
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +53 -28
- package/dist/index.js.map +1 -1
- package/dist/mcp/effectors.d.ts +1 -1
- package/dist/types-E9-HV-SW.d.ts +11 -0
- package/dist/{will-D-slky1N.d.ts → will-BDq-TMQr.d.ts} +8 -6
- package/package.json +12 -3
- package/src/channels/whatsapp.ts +318 -0
- package/src/cli.ts +32 -9
- package/src/cognition/faculties/executive.engine/engine.ts +3 -2
- package/src/cognition/utilities/token.tracker.ts +6 -0
- package/src/host/boot.ts +32 -10
- package/src/llm/index.ts +75 -25
- package/src/sdk/will.ts +16 -7
- package/src/stem/guards/identity.coherence.ts +4 -3
package/src/llm/index.ts
CHANGED
|
@@ -12,7 +12,68 @@ import type { LLMCompletionRecord } from '#core/completion.recorder'
|
|
|
12
12
|
import { withGate } from '#llm/gate'
|
|
13
13
|
import { matchConversationFocus, wrapReplyText } from '#llm/wire.contracts'
|
|
14
14
|
|
|
15
|
-
export type LLMProvider = 'anthropic' | 'deepseek' | 'openai' | 'google'
|
|
15
|
+
export type LLMProvider = 'anthropic' | 'glm' | 'deepseek' | 'openai' | 'google'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Providers that speak the Anthropic Messages wire.
|
|
19
|
+
*
|
|
20
|
+
* Z.ai ships a real Anthropic-compatible endpoint for GLM — it is what Claude
|
|
21
|
+
* Code itself targets — so GLM rides this path rather than the OpenAI scaffold.
|
|
22
|
+
* That buys it everything the path already has: token streaming, the first-byte
|
|
23
|
+
* deadline, prompt-cache breakpoints, and the structured-output contract. GLM is
|
|
24
|
+
* therefore a second *production* provider, not a fifth scaffold.
|
|
25
|
+
*/
|
|
26
|
+
const ANTHROPIC_WIRE = new Set<LLMProvider>( [ 'anthropic', 'glm' ] )
|
|
27
|
+
|
|
28
|
+
/** Does this provider accept Anthropic-shaped requests? */
|
|
29
|
+
export function speaksAnthropicWire( provider: LLMProvider ): boolean {
|
|
30
|
+
return ANTHROPIC_WIRE.has( provider )
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Official API base URL (including version segment) for a provider. */
|
|
34
|
+
export function defaultBaseFor( provider: LLMProvider ): string {
|
|
35
|
+
switch( provider ){
|
|
36
|
+
case 'anthropic': return 'https://api.anthropic.com/v1'
|
|
37
|
+
// Z.ai documents the base as `…/api/anthropic` because the Anthropic SDK
|
|
38
|
+
// appends `/v1/messages`; this client appends `/messages`, so the version
|
|
39
|
+
// segment belongs here — verified against the live endpoint.
|
|
40
|
+
case 'glm': return 'https://api.z.ai/api/anthropic/v1'
|
|
41
|
+
case 'openai': return 'https://api.openai.com/v1'
|
|
42
|
+
case 'deepseek': return 'https://api.deepseek.com/v1'
|
|
43
|
+
case 'google': return 'https://generativelanguage.googleapis.com/v1beta'
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The model the executive recruits when none is pinned.
|
|
49
|
+
*
|
|
50
|
+
* Provider-specific because the default is *sent* — a GLM Will with no
|
|
51
|
+
* `WILL_LLM_MODEL` would otherwise ask Z.ai for a Claude id and get a 404 it
|
|
52
|
+
* could do nothing with. The scaffolded providers (openai/deepseek/google) keep
|
|
53
|
+
* today's value: they need an explicit `WILL_LLM_MODEL` to work at all, and
|
|
54
|
+
* inventing ids for them here would look like support that does not exist.
|
|
55
|
+
*/
|
|
56
|
+
export function defaultModelFor( provider: LLMProvider ): string {
|
|
57
|
+
return provider === 'glm' ? 'glm-5.2' : 'claude-sonnet-4-5-20250929'
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Auth + version headers for the Anthropic wire.
|
|
62
|
+
*
|
|
63
|
+
* Anthropic authenticates with `x-api-key`. Z.ai's compat endpoint accepts
|
|
64
|
+
* either that or the `Authorization: Bearer` its own docs describe (both were
|
|
65
|
+
* probed against the live endpoint; each is read and validated). GLM sends both
|
|
66
|
+
* — same secret, same host — so the mind keeps working whichever one Z.ai
|
|
67
|
+
* eventually settles on.
|
|
68
|
+
*/
|
|
69
|
+
export function anthropicWireHeaders( provider: LLMProvider, apiKey: string ): Record<string, string> {
|
|
70
|
+
return {
|
|
71
|
+
'Content-Type': 'application/json',
|
|
72
|
+
'anthropic-version': '2023-06-01',
|
|
73
|
+
'x-api-key': apiKey,
|
|
74
|
+
...( provider === 'glm' ? { Authorization: `Bearer ${ apiKey }` } : {} ),
|
|
75
|
+
}
|
|
76
|
+
}
|
|
16
77
|
export interface LLMDirectorConfig {
|
|
17
78
|
willId: string
|
|
18
79
|
model: string
|
|
@@ -216,7 +277,7 @@ export class LLMDirector {
|
|
|
216
277
|
return result
|
|
217
278
|
}
|
|
218
279
|
|
|
219
|
-
const result = this._provider
|
|
280
|
+
const result = speaksAnthropicWire( this._provider )
|
|
220
281
|
? await this._callAnthropicStream( systemPrompt, userMessage, onChunk, temperature )
|
|
221
282
|
: await ( async () => {
|
|
222
283
|
// Other providers: fall back to regular call, emit whole response as one chunk
|
|
@@ -328,11 +389,7 @@ export class LLMDirector {
|
|
|
328
389
|
try {
|
|
329
390
|
res = await fetch(`${this._resolvedBase()}/messages`, {
|
|
330
391
|
method: 'POST',
|
|
331
|
-
headers:
|
|
332
|
-
'Content-Type': 'application/json',
|
|
333
|
-
'anthropic-version': '2023-06-01',
|
|
334
|
-
'x-api-key': this._apiKey,
|
|
335
|
-
},
|
|
392
|
+
headers: anthropicWireHeaders( this._provider, this._apiKey ),
|
|
336
393
|
body: JSON.stringify({
|
|
337
394
|
model: this._model,
|
|
338
395
|
max_tokens: this._maxOutputTokens,
|
|
@@ -438,13 +495,14 @@ export class LLMDirector {
|
|
|
438
495
|
return result
|
|
439
496
|
}
|
|
440
497
|
|
|
441
|
-
// Anthropic
|
|
442
|
-
// (TTFT), not whole-request: a long-but-healthy
|
|
443
|
-
// 20–40s on Sonnet) no longer trips the timeout
|
|
444
|
-
// no-op here — call() returns the full
|
|
445
|
-
// through callStream(). Other
|
|
498
|
+
// The Anthropic-wire providers route through the streaming path so the
|
|
499
|
+
// deadline is first-byte (TTFT), not whole-request: a long-but-healthy
|
|
500
|
+
// executive completion (often 20–40s on Sonnet) no longer trips the timeout
|
|
501
|
+
// mid-generation. onChunk is a no-op here — call() returns the full
|
|
502
|
+
// accumulated text; live token chunks go through callStream(). Other
|
|
503
|
+
// providers keep the whole-request deadline.
|
|
446
504
|
const result = await withGate(
|
|
447
|
-
() => this._provider
|
|
505
|
+
() => speaksAnthropicWire( this._provider )
|
|
448
506
|
? this._callAnthropicStream( systemPrompt, userMessage, () => {}, temperature )
|
|
449
507
|
: this._callProvider( systemPrompt, userMessage, temperature ),
|
|
450
508
|
'executive/direct',
|
|
@@ -465,6 +523,7 @@ export class LLMDirector {
|
|
|
465
523
|
): Promise<LLMCallResult> {
|
|
466
524
|
switch( this._provider ){
|
|
467
525
|
case 'anthropic': return this._callAnthropic( systemPrompt, userMessage, temperature )
|
|
526
|
+
case 'glm': return this._callAnthropic( systemPrompt, userMessage, temperature )
|
|
468
527
|
case 'deepseek': return this._callOpenAI( systemPrompt, userMessage, temperature )
|
|
469
528
|
case 'openai': return this._callOpenAI( systemPrompt, userMessage, temperature )
|
|
470
529
|
case 'google': return this._callGoogle( systemPrompt, userMessage, temperature )
|
|
@@ -474,12 +533,7 @@ export class LLMDirector {
|
|
|
474
533
|
|
|
475
534
|
/** Default API base URL (including version segment) for a provider. */
|
|
476
535
|
private _baseFor( provider: LLMProvider ): string {
|
|
477
|
-
|
|
478
|
-
case 'anthropic': return 'https://api.anthropic.com/v1'
|
|
479
|
-
case 'openai': return 'https://api.openai.com/v1'
|
|
480
|
-
case 'deepseek': return 'https://api.deepseek.com/v1'
|
|
481
|
-
case 'google': return 'https://generativelanguage.googleapis.com/v1beta'
|
|
482
|
-
}
|
|
536
|
+
return defaultBaseFor( provider )
|
|
483
537
|
}
|
|
484
538
|
|
|
485
539
|
/** Resolved API base: explicit override wins, else the provider default. */
|
|
@@ -526,16 +580,12 @@ export class LLMDirector {
|
|
|
526
580
|
|
|
527
581
|
const res = await this._fetchWithTimeout(`${this._resolvedBase()}/messages`, {
|
|
528
582
|
method: 'POST',
|
|
529
|
-
headers:
|
|
530
|
-
'Content-Type': 'application/json',
|
|
531
|
-
'anthropic-version': '2023-06-01',
|
|
532
|
-
'x-api-key': this._apiKey
|
|
533
|
-
},
|
|
583
|
+
headers: anthropicWireHeaders( this._provider, this._apiKey ),
|
|
534
584
|
body: JSON.stringify( body )
|
|
535
585
|
})
|
|
536
586
|
|
|
537
587
|
if( !res.ok )
|
|
538
|
-
throw new Error(
|
|
588
|
+
throw new Error(`${this._provider} API ${res.status}: ${( await res.text() ).slice(0, 300)}`)
|
|
539
589
|
|
|
540
590
|
const
|
|
541
591
|
data = await res.json() as {
|
package/src/sdk/will.ts
CHANGED
|
@@ -171,12 +171,14 @@ export interface CreateWillOptions {
|
|
|
171
171
|
* Unset fields fall back to WILL_LLM_* envs. apiKey stays in memory only.
|
|
172
172
|
* (Named llmConfig because `llm` is the mock/anthropic MODE switch.) */
|
|
173
173
|
llmConfig?: WillLLMConfig
|
|
174
|
-
|
|
175
|
-
* LLM mode. 'mock' (default when no
|
|
176
|
-
* canned executive — zero keys, zero cost. 'anthropic' calls
|
|
177
|
-
*
|
|
174
|
+
/**
|
|
175
|
+
* LLM mode. 'mock' (default when no key is present) runs a deterministic
|
|
176
|
+
* canned executive — zero keys, zero cost. 'anthropic' calls Claude (needs
|
|
177
|
+
* ANTHROPIC_API_KEY / WILL_LLM_* env); 'glm' calls Z.ai's GLM over its
|
|
178
|
+
* Anthropic-compatible endpoint (needs ZAI_API_KEY / WILL_LLM_*). Omit to
|
|
179
|
+
* auto-detect from whichever key is set.
|
|
178
180
|
*/
|
|
179
|
-
llm?: 'mock' | 'anthropic'
|
|
181
|
+
llm?: 'mock' | 'anthropic' | 'glm'
|
|
180
182
|
/**
|
|
181
183
|
* Abilities the Will can choose to enact. `name → handler`, or
|
|
182
184
|
* `name → { handler, description?, cost?, valence?, preconditions? }` to seed
|
|
@@ -459,7 +461,14 @@ export class Will {
|
|
|
459
461
|
// ── Internals ──────────────────────────────────────────────
|
|
460
462
|
|
|
461
463
|
private _buildConfig( id: string, opts: CreateWillOptions ): WillConfig {
|
|
462
|
-
|
|
464
|
+
// Auto-detect from whichever provider key is present; explicit `llm` wins.
|
|
465
|
+
const mode = opts.llm
|
|
466
|
+
?? ( process.env.ANTHROPIC_API_KEY ? 'anthropic' : process.env.ZAI_API_KEY ? 'glm' : 'mock' )
|
|
467
|
+
const useMock = mode === 'mock'
|
|
468
|
+
// `llm: 'glm'` selects the provider; an explicit llmConfig still overrides it.
|
|
469
|
+
const llmConfig = mode === 'glm'
|
|
470
|
+
? { provider: 'glm' as const, ...opts.llmConfig }
|
|
471
|
+
: opts.llmConfig
|
|
463
472
|
return {
|
|
464
473
|
id, name: opts.name,
|
|
465
474
|
identity: {
|
|
@@ -470,7 +479,7 @@ export class Will {
|
|
|
470
479
|
},
|
|
471
480
|
anatomy: opts.anatomy ?? 'mind',
|
|
472
481
|
model: opts.model,
|
|
473
|
-
llm:
|
|
482
|
+
llm: llmConfig,
|
|
474
483
|
testMode: useMock,
|
|
475
484
|
persistentMemory: opts.persist ?? false,
|
|
476
485
|
snapshotInterval: 100,
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
// ─────────────────────────────────────────────────────────────
|
|
19
19
|
|
|
20
20
|
import type { WillIdentity } from '#stem/mind'
|
|
21
|
-
import { LLMDirector, type LLMProvider, type LLMCallMeta } from '#llm/index'
|
|
21
|
+
import { LLMDirector, defaultModelFor, type LLMProvider, type LLMCallMeta } from '#llm/index'
|
|
22
22
|
import type { TokenTracker } from '#cognition/utilities/token.tracker'
|
|
23
23
|
|
|
24
24
|
export interface CoherenceIssue {
|
|
@@ -112,12 +112,13 @@ export async function reviewIdentityCoherence(
|
|
|
112
112
|
input: CoherenceInput,
|
|
113
113
|
opts: { willId?: string; tokenTracker?: TokenTracker | null } = {},
|
|
114
114
|
): Promise<CoherenceResult> {
|
|
115
|
+
const provider = ( process.env.WILL_LLM_PROVIDER ?? 'anthropic' ) as LLMProvider
|
|
115
116
|
const director = new LLMDirector({
|
|
116
117
|
willId: opts.willId ?? 'identity-coherence',
|
|
117
|
-
model: process.env.WILL_LLM_MODEL ??
|
|
118
|
+
model: process.env.WILL_LLM_MODEL ?? defaultModelFor( provider ),
|
|
118
119
|
maxOutputTokens: 512,
|
|
119
120
|
apiKey: process.env.WILL_LLM_API_KEY ?? process.env.ANTHROPIC_API_KEY ?? '',
|
|
120
|
-
provider
|
|
121
|
+
provider,
|
|
121
122
|
sessionLogger: null,
|
|
122
123
|
baseUrl: process.env.WILL_LLM_BASE_URL ?? process.env.OPENAI_BASE_URL,
|
|
123
124
|
// When a per-Will tracker is supplied, the creation-time review records under
|