@mindot/will 0.5.0 → 0.7.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/dist/channels/discord.d.ts +1 -1
- package/dist/channels/discord.js.map +1 -1
- package/dist/channels/whatsapp.d.ts +1 -1
- package/dist/channels/whatsapp.js.map +1 -1
- package/dist/cli.js +998 -59
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +998 -59
- package/dist/index.js.map +1 -1
- package/dist/mcp/effectors.d.ts +1 -1
- package/dist/mcp/effectors.js.map +1 -1
- package/dist/{will-BDq-TMQr.d.ts → will-DAW0l-lY.d.ts} +90 -10
- package/package.json +1 -1
- package/src/channels/discord.ts +11 -11
- package/src/channels/roster.ts +1 -1
- package/src/channels/types.ts +1 -1
- package/src/channels/whatsapp.ts +25 -25
- package/src/cli.ts +20 -20
- package/src/cognition/acp.ts +28 -0
- package/src/cognition/agency/consequence.ts +174 -0
- package/src/cognition/agency/engines/action.selector.ts +333 -48
- package/src/cognition/agency/engines/affordance.synthesizer.ts +35 -16
- package/src/cognition/agency/engines/deliberation.engine.ts +28 -14
- package/src/cognition/agency/engines/motor.schema.executor.ts +90 -14
- package/src/cognition/agency/engines/reafference.engine.ts +152 -23
- package/src/cognition/agency/execution.primitives.ts +13 -13
- package/src/cognition/agency/proactive.communicator.ts +1 -1
- package/src/cognition/agency/reconcile.learning.ts +10 -1
- package/src/cognition/agency/revocation.ts +79 -0
- package/src/cognition/agency/schemas/repertoire.ts +109 -9
- package/src/cognition/agency/selection.scoring.ts +32 -10
- package/src/cognition/agency/types.ts +9 -0
- package/src/cognition/bus.ts +2 -2
- package/src/cognition/completion.inbox.ts +1 -1
- package/src/cognition/config.mirror.entities.ts +5 -5
- package/src/cognition/event.log.ts +2 -2
- package/src/cognition/event.schemas.ts +67 -67
- package/src/cognition/faculties/aesthetic.evaluator.ts +6 -6
- package/src/cognition/faculties/affective.blender.ts +36 -4
- package/src/cognition/faculties/attachment.evaluator.ts +6 -6
- package/src/cognition/faculties/attention.allocator.ts +63 -8
- package/src/cognition/faculties/autobiographical.narrator.ts +3 -3
- package/src/cognition/faculties/bias.detector.ts +7 -7
- package/src/cognition/faculties/circadian.oscillator.ts +3 -3
- package/src/cognition/faculties/confidence.calibrator.ts +7 -7
- package/src/cognition/faculties/dream.simulator.ts +5 -5
- package/src/cognition/faculties/empathy.simulator.ts +7 -7
- package/src/cognition/faculties/energy.regulator.ts +4 -4
- package/src/cognition/faculties/episodic.consolidator.ts +6 -6
- package/src/cognition/faculties/executive.engine/commands.ts +6 -6
- package/src/cognition/faculties/executive.engine/context.ts +42 -42
- package/src/cognition/faculties/executive.engine/deferred.effects.ts +2 -2
- package/src/cognition/faculties/executive.engine/engine.ts +25 -25
- package/src/cognition/faculties/executive.engine/facet.supervisor.ts +12 -12
- package/src/cognition/faculties/executive.engine/facet.ts +18 -18
- package/src/cognition/faculties/executive.engine/gating.ts +2 -2
- package/src/cognition/faculties/executive.engine/messages.ts +3 -3
- package/src/cognition/faculties/executive.engine/parser.ts +11 -11
- package/src/cognition/faculties/executive.engine/prompt.factory.ts +55 -55
- package/src/cognition/faculties/exteroception.ts +99 -5
- package/src/cognition/faculties/forgetting.curve.ts +3 -3
- package/src/cognition/faculties/frustration.evaluator.ts +6 -6
- package/src/cognition/faculties/goal.manager.ts +15 -15
- package/src/cognition/faculties/inhibition.controller.ts +3 -3
- package/src/cognition/faculties/interoception.ts +3 -3
- package/src/cognition/faculties/introspection.engine.ts +3 -3
- package/src/cognition/faculties/known.entity.tracker.ts +15 -15
- package/src/cognition/faculties/loss.evaluator.ts +2 -2
- package/src/cognition/faculties/moral.evaluator.ts +4 -4
- package/src/cognition/faculties/novelty.detector.ts +4 -4
- package/src/cognition/faculties/persona.consolidator.ts +12 -12
- package/src/cognition/faculties/planning.engine/engine.ts +47 -47
- package/src/cognition/faculties/planning.engine/plan.frontier.ts +5 -5
- package/src/cognition/faculties/planning.engine/plan.store.ts +1 -1
- package/src/cognition/faculties/planning.engine/plan.supervision.ts +20 -20
- package/src/cognition/faculties/reputation.tracker.ts +6 -6
- package/src/cognition/faculties/reward.evaluator.ts +4 -4
- package/src/cognition/faculties/self.model.updater.ts +8 -8
- package/src/cognition/faculties/semantic.engine/integrator.ts +15 -15
- package/src/cognition/faculties/sleep.pressure.regulator.ts +4 -4
- package/src/cognition/faculties/social.perception.ts +3 -3
- package/src/cognition/faculties/spaced.repetition.ts +2 -2
- package/src/cognition/faculties/stress.regulator.ts +27 -4
- package/src/cognition/faculties/task.switcher.ts +11 -6
- package/src/cognition/faculties/theory.of.mind.ts +6 -6
- package/src/cognition/faculties/threat.evaluator.ts +3 -3
- package/src/cognition/faculties/working.memory.ts +8 -8
- package/src/cognition/generative.model.ts +2 -2
- package/src/cognition/memory/vector.adapter.ts +5 -5
- package/src/cognition/memory/vector.content.ts +4 -4
- package/src/cognition/memory/vector.embedder.ts +9 -9
- package/src/cognition/persona.prior.ts +1 -1
- package/src/cognition/schema.registry.ts +2 -2
- package/src/cognition/senses/audition.engine/engine.ts +2 -2
- package/src/cognition/senses/base.sense.engine.ts +1 -1
- package/src/cognition/utilities/token.tracker.ts +5 -5
- package/src/core/abstracts.ts +1 -1
- package/src/core/completion.recorder.ts +2 -2
- package/src/core/serialization.ts +3 -3
- package/src/core/snapshot.manager.ts +1 -1
- package/src/core/state.manager.ts +1 -1
- package/src/core/utils.ts +1 -1
- package/src/host/boot.ts +30 -30
- package/src/host/utterances.ts +1 -1
- package/src/llm/index.ts +9 -9
- package/src/llm/summarizer.ts +4 -4
- package/src/llm/wire.contracts.ts +1 -1
- package/src/mcp/effectors.ts +10 -10
- package/src/mcp/server.ts +11 -11
- package/src/pma/eval.ts +1 -1
- package/src/pma/index.ts +21 -21
- package/src/runners/outreach.runner.ts +3 -3
- package/src/runners/social.runner.ts +1 -1
- package/src/runners/thin-shim.runner.ts +1 -1
- package/src/sdk/will.ts +5 -5
- package/src/serve/server.ts +12 -12
- package/src/stem/assembly.audit.ts +3 -3
- package/src/stem/guards/identity.coherence.ts +10 -10
- package/src/stem/guards/identity.guard.ts +22 -22
- package/src/stem/index.ts +26 -11
- package/src/stem/mind.ts +12 -12
- package/src/stem/policy/arbiter.ts +136 -0
- package/src/stem/policy/rule.table.ts +172 -0
- package/src/stem/policy/verdict.recorder.ts +0 -0
- package/src/stem/tracts/biography.writer.ts +3 -3
- package/src/stem/tracts/effector.controller.ts +383 -4
- package/src/stem/tracts/health.reporter.ts +1 -1
- package/src/stem/tracts/outbox.controller.ts +4 -0
- package/src/stem/tracts/outbox.writer.ts +4 -4
- package/src/stem/tracts/pma.controller.ts +1 -1
- package/src/stem/tracts/replay.controller.ts +10 -10
- package/src/stem/tracts/sensory.controller.ts +2 -2
- package/src/stem/tracts/session.logger.ts +4 -4
- package/src/stem/tracts/transport/socketio.transport.ts +7 -7
- package/src/stem/tracts/transport/stream.transport.ts +2 -2
- package/src/stem/tracts/transport.controller.ts +15 -15
package/dist/mcp/effectors.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
2
|
-
import { E as EffectorHandler, W as Will } from '../will-
|
|
2
|
+
import { E as EffectorHandler, W as Will } from '../will-DAW0l-lY.js';
|
|
3
3
|
|
|
4
4
|
/** Where the tools live: spawn a local server, reach a remote one, or bring a connected client. */
|
|
5
5
|
type McpToolsSource = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/mcp/effectors.ts"],"names":[],"mappings":"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","file":"effectors.js","sourcesContent":["// ─────────────────────────────────────────────────────────────\n// src/mcp/effectors.ts — a Will EMPLOYING MCP tools (Seam 1)\n// ─────────────────────────────────────────────────────────────\n//\n// The other direction from server.ts: connect a Will to an external MCP server\n// and register that server's tools as the Will's own ABILITIES. Each tool\n// becomes a learnable affordance — its description (plus a compact hint of the\n// arguments it takes) is the ability's meaning, surfaced to the executive and\n// the deliberator; the WILL decides when to enact one (nothing here dispatches);\n// the tool's result feeds back through reafference, so the Will gets *skilled*\n// at the tools it uses.\n//\n// Arguments come from conscious intent: the executive supplies them via an\n// action's `args`, which ride the ideomotor leg into the invocation (see\n// executive commands.ts). A tool with required arguments enacted habitually\n// (without args) fails informatively — reafference then teaches the Will that\n// this ability wants deliberate articulation.\n//\n// const { names, close } = await connectMcpEffectors( will, {\n// command: 'npx', args: [ '-y', '@modelcontextprotocol/server-filesystem', '/tmp' ],\n// } )\n//\n// Import from '@mindot/will/mcp' — kept off the main entry so non-MCP\n// consumers never load the MCP SDK.\n// ─────────────────────────────────────────────────────────────\n\nimport { Client } from '@modelcontextprotocol/sdk/client/index.js'\nimport { StdioClientTransport, getDefaultEnvironment } from '@modelcontextprotocol/sdk/client/stdio.js'\nimport { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'\nimport type { Will, EffectorHandler, EffectorResult } from '#sdk/will'\n\n/** Where the tools live: spawn a local server, reach a remote one, or bring a connected client. */\nexport type McpToolsSource =\n | { command: string; args?: string[]; env?: Record<string, string> }\n | { url: string }\n | { client: Client }\n\nexport interface McpEffectorsOptions {\n /** Intrinsic effort prior 0..1 seeded on every bridged ability (default 0.2). */\n cost?: number\n /** Prefix for the ability names (e.g. 'fs_') — avoids collisions across servers. */\n prefix?: string\n}\n\n/** Minimal structural view of an MCP tool (the SDK's zod-inferred type, loosened). */\nexport interface McpToolInfo {\n name: string\n description?: string\n inputSchema?: {\n type?: string\n properties?: Record<string, { type?: string; description?: string }>\n required?: string[]\n }\n}\n\n/** Keep tool outcomes bounded — the description feeds reafference + episodic memory. */\nconst RESULT_DESCRIPTION_CAP = 700\n/** Keep ability meanings bounded — they render into the executive prompt. */\nconst MEANING_CAP = 300\n\n/**\n * The ability's *meaning*: the tool's description plus a compact hint of the\n * arguments it takes — so the executive knows what to supply in an action's\n * `args` when it enacts this ability.\n */\nexport function describeMcpTool( tool: McpToolInfo ): string {\n const props = tool.inputSchema?.properties ?? {}\n const required = new Set( tool.inputSchema?.required ?? [] )\n const argHints = Object.entries( props ).map( ( [ key, p ] ) =>\n `${ key }${ required.has( key ) ? '' : '?' }${ p.description ? `: ${ p.description }` : '' }` )\n\n const base = ( tool.description ?? `The ${ tool.name } tool.` ).trim().replace( /\\s+/g, ' ' )\n const hint = argHints.length > 0 ? ` (args — ${ argHints.join( '; ' ) })` : ''\n const full = `${ base }${ hint }`\n return full.length > MEANING_CAP ? `${ full.slice( 0, MEANING_CAP - 1 ) }…` : full\n}\n\n/**\n * The effector handler for one bridged tool: checks required args (an ability\n * enacted without its needed articulation fails informatively — reafference\n * learns from it), calls the tool, and maps the result onto EffectorResult.\n */\nexport function buildMcpHandler( client: Client, tool: McpToolInfo ): EffectorHandler {\n return async ( args ): Promise<EffectorResult> => {\n const props = tool.inputSchema?.properties\n // Only pass keys the tool declares — invocation params can carry situation\n // extras (targetEntityName, learned priors) the tool never asked for.\n const filtered: Record<string, unknown> = {}\n for( const [ k, v ] of Object.entries( args ?? {} ) )\n if( !props || k in props ) filtered[ k ] = v\n\n const missing = ( tool.inputSchema?.required ?? [] ).filter(\n k => filtered[ k ] === undefined || filtered[ k ] === '' )\n if( missing.length > 0 )\n return {\n success: false,\n description: `${ tool.name } needs ${ missing.join( ', ' ) } — enact it deliberately, supplying them in the action's args.`,\n }\n\n try {\n const res = await client.callTool( { name: tool.name, arguments: filtered } ) as\n { content?: Array<{ type: string; text?: string }>; isError?: boolean }\n const text = ( res.content ?? [] )\n .filter( c => c.type === 'text' && typeof c.text === 'string' )\n .map( c => c.text as string )\n .join( '\\n' )\n .trim() || ( res.isError ? 'The tool reported an error.' : 'Done (no output).' )\n const bounded = text.length > RESULT_DESCRIPTION_CAP ? `${ text.slice( 0, RESULT_DESCRIPTION_CAP - 1 ) }…` : text\n return { success: !res.isError, description: bounded }\n }\n catch( err ){\n return { success: false, description: `${ tool.name } failed: ${ err instanceof Error ? err.message : String( err ) }` }\n }\n }\n}\n\nasync function connect( source: McpToolsSource ): Promise<{ client: Client; owned: boolean }> {\n if( 'client' in source ) return { client: source.client, owned: false }\n\n const client = new Client( { name: 'mindot-will', version: '0' } )\n if( 'url' in source )\n await client.connect( new StreamableHTTPClientTransport( new URL( source.url ) ) )\n else\n await client.connect( new StdioClientTransport( {\n command: source.command,\n ...( source.args ? { args: source.args } : {} ),\n // Merge over the SDK's safe default env so PATH etc. survive a custom env.\n env: { ...getDefaultEnvironment(), ...( source.env ?? {} ) },\n } ) )\n return { client, owned: true }\n}\n\n/**\n * Register an MCP server's tools as the Will's abilities. Returns the ability\n * names registered and a `close()` for the connection (call it when the Will\n * stops; a client passed in via `source.client` is left open).\n */\nexport async function connectMcpEffectors(\n will: Will,\n source: McpToolsSource,\n opts: McpEffectorsOptions = {},\n): Promise<{ names: string[]; close: () => Promise<void> }> {\n const { client, owned } = await connect( source )\n const { tools } = await client.listTools() as unknown as { tools: McpToolInfo[] }\n\n const names: string[] = []\n for( const tool of tools ){\n const name = `${ opts.prefix ?? '' }${ tool.name }`\n will.effector( name, {\n description: describeMcpTool( tool ),\n cost: opts.cost ?? 0.2,\n tags: [ 'mcp' ],\n handler: buildMcpHandler( client, tool ),\n } )\n names.push( name )\n }\n\n return { names, close: async () => { if( owned ) await client.close() } }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/mcp/effectors.ts"],"names":[],"mappings":"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","file":"effectors.js","sourcesContent":["// ─────────────────────────────────────────────────────────────\n// src/mcp/effectors.ts — a Will EMPLOYING MCP tools (Seam 1)\n// ─────────────────────────────────────────────────────────────\n//\n// The other direction from server.ts: connect a Will to an external MCP server\n// and register that server's tools as the Will's own ABILITIES. Each tool\n// becomes a learnable affordance — its description (plus a compact hint of the\n// arguments it takes) is the ability's meaning, surfaced to the executive and\n// the deliberator; the WILL decides when to enact one (nothing here dispatches);\n// the tool's result feeds back through reafference, so the Will gets *skilled*\n// at the tools it uses.\n//\n// Arguments come from conscious intent: the executive supplies them via an\n// action's `args`, which ride the ideomotor leg into the invocation (see\n// executive commands.ts). A tool with required arguments enacted habitually\n// (without args) fails informatively — reafference then teaches the Will that\n// this ability wants deliberate articulation.\n//\n// const { names, close } = await connectMcpEffectors( will, {\n// command: 'npx', args: [ '-y', '@modelcontextprotocol/server-filesystem', '/tmp' ],\n// } )\n//\n// Import from '@mindot/will/mcp' — kept off the main entry so non-MCP\n// consumers never load the MCP SDK.\n// ─────────────────────────────────────────────────────────────\n\nimport { Client } from '@modelcontextprotocol/sdk/client/index.js'\nimport { StdioClientTransport, getDefaultEnvironment } from '@modelcontextprotocol/sdk/client/stdio.js'\nimport { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'\nimport type { Will, EffectorHandler, EffectorResult } from '#sdk/will'\n\n/** Where the tools live: spawn a local server, reach a remote one, or bring a connected client. */\nexport type McpToolsSource =\n | { command: string; args?: string[]; env?: Record<string, string> }\n | { url: string }\n | { client: Client }\n\nexport interface McpEffectorsOptions {\n /** Intrinsic effort prior 0..1 seeded on every bridged ability (default 0.2). */\n cost?: number\n /** Prefix for the ability names (e.g. 'fs_') — avoids collisions across servers. */\n prefix?: string\n}\n\n/** Minimal structural view of an MCP tool (the SDK's zod-inferred type, loosened). */\nexport interface McpToolInfo {\n name: string\n description?: string\n inputSchema?: {\n type?: string\n properties?: Record<string, { type?: string; description?: string }>\n required?: string[]\n }\n}\n\n/** Keep tool outcomes bounded — the description feeds reafference + episodic memory. */\nconst RESULT_DESCRIPTION_CAP = 700\n/** Keep ability meanings bounded — they render into the executive prompt. */\nconst MEANING_CAP = 300\n\n/**\n * The ability's *meaning*: the tool's description plus a compact hint of the\n * arguments it takes — so the executive knows what to supply in an action's\n * `args` when it enacts this ability.\n */\nexport function describeMcpTool( tool: McpToolInfo ): string {\n const props = tool.inputSchema?.properties ?? {}\n const required = new Set( tool.inputSchema?.required ?? [] )\n const argHints = Object.entries( props ).map( ( [ key, p ] ) =>\n `${ key }${ required.has( key ) ? '' : '?' }${ p.description ? `: ${ p.description }` : '' }`)\n\n const base = ( tool.description ?? `The ${ tool.name } tool.`).trim().replace( /\\s+/g, ' ')\n const hint = argHints.length > 0 ? ` (args — ${ argHints.join('; ') })` : ''\n const full = `${ base }${ hint }`\n return full.length > MEANING_CAP ? `${ full.slice( 0, MEANING_CAP - 1 ) }…` : full\n}\n\n/**\n * The effector handler for one bridged tool: checks required args (an ability\n * enacted without its needed articulation fails informatively — reafference\n * learns from it), calls the tool, and maps the result onto EffectorResult.\n */\nexport function buildMcpHandler( client: Client, tool: McpToolInfo ): EffectorHandler {\n return async ( args ): Promise<EffectorResult> => {\n const props = tool.inputSchema?.properties\n // Only pass keys the tool declares — invocation params can carry situation\n // extras (targetEntityName, learned priors) the tool never asked for.\n const filtered: Record<string, unknown> = {}\n for( const [ k, v ] of Object.entries( args ?? {} ) )\n if( !props || k in props ) filtered[ k ] = v\n\n const missing = ( tool.inputSchema?.required ?? [] ).filter(\n k => filtered[ k ] === undefined || filtered[ k ] === '')\n if( missing.length > 0 )\n return {\n success: false,\n description: `${ tool.name } needs ${ missing.join(', ') } — enact it deliberately, supplying them in the action's args.`,\n }\n\n try {\n const res = await client.callTool( { name: tool.name, arguments: filtered } ) as\n { content?: Array<{ type: string; text?: string }>; isError?: boolean }\n const text = ( res.content ?? [] )\n .filter( c => c.type === 'text' && typeof c.text === 'string')\n .map( c => c.text as string )\n .join('\\n')\n .trim() || ( res.isError ? 'The tool reported an error.' : 'Done (no output).')\n const bounded = text.length > RESULT_DESCRIPTION_CAP ? `${ text.slice( 0, RESULT_DESCRIPTION_CAP - 1 ) }…` : text\n return { success: !res.isError, description: bounded }\n }\n catch( err ){\n return { success: false, description: `${ tool.name } failed: ${ err instanceof Error ? err.message : String( err ) }` }\n }\n }\n}\n\nasync function connect( source: McpToolsSource ): Promise<{ client: Client; owned: boolean }> {\n if('client' in source ) return { client: source.client, owned: false }\n\n const client = new Client( { name: 'mindot-will', version: '0' } )\n if('url' in source )\n await client.connect( new StreamableHTTPClientTransport( new URL( source.url ) ) )\n else\n await client.connect( new StdioClientTransport( {\n command: source.command,\n ...( source.args ? { args: source.args } : {} ),\n // Merge over the SDK's safe default env so PATH etc. survive a custom env.\n env: { ...getDefaultEnvironment(), ...( source.env ?? {} ) },\n } ) )\n return { client, owned: true }\n}\n\n/**\n * Register an MCP server's tools as the Will's abilities. Returns the ability\n * names registered and a `close()` for the connection (call it when the Will\n * stops; a client passed in via `source.client` is left open).\n */\nexport async function connectMcpEffectors(\n will: Will,\n source: McpToolsSource,\n opts: McpEffectorsOptions = {},\n): Promise<{ names: string[]; close: () => Promise<void> }> {\n const { client, owned } = await connect( source )\n const { tools } = await client.listTools() as unknown as { tools: McpToolInfo[] }\n\n const names: string[] = []\n for( const tool of tools ){\n const name = `${ opts.prefix ?? '' }${ tool.name }`\n will.effector( name, {\n description: describeMcpTool( tool ),\n cost: opts.cost ?? 0.2,\n tags: [ 'mcp' ],\n handler: buildMcpHandler( client, tool ),\n } )\n names.push( name )\n }\n\n return { names, close: async () => { if( owned ) await client.close() } }\n}\n"]}
|
|
@@ -3347,6 +3347,8 @@ declare class AttentionAllocator implements SimulationEngine, CognitiveEngine {
|
|
|
3347
3347
|
private _effort;
|
|
3348
3348
|
/** A one-shot focus/rest request from the executive, applied next react(). */
|
|
3349
3349
|
private _effortRequest;
|
|
3350
|
+
/** ACP-P2: a self-caused precision attenuation is armed; react() restores after one observe. */
|
|
3351
|
+
private _acpOneShot;
|
|
3350
3352
|
private _bus;
|
|
3351
3353
|
private readonly _model;
|
|
3352
3354
|
constructor(config?: AttentionAllocatorConfig);
|
|
@@ -3435,6 +3437,8 @@ declare class StressRegulator implements SimulationEngine, CognitiveEngine {
|
|
|
3435
3437
|
subscribes(): string[];
|
|
3436
3438
|
publishes(): CognitiveEventSchema[];
|
|
3437
3439
|
onCognitiveEvent(e: CognitiveEvent): StateCommands | void;
|
|
3440
|
+
/** ACP-P2: self-caused stress attenuation armed; react() restores after one observe. */
|
|
3441
|
+
private _acpOneShot;
|
|
3438
3442
|
snapshot(): Record<string, unknown>;
|
|
3439
3443
|
react(delta: Duration, _tick: Tick, state: ReadonlySimulationState, context: SimulationContext): Promise<EngineResult>;
|
|
3440
3444
|
private _readConfigFromState;
|
|
@@ -3549,6 +3553,28 @@ declare class Exteroception implements SimulationEngine, CognitiveEngine {
|
|
|
3549
3553
|
* Generate a meaningful summary for an entity.
|
|
3550
3554
|
* Instead of "New percept: percept-54-0", produce something useful.
|
|
3551
3555
|
*/
|
|
3556
|
+
/**
|
|
3557
|
+
* The text the corollary-discharge matcher inspects for this entity — its
|
|
3558
|
+
* content (a message body) over its description over its summary. Where our
|
|
3559
|
+
* own delivered words would surface if the world echoes them back.
|
|
3560
|
+
*/
|
|
3561
|
+
/**
|
|
3562
|
+
* The affect→percept valence seam (registry #5). A percept carries how the
|
|
3563
|
+
* mind *feels* about what it is a percept OF, resolved most-specific-first:
|
|
3564
|
+
*
|
|
3565
|
+
* 1. the KnownEntityTracker's dossier for this entity (`ke-<id>.valence`) —
|
|
3566
|
+
* a real per-entity felt valence, the honest signal;
|
|
3567
|
+
* 2. otherwise the ambient `affect.valence` — the felt tone at perception
|
|
3568
|
+
* time. Weaker evidence, so it is tagged `'ambient'` and consumers
|
|
3569
|
+
* weight it down (mood is context, not appraisal of this thing).
|
|
3570
|
+
*
|
|
3571
|
+
* Absent both, no valence is stamped and every consumer keeps its
|
|
3572
|
+
* pre-seam behaviour.
|
|
3573
|
+
*/
|
|
3574
|
+
/** Spread-friendly form of `_valenceFor` for percept construction. */
|
|
3575
|
+
private _valenceOf;
|
|
3576
|
+
private _valenceFor;
|
|
3577
|
+
private _matchText;
|
|
3552
3578
|
private _summarizeEntity;
|
|
3553
3579
|
/**
|
|
3554
3580
|
* Compute salience of a percept based on entity characteristics.
|
|
@@ -4263,6 +4289,8 @@ declare class AffectiveBlender implements SimulationEngine, CognitiveEngine {
|
|
|
4263
4289
|
private _previousPAD;
|
|
4264
4290
|
private _emotionHistory;
|
|
4265
4291
|
private _moodBaseline;
|
|
4292
|
+
/** ACP-P2: self-caused arousal attenuation armed; react() restores after one observe. */
|
|
4293
|
+
private _acpOneShot;
|
|
4266
4294
|
private _energyLevel;
|
|
4267
4295
|
private _comfort;
|
|
4268
4296
|
private _sleepPressure;
|
|
@@ -5451,25 +5479,25 @@ declare class AccessGrants {
|
|
|
5451
5479
|
declare class VisionEngine extends ShellSenseEngine {
|
|
5452
5480
|
readonly name = "vision-engine";
|
|
5453
5481
|
readonly domain: "vision";
|
|
5454
|
-
protected readonly acceptedKinds: Set<"text" | "system" | "
|
|
5482
|
+
protected readonly acceptedKinds: Set<"text" | "system" | "ambient" | "voice" | "image" | "video" | "webhook" | "background" | "self-eval" | "assessment">;
|
|
5455
5483
|
}
|
|
5456
5484
|
|
|
5457
5485
|
declare class SomatosensationEngine extends ShellSenseEngine {
|
|
5458
5486
|
readonly name = "somatosensation-engine";
|
|
5459
5487
|
readonly domain: "somatosensation";
|
|
5460
|
-
protected readonly acceptedKinds: Set<"text" | "system" | "
|
|
5488
|
+
protected readonly acceptedKinds: Set<"text" | "system" | "ambient" | "voice" | "image" | "video" | "webhook" | "background" | "self-eval" | "assessment">;
|
|
5461
5489
|
}
|
|
5462
5490
|
|
|
5463
5491
|
declare class OlfactionEngine extends ShellSenseEngine {
|
|
5464
5492
|
readonly name = "olfaction-engine";
|
|
5465
5493
|
readonly domain: "olfaction";
|
|
5466
|
-
protected readonly acceptedKinds: Set<"text" | "system" | "
|
|
5494
|
+
protected readonly acceptedKinds: Set<"text" | "system" | "ambient" | "voice" | "image" | "video" | "webhook" | "background" | "self-eval" | "assessment">;
|
|
5467
5495
|
}
|
|
5468
5496
|
|
|
5469
5497
|
declare class GustationEngine extends ShellSenseEngine {
|
|
5470
5498
|
readonly name = "gustation-engine";
|
|
5471
5499
|
readonly domain: "gustation";
|
|
5472
|
-
protected readonly acceptedKinds: Set<"text" | "system" | "
|
|
5500
|
+
protected readonly acceptedKinds: Set<"text" | "system" | "ambient" | "voice" | "image" | "video" | "webhook" | "background" | "self-eval" | "assessment">;
|
|
5473
5501
|
}
|
|
5474
5502
|
|
|
5475
5503
|
/**
|
|
@@ -5639,7 +5667,7 @@ declare class AuditionEngine extends BaseSenseEngine {
|
|
|
5639
5667
|
readonly name = "audition-engine";
|
|
5640
5668
|
readonly domain: "audition";
|
|
5641
5669
|
/** Audition consumes language input; the base filters every other kind out. */
|
|
5642
|
-
protected readonly acceptedKinds: Set<"text" | "system" | "
|
|
5670
|
+
protected readonly acceptedKinds: Set<"text" | "system" | "ambient" | "voice" | "image" | "video" | "webhook" | "background" | "self-eval" | "assessment">;
|
|
5643
5671
|
/** Inbound is gated on the 'listen' effector — the base enforces it before _perceive(). */
|
|
5644
5672
|
protected readonly gateEffector = "listen";
|
|
5645
5673
|
private _executiveEngine;
|
|
@@ -5998,6 +6026,9 @@ declare class SchemaRepertoire {
|
|
|
5998
6026
|
private _skills;
|
|
5999
6027
|
/** Tracks which templates were learned at runtime (vs innate) so decay can forget them. */
|
|
6000
6028
|
private _learned;
|
|
6029
|
+
/** Availability layer (P2): schema → { value 0..1, lastRefusedTick }. Empty until
|
|
6030
|
+
* a refusal lands — a never-refused Will writes nothing here (byte-identical). */
|
|
6031
|
+
private _availability;
|
|
6001
6032
|
constructor(seed?: MotorSchema[]);
|
|
6002
6033
|
schemas(): MotorSchema[];
|
|
6003
6034
|
getSchema(id: string): MotorSchema | undefined;
|
|
@@ -6012,6 +6043,23 @@ declare class SchemaRepertoire {
|
|
|
6012
6043
|
registerExternal(schema: MotorSchema): void;
|
|
6013
6044
|
skills(): ReadonlyMap<string, LearnedSkill>;
|
|
6014
6045
|
getSkill(id: string): LearnedSkill | undefined;
|
|
6046
|
+
availability(): ReadonlyMap<string, {
|
|
6047
|
+
value: number;
|
|
6048
|
+
lastRefusedTick: number;
|
|
6049
|
+
}>;
|
|
6050
|
+
/**
|
|
6051
|
+
* How available a schema is right now, 0..1. Absent from the ledger ⇒ 1
|
|
6052
|
+
* (fully available — the common case). This is the ONLY value the
|
|
6053
|
+
* AffordanceSynthesizer reads; it never touches competence.
|
|
6054
|
+
*/
|
|
6055
|
+
availabilityOf(schema: string): number;
|
|
6056
|
+
/**
|
|
6057
|
+
* Fold a policy refusal into the availability layer (NOT competence). A
|
|
6058
|
+
* `class` refusal cuts availability hard; an `instance` refusal dents it
|
|
6059
|
+
* lightly. Multiplicative so repeated refusals compound toward — but never
|
|
6060
|
+
* reach — zero, keeping re-probe alive.
|
|
6061
|
+
*/
|
|
6062
|
+
recordRefusal(schema: string, finality: 'class' | 'instance', tick: number): number;
|
|
6015
6063
|
/**
|
|
6016
6064
|
* Fold one outcome into the schema's learned skill. Returns the updated skill
|
|
6017
6065
|
* and whether it just crossed the proceduralization threshold this update.
|
|
@@ -6021,11 +6069,16 @@ declare class SchemaRepertoire {
|
|
|
6021
6069
|
proceduralized: boolean;
|
|
6022
6070
|
};
|
|
6023
6071
|
/**
|
|
6024
|
-
* Forgetting curve over the competence layer
|
|
6025
|
-
* lose habit; learned composites
|
|
6026
|
-
* entirely (template + skill).
|
|
6072
|
+
* Forgetting curve over the competence layer, plus availability recovery.
|
|
6073
|
+
* Skills unused for IDLE_TICKS lose habit; learned composites below DROP_HABIT
|
|
6074
|
+
* are dropped entirely (template + skill). Availability entries climb back
|
|
6075
|
+
* toward 1 and are dropped once fully recovered. Returns the ids that were
|
|
6076
|
+
* removed from each layer so their mirrored state entities can be deleted.
|
|
6027
6077
|
*/
|
|
6028
|
-
decay(tick: number):
|
|
6078
|
+
decay(tick: number): {
|
|
6079
|
+
skills: string[];
|
|
6080
|
+
availability: string[];
|
|
6081
|
+
};
|
|
6029
6082
|
/** Learned composite templates + all skills above a confidence floor. */
|
|
6030
6083
|
export(minHabit?: number): {
|
|
6031
6084
|
composites: MotorSchema[];
|
|
@@ -6045,6 +6098,12 @@ declare class SchemaRepertoire {
|
|
|
6045
6098
|
* Mirrors GoalManager._syncFromStateGoals.
|
|
6046
6099
|
*/
|
|
6047
6100
|
restoreComposites(entities: ReadonlySimulationState['entities']): void;
|
|
6101
|
+
/** Availability ledger encoded as `agency.availability` state entities (P2).
|
|
6102
|
+
* Empty until a refusal lands, so the quiet path writes nothing. */
|
|
6103
|
+
availabilityEntities(): EntityInput[];
|
|
6104
|
+
/** Rehydrate the availability ledger from state after a restore. Idempotent;
|
|
6105
|
+
* keeps whichever value is more restrictive so a concurrent refusal isn't lost. */
|
|
6106
|
+
restoreAvailability(entities: ReadonlySimulationState['entities']): void;
|
|
6048
6107
|
}
|
|
6049
6108
|
|
|
6050
6109
|
type SkillAccessor = () => ReadonlyMap<string, LearnedSkill>;
|
|
@@ -6085,11 +6144,25 @@ declare class ActionSelector implements CognitiveEngine {
|
|
|
6085
6144
|
private _bus;
|
|
6086
6145
|
private _lastEntropy;
|
|
6087
6146
|
private _lastDeliberate;
|
|
6147
|
+
private _lastRevoked;
|
|
6148
|
+
private _senseBuffer;
|
|
6088
6149
|
attachBus(bus: CognitiveBus): void;
|
|
6089
6150
|
publishes(): CognitiveEventSchema[];
|
|
6090
6151
|
subscribes(): string[];
|
|
6091
|
-
|
|
6152
|
+
/**
|
|
6153
|
+
* Mostly pull-model — but two afferent classes never become entities and
|
|
6154
|
+
* would leave rupture blind: sense-channel percepts
|
|
6155
|
+
* (ACTION_CONDITIONED_PREDICTION §2b) and the model-error state-change
|
|
6156
|
+
* events (registry #6). Buffer both (cross-tick: bus flush at T, consumed
|
|
6157
|
+
* by react at T+1 — FN9-snapshotted); the echo guard is applied at read
|
|
6158
|
+
* time for texts, and at the SOURCE for model errors (ACP-P2 precision).
|
|
6159
|
+
*/
|
|
6160
|
+
onCognitiveEvent(e: CognitiveEvent): void;
|
|
6092
6161
|
snapshot(): Record<string, unknown>;
|
|
6162
|
+
/** FN9: `_lastRevoked` has behavioral effect (the Channel-B `revokedBy` stamp),
|
|
6163
|
+
* so a restored mind must carry it — a rupture-driven letting-go survives a
|
|
6164
|
+
* snapshot boundary instead of silently losing its narrative thread. */
|
|
6165
|
+
restore(s: Record<string, unknown>): void;
|
|
6093
6166
|
react(_delta: Duration, tick: Tick, state: ReadonlySimulationState, _context: SimulationContext): Promise<EngineResult>;
|
|
6094
6167
|
}
|
|
6095
6168
|
|
|
@@ -7653,6 +7726,13 @@ declare class WillStem {
|
|
|
7653
7726
|
description: string;
|
|
7654
7727
|
metrics?: Record<string, number>;
|
|
7655
7728
|
}): void;
|
|
7729
|
+
/**
|
|
7730
|
+
* Resolve a policy escalation the Will raised (POLICY_REAFFERENCE P4).
|
|
7731
|
+
* `approved` dispatches the held invocation to the world; otherwise it is
|
|
7732
|
+
* refused. Applied at the next tick boundary. `invocationId` is the awaiting
|
|
7733
|
+
* `agency.intent` id the escalation ask referenced.
|
|
7734
|
+
*/
|
|
7735
|
+
resolveEscalation(id: string, invocationId: string, approved: boolean): void;
|
|
7656
7736
|
/**
|
|
7657
7737
|
* Confirm a message was received by the target entity. Writes a
|
|
7658
7738
|
* message.delivery percept ("ear hears the word you spoke") and updates the
|
package/package.json
CHANGED
package/src/channels/discord.ts
CHANGED
|
@@ -84,8 +84,8 @@ export interface DiscordBridgeOptions {
|
|
|
84
84
|
* itself is not stopped; it simply loses this surface.
|
|
85
85
|
*/
|
|
86
86
|
export async function connectDiscord( will: Will, opts: DiscordBridgeOptions ): Promise<ChannelBridge> {
|
|
87
|
-
const log = opts.log ?? ( ( m: string ) => console.error(
|
|
88
|
-
const roster = new ChannelRoster( opts.rosterPath ?? `.will/${ will.id }.discord.json`
|
|
87
|
+
const log = opts.log ?? ( ( m: string ) => console.error(`[will:discord] ${ m }`) )
|
|
88
|
+
const roster = new ChannelRoster( opts.rosterPath ?? `.will/${ will.id }.discord.json`)
|
|
89
89
|
const allowed = opts.channels?.length ? new Set( opts.channels ) : null
|
|
90
90
|
|
|
91
91
|
const client = opts.client ?? await createDiscordClient()
|
|
@@ -94,7 +94,7 @@ export async function connectDiscord( will: Will, opts: DiscordBridgeOptions ):
|
|
|
94
94
|
let lastActiveChannelId: string | null = opts.homeChannelId ?? null
|
|
95
95
|
|
|
96
96
|
// ── inbound: platform message → stimulus ──────────────────────────────────
|
|
97
|
-
client.on(
|
|
97
|
+
client.on('messageCreate', message => { void onMessage( message ) } )
|
|
98
98
|
|
|
99
99
|
async function onMessage( message: DiscordLikeMessage ): Promise<void> {
|
|
100
100
|
const self = client.user
|
|
@@ -135,7 +135,7 @@ export async function connectDiscord( will: Will, opts: DiscordBridgeOptions ):
|
|
|
135
135
|
// ── outbound: projected utterance → the addressee ─────────────────────────
|
|
136
136
|
// The facade has no off(); the bridge gates its handler on `closed` instead.
|
|
137
137
|
let closed = false
|
|
138
|
-
will.on(
|
|
138
|
+
will.on('message', ( m: WillMessage ) => { if( !closed ) void deliver( m ) } )
|
|
139
139
|
|
|
140
140
|
async function deliver( m: WillMessage ): Promise<void> {
|
|
141
141
|
const peer = m.to ? roster.resolve( m.to ) : undefined
|
|
@@ -161,7 +161,7 @@ export async function connectDiscord( will: Will, opts: DiscordBridgeOptions ):
|
|
|
161
161
|
}
|
|
162
162
|
catch { /* fall through */ }
|
|
163
163
|
}
|
|
164
|
-
log(
|
|
164
|
+
log(`no route for utterance to '${ m.to }' — dropped (${ m.content.length } chars)`)
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
// ── lifecycle ──────────────────────────────────────────────────────────────
|
|
@@ -175,14 +175,14 @@ export async function connectDiscord( will: Will, opts: DiscordBridgeOptions ):
|
|
|
175
175
|
const ready = new Promise<void>( resolve => {
|
|
176
176
|
let poll: ReturnType<typeof setInterval> | null = null
|
|
177
177
|
const done = (): void => { if( poll ) clearInterval( poll ); resolve() }
|
|
178
|
-
client.once(
|
|
178
|
+
client.once('clientReady', done )
|
|
179
179
|
poll = setInterval( () => { if( client.isReady?.() ) done() }, 100 )
|
|
180
180
|
poll.unref?.()
|
|
181
181
|
} )
|
|
182
|
-
await client.login( opts.token ?? ''
|
|
182
|
+
await client.login( opts.token ?? '')
|
|
183
183
|
await ready
|
|
184
184
|
}
|
|
185
|
-
log(
|
|
185
|
+
log(`${ will.name } is present on Discord as user ${ client.user?.id }`)
|
|
186
186
|
},
|
|
187
187
|
async close(): Promise<void> {
|
|
188
188
|
if( closed ) return
|
|
@@ -196,10 +196,10 @@ export async function connectDiscord( will: Will, opts: DiscordBridgeOptions ):
|
|
|
196
196
|
|
|
197
197
|
/** Build a real discord.js client (lazy import keeps it out of non-Discord hosts). */
|
|
198
198
|
async function createDiscordClient(): Promise<DiscordLikeClient> {
|
|
199
|
-
let mod: typeof import(
|
|
200
|
-
try { mod = await import(
|
|
199
|
+
let mod: typeof import('discord.js')
|
|
200
|
+
try { mod = await import('discord.js') }
|
|
201
201
|
catch {
|
|
202
|
-
throw new Error(
|
|
202
|
+
throw new Error('discord.js is not installed (it is an optionalDependency) — run `bun add discord.js` / `npm i discord.js` and retry.')
|
|
203
203
|
}
|
|
204
204
|
const { Client, GatewayIntentBits, Partials } = mod
|
|
205
205
|
return new Client( {
|
package/src/channels/roster.ts
CHANGED
|
@@ -42,7 +42,7 @@ export class ChannelRoster {
|
|
|
42
42
|
constructor( private readonly path: string ) {
|
|
43
43
|
if( existsSync( path ) ){
|
|
44
44
|
try {
|
|
45
|
-
const raw = JSON.parse( readFileSync( path, 'utf8'
|
|
45
|
+
const raw = JSON.parse( readFileSync( path, 'utf8') ) as RosterEntry[]
|
|
46
46
|
for( const e of Array.isArray( raw ) ? raw : [] ) this.entries.set( e.entityId, e )
|
|
47
47
|
}
|
|
48
48
|
catch { /* a corrupt roster is not worth failing a boot over — start fresh */ }
|
package/src/channels/types.ts
CHANGED
|
@@ -36,7 +36,7 @@ export function chunkText( text: string, max: number ): string[] {
|
|
|
36
36
|
while( rest.length > max ){
|
|
37
37
|
// Prefer a paragraph break, then a line break, then a space — else hard-cut.
|
|
38
38
|
const window = rest.slice( 0, max )
|
|
39
|
-
const cut = Math.max( window.lastIndexOf(
|
|
39
|
+
const cut = Math.max( window.lastIndexOf('\n\n'), window.lastIndexOf('\n'), window.lastIndexOf(' ') )
|
|
40
40
|
const at = cut > max * 0.5 ? cut : max
|
|
41
41
|
chunks.push( rest.slice( 0, at ).trimEnd() )
|
|
42
42
|
rest = rest.slice( at ).trimStart()
|
package/src/channels/whatsapp.ts
CHANGED
|
@@ -82,10 +82,10 @@ export interface WhatsAppBridgeOptions {
|
|
|
82
82
|
|
|
83
83
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
84
84
|
|
|
85
|
-
const isGroupJid = ( jid: string ): boolean => jid.endsWith(
|
|
85
|
+
const isGroupJid = ( jid: string ): boolean => jid.endsWith('@g.us')
|
|
86
86
|
|
|
87
87
|
/** '4915123:7@s.whatsapp.net' → '4915123' (device + server stripped). */
|
|
88
|
-
const bareId = ( jid: string ): string => jid.split(
|
|
88
|
+
const bareId = ( jid: string ): string => jid.split('@')[0]!.split(':')[0]!
|
|
89
89
|
|
|
90
90
|
/** A DM jid is derivable from the bare number — WhatsApp's gift to proactivity. */
|
|
91
91
|
const dmJidFor = ( userId: string ): string => `${ userId }@s.whatsapp.net`
|
|
@@ -108,8 +108,8 @@ function textOf( m: WaLikeMessage ): string {
|
|
|
108
108
|
* `ChannelBridge.close()` — the Will keeps ticking; it only loses this surface.
|
|
109
109
|
*/
|
|
110
110
|
export async function connectWhatsApp( will: Will, opts: WhatsAppBridgeOptions = {} ): Promise<ChannelBridge> {
|
|
111
|
-
const log = opts.log ?? ( ( m: string ) => console.error(
|
|
112
|
-
const roster = new ChannelRoster( opts.rosterPath ?? `.will/${ will.id }.whatsapp.json`
|
|
111
|
+
const log = opts.log ?? ( ( m: string ) => console.error(`[will:whatsapp] ${ m }`) )
|
|
112
|
+
const roster = new ChannelRoster( opts.rosterPath ?? `.will/${ will.id }.whatsapp.json`)
|
|
113
113
|
const allowed = opts.chats?.length ? new Set( opts.chats ) : null
|
|
114
114
|
|
|
115
115
|
let closed = false
|
|
@@ -123,15 +123,15 @@ export async function connectWhatsApp( will: Will, opts: WhatsAppBridgeOptions =
|
|
|
123
123
|
let lastActiveChatId: string | null = opts.homeChatId ?? null
|
|
124
124
|
|
|
125
125
|
// ── inbound: platform message → stimulus ──────────────────────────────────
|
|
126
|
-
socket.ev.on(
|
|
127
|
-
if( type && type !== 'notify'
|
|
126
|
+
socket.ev.on('messages.upsert', ( { messages, type } ) => {
|
|
127
|
+
if( type && type !== 'notify') return // history sync/corrections are not new percepts
|
|
128
128
|
for( const m of messages ) void onMessage( m )
|
|
129
129
|
} )
|
|
130
130
|
|
|
131
131
|
async function onMessage( m: WaLikeMessage ): Promise<void> {
|
|
132
132
|
const jid = m.key.remoteJid
|
|
133
133
|
if( !jid || m.key.fromMe || !m.message || m.messageStubType ) return
|
|
134
|
-
if( jid.endsWith(
|
|
134
|
+
if( jid.endsWith('@broadcast') || jid.endsWith('@newsletter') ) return // stories/broadcasts aren't a room the Will is in
|
|
135
135
|
if( allowed && !allowed.has( jid ) ) return
|
|
136
136
|
|
|
137
137
|
const isGroup = isGroupJid( jid )
|
|
@@ -157,7 +157,7 @@ export async function connectWhatsApp( will: Will, opts: WhatsAppBridgeOptions =
|
|
|
157
157
|
|
|
158
158
|
// Same rule as Discord: a presence cue only when addressed — the mind may
|
|
159
159
|
// still choose silence, and 'composing' expires on its own.
|
|
160
|
-
if( addressed ) await socket.sendPresenceUpdate?.(
|
|
160
|
+
if( addressed ) await socket.sendPresenceUpdate?.('composing', jid ).catch( () => {} )
|
|
161
161
|
|
|
162
162
|
const text = textOf( m )
|
|
163
163
|
if( !text.trim() ) return
|
|
@@ -171,7 +171,7 @@ export async function connectWhatsApp( will: Will, opts: WhatsAppBridgeOptions =
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
// ── outbound: projected utterance → the addressee ─────────────────────────
|
|
174
|
-
will.on(
|
|
174
|
+
will.on('message', ( m: WillMessage ) => { if( !closed ) void deliver( m ) } )
|
|
175
175
|
|
|
176
176
|
async function deliver( m: WillMessage ): Promise<void> {
|
|
177
177
|
const peer = m.to ? roster.resolve( m.to ) : undefined
|
|
@@ -180,7 +180,7 @@ export async function connectWhatsApp( will: Will, opts: WhatsAppBridgeOptions =
|
|
|
180
180
|
// Last shared group → known DM → DM derived from the entity id itself →
|
|
181
181
|
// home chat → last active chat. Unlike Discord, an unmet-but-addressed
|
|
182
182
|
// entity is still reachable: `whatsapp:<number>` implies its DM jid.
|
|
183
|
-
const derivedDm = m.to?.startsWith(
|
|
183
|
+
const derivedDm = m.to?.startsWith('whatsapp:') ? dmJidFor( m.to.slice('whatsapp:'.length ) ) : undefined
|
|
184
184
|
const targets = [ peer?.lastChannelId, peer?.dmChannelId, derivedDm, opts.homeChatId ?? undefined, lastActiveChatId ?? undefined ]
|
|
185
185
|
for( const jid of targets ){
|
|
186
186
|
if( !jid ) continue
|
|
@@ -190,7 +190,7 @@ export async function connectWhatsApp( will: Will, opts: WhatsAppBridgeOptions =
|
|
|
190
190
|
}
|
|
191
191
|
catch { /* try the next route */ }
|
|
192
192
|
}
|
|
193
|
-
log(
|
|
193
|
+
log(`no route for utterance to '${ m.to }' — dropped (${ m.content.length } chars)`)
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
// ── lifecycle ──────────────────────────────────────────────────────────────
|
|
@@ -199,7 +199,7 @@ export async function connectWhatsApp( will: Will, opts: WhatsAppBridgeOptions =
|
|
|
199
199
|
async start(): Promise<void> {
|
|
200
200
|
// createWhatsAppSocket resolves already-open; an injected socket is the
|
|
201
201
|
// caller's to have readied.
|
|
202
|
-
log(
|
|
202
|
+
log(`${ will.name } is present on WhatsApp${ socket.user ? ` as ${ bareId( socket.user.id ) }` : '' }`)
|
|
203
203
|
},
|
|
204
204
|
async close(): Promise<void> {
|
|
205
205
|
if( closed ) return
|
|
@@ -226,10 +226,10 @@ interface SocketFactoryOpts {
|
|
|
226
226
|
* Resolves once the first connection is open (after QR pairing on first run).
|
|
227
227
|
*/
|
|
228
228
|
async function createWhatsAppSocket( o: SocketFactoryOpts ): Promise<WaLikeSocket> {
|
|
229
|
-
let baileys: typeof import(
|
|
230
|
-
try { baileys = await import(
|
|
229
|
+
let baileys: typeof import('baileys')
|
|
230
|
+
try { baileys = await import('baileys') }
|
|
231
231
|
catch {
|
|
232
|
-
throw new Error(
|
|
232
|
+
throw new Error('baileys is not installed (it is an optionalDependency) — run `bun add baileys` / `npm i baileys` and retry.')
|
|
233
233
|
}
|
|
234
234
|
const { default: makeWASocket, useMultiFileAuthState, DisconnectReason } = baileys
|
|
235
235
|
|
|
@@ -252,7 +252,7 @@ async function createWhatsAppSocket( o: SocketFactoryOpts ): Promise<WaLikeSocke
|
|
|
252
252
|
get user(){ return inner?.user ? { id: inner.user.id, name: inner.user.name ?? undefined } : null },
|
|
253
253
|
ev: { on: ( _e, fn ) => { subscribers.push( fn ) } },
|
|
254
254
|
sendMessage: ( jid, content ) => {
|
|
255
|
-
if( !inner ) return Promise.reject( new Error(
|
|
255
|
+
if( !inner ) return Promise.reject( new Error('whatsapp socket not connected') )
|
|
256
256
|
return inner.sendMessage( jid, content )
|
|
257
257
|
},
|
|
258
258
|
sendPresenceUpdate: ( state, jid ) => inner?.sendPresenceUpdate( state, jid ) ?? Promise.resolve(),
|
|
@@ -268,10 +268,10 @@ async function createWhatsAppSocket( o: SocketFactoryOpts ): Promise<WaLikeSocke
|
|
|
268
268
|
const sock = makeWASocket( { auth: state, logger: logger as never } )
|
|
269
269
|
inner = sock
|
|
270
270
|
|
|
271
|
-
sock.ev.on(
|
|
272
|
-
sock.ev.on(
|
|
271
|
+
sock.ev.on('creds.update', saveCreds )
|
|
272
|
+
sock.ev.on('messages.upsert', u => { for( const fn of subscribers ) fn( u as never ) } )
|
|
273
273
|
|
|
274
|
-
sock.ev.on(
|
|
274
|
+
sock.ev.on('connection.update', update => {
|
|
275
275
|
const { connection, lastDisconnect, qr } = update as {
|
|
276
276
|
connection?: string; lastDisconnect?: { error?: unknown }; qr?: string
|
|
277
277
|
}
|
|
@@ -282,16 +282,16 @@ async function createWhatsAppSocket( o: SocketFactoryOpts ): Promise<WaLikeSocke
|
|
|
282
282
|
opened = true
|
|
283
283
|
resolveOpen()
|
|
284
284
|
}
|
|
285
|
-
if( connection === 'close'
|
|
285
|
+
if( connection === 'close'){
|
|
286
286
|
const code = ( lastDisconnect?.error as { output?: { statusCode?: number } } | undefined )?.output?.statusCode
|
|
287
287
|
if( code === DisconnectReason.loggedOut ){
|
|
288
|
-
const err = new Error(
|
|
288
|
+
const err = new Error('WhatsApp unlinked this device (logged out) — delete the auth dir and pair again.')
|
|
289
289
|
o.log( err.message )
|
|
290
290
|
if( !opened ) rejectOpen( err )
|
|
291
291
|
return
|
|
292
292
|
}
|
|
293
293
|
if( o.stillOpen() ){
|
|
294
|
-
o.log(
|
|
294
|
+
o.log(`connection closed (status ${ code ?? '?' }) — reconnecting…`)
|
|
295
295
|
setTimeout( connect, 3_000 )
|
|
296
296
|
}
|
|
297
297
|
}
|
|
@@ -306,13 +306,13 @@ async function createWhatsAppSocket( o: SocketFactoryOpts ): Promise<WaLikeSocke
|
|
|
306
306
|
|
|
307
307
|
/** QR to the terminal; falls back to the raw pairing string if the tiny renderer is absent. */
|
|
308
308
|
async function printQr( qr: string, log: ( m: string ) => void ): Promise<void> {
|
|
309
|
-
log(
|
|
309
|
+
log('pair this device: WhatsApp → Settings → Linked devices → Link a device')
|
|
310
310
|
try {
|
|
311
311
|
type QrModule = { generate( s: string, o: { small: boolean } ): void }
|
|
312
|
-
const qrt = ( await import(
|
|
312
|
+
const qrt = ( await import('qrcode-terminal' as string ) ) as QrModule & { default?: QrModule }
|
|
313
313
|
;( qrt.default ?? qrt ).generate( qr, { small: true } )
|
|
314
314
|
}
|
|
315
315
|
catch {
|
|
316
|
-
log(
|
|
316
|
+
log(`qrcode-terminal not installed — raw pairing code:\n${ qr }`)
|
|
317
317
|
}
|
|
318
318
|
}
|
package/src/cli.ts
CHANGED
|
@@ -50,74 +50,74 @@ its PMA artifact.`
|
|
|
50
50
|
async function main(): Promise<void> {
|
|
51
51
|
const sub = process.argv[2]
|
|
52
52
|
|
|
53
|
-
if( sub !== 'mcp' && sub !== 'serve' && sub !== 'discord' && sub !== 'whatsapp'
|
|
53
|
+
if( sub !== 'mcp' && sub !== 'serve' && sub !== 'discord' && sub !== 'whatsapp'){
|
|
54
54
|
console.error( sub ? `unknown subcommand: ${ sub }\n\n${ USAGE }` : USAGE )
|
|
55
55
|
process.exit( sub ? 2 : 0 )
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
// Fail on missing platform credentials BEFORE raising a mind.
|
|
59
59
|
if( sub === 'discord' && !process.env.DISCORD_BOT_TOKEN ){
|
|
60
|
-
console.error(
|
|
60
|
+
console.error('[will] DISCORD_BOT_TOKEN is required for `will discord` — create a bot at https://discord.com/developers/applications (enable the Message Content intent) and set the token.')
|
|
61
61
|
process.exit( 2 )
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
const { will, name, pmaPath, tickMs, anatomy, onCleanup, shutdown } = await bootWillFromEnv()
|
|
65
65
|
|
|
66
|
-
if( sub === 'mcp'
|
|
66
|
+
if( sub === 'mcp'){
|
|
67
67
|
// The MCP client owns our stdin — its disconnect is the shutdown signal.
|
|
68
|
-
process.stdin.on(
|
|
68
|
+
process.stdin.on('end', () => void shutdown('client disconnected') )
|
|
69
69
|
const server = buildWillMcpServer( will, { pmaPath } )
|
|
70
70
|
await server.connect( new StdioServerTransport() )
|
|
71
|
-
console.error(
|
|
71
|
+
console.error(`[will] ${ name } is listening on MCP stdio (tick ${ tickMs }ms, anatomy ${ anatomy })`)
|
|
72
72
|
return
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
if( sub === 'discord'
|
|
76
|
-
const csv = ( v?: string ) => v?.split(
|
|
75
|
+
if( sub === 'discord'){
|
|
76
|
+
const csv = ( v?: string ) => v?.split(',').map( s => s.trim() ).filter( Boolean )
|
|
77
77
|
const bridge = await connectDiscord( will, {
|
|
78
78
|
token: process.env.DISCORD_BOT_TOKEN!,
|
|
79
79
|
channels: csv( process.env.WILL_DISCORD_CHANNELS ),
|
|
80
|
-
mentionOnly: /^(1|true|yes)$/i.test( process.env.WILL_DISCORD_MENTION_ONLY ?? ''
|
|
80
|
+
mentionOnly: /^(1|true|yes)$/i.test( process.env.WILL_DISCORD_MENTION_ONLY ?? ''),
|
|
81
81
|
homeChannelId: process.env.WILL_DISCORD_HOME_CHANNEL,
|
|
82
|
-
rosterPath: pmaPath.replace( /(\.pma)?\.json$/, ''
|
|
82
|
+
rosterPath: pmaPath.replace( /(\.pma)?\.json$/, '') + '.discord.json',
|
|
83
83
|
} )
|
|
84
84
|
onCleanup( () => bridge.close() )
|
|
85
85
|
await bridge.start()
|
|
86
|
-
console.error(
|
|
86
|
+
console.error(`[will] ${ name } is present on Discord (tick ${ tickMs }ms, anatomy ${ anatomy }) — it speaks when it decides to.`)
|
|
87
87
|
return
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
if( sub === 'whatsapp'
|
|
91
|
-
const csv = ( v?: string ) => v?.split(
|
|
92
|
-
const stem = pmaPath.replace( /(\.pma)?\.json$/, ''
|
|
90
|
+
if( sub === 'whatsapp'){
|
|
91
|
+
const csv = ( v?: string ) => v?.split(',').map( s => s.trim() ).filter( Boolean )
|
|
92
|
+
const stem = pmaPath.replace( /(\.pma)?\.json$/, '')
|
|
93
93
|
// connectWhatsApp blocks here on first run until the printed QR is scanned.
|
|
94
94
|
const bridge = await connectWhatsApp( will, {
|
|
95
95
|
chats: csv( process.env.WILL_WHATSAPP_CHATS ),
|
|
96
|
-
mentionOnly: /^(1|true|yes)$/i.test( process.env.WILL_WHATSAPP_MENTION_ONLY ?? ''
|
|
96
|
+
mentionOnly: /^(1|true|yes)$/i.test( process.env.WILL_WHATSAPP_MENTION_ONLY ?? ''),
|
|
97
97
|
homeChatId: process.env.WILL_WHATSAPP_HOME_CHAT,
|
|
98
98
|
authPath: stem + '.wa-auth',
|
|
99
99
|
rosterPath: stem + '.whatsapp.json',
|
|
100
100
|
} )
|
|
101
101
|
onCleanup( () => bridge.close() )
|
|
102
102
|
await bridge.start()
|
|
103
|
-
console.error(
|
|
103
|
+
console.error(`[will] ${ name } is present on WhatsApp (tick ${ tickMs }ms, anatomy ${ anatomy }) — it speaks when it decides to.`)
|
|
104
104
|
return
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
// serve — the HTTP sidecar.
|
|
108
|
-
const port = parseInt( process.env.WILL_PORT ?? '7777'
|
|
108
|
+
const port = parseInt( process.env.WILL_PORT ?? '7777')
|
|
109
109
|
const host = process.env.WILL_HOST ?? '127.0.0.1'
|
|
110
110
|
const server = buildWillHttpServer( will, { pmaPath } )
|
|
111
111
|
onCleanup( () => new Promise<void>( r => server.close( () => r() ) ) )
|
|
112
112
|
await new Promise<void>( ( resolve, reject ) => {
|
|
113
|
-
server.once(
|
|
113
|
+
server.once('error', reject )
|
|
114
114
|
server.listen( port, host, () => resolve() )
|
|
115
115
|
} )
|
|
116
|
-
console.error(
|
|
117
|
-
console.error(
|
|
116
|
+
console.error(`[will] ${ name } is listening on http://${ host }:${ port } (tick ${ tickMs }ms, anatomy ${ anatomy })`)
|
|
117
|
+
console.error(`[will] try: curl -X POST http://${ host }:${ port }/perceive -H 'content-type: application/json' -d '{"text":"Hello"}'`)
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
main().catch( e => {
|
|
121
|
-
console.error(
|
|
121
|
+
console.error(`[will] fatal: ${ e instanceof Error ? e.stack ?? e.message : String( e ) }`)
|
|
122
122
|
process.exit( 1 )
|
|
123
123
|
} )
|