@mindot/will 0.8.0 → 0.10.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 +15 -11
- package/dist/index.d.ts +188 -141
- package/dist/index.js +6985 -3337
- package/dist/index.js.map +1 -1
- package/dist/surface/channels/discord.d.ts +177 -0
- package/dist/surface/channels/discord.js +394 -0
- package/dist/surface/channels/discord.js.map +1 -0
- package/dist/{channels → surface/channels}/whatsapp.d.ts +2 -2
- package/dist/{channels → surface/channels}/whatsapp.js +11 -5
- package/dist/surface/channels/whatsapp.js.map +1 -0
- package/dist/{cli.js → surface/cli.js} +5382 -1573
- package/dist/surface/cli.js.map +1 -0
- package/dist/{mcp → surface/mcp}/effectors.d.ts +1 -1
- package/dist/{mcp → surface/mcp}/effectors.js +10 -4
- package/dist/surface/mcp/effectors.js.map +1 -0
- package/dist/{will-cS6k4uiJ.d.ts → will-evj9_vrd.d.ts} +6658 -5078
- package/package.json +11 -12
- package/src/cognition/agency/consequence.ts +356 -1
- package/src/cognition/agency/conversation.aim.ts +292 -0
- package/src/cognition/agency/engines/action.selector.ts +142 -5
- package/src/cognition/agency/engines/affordance.synthesizer.ts +200 -7
- package/src/cognition/agency/engines/deliberation.engine.ts +76 -3
- package/src/cognition/agency/engines/motor.schema.executor.ts +509 -32
- package/src/cognition/agency/engines/reafference.engine.ts +160 -4
- package/src/cognition/agency/execution.primitives.ts +17 -4
- package/src/cognition/agency/proactive.communicator.ts +19 -3
- package/src/cognition/agency/reconcile.learning.ts +5 -4
- package/src/cognition/agency/restart.ts +66 -0
- package/src/cognition/agency/schemas/innate.ts +96 -1
- package/src/cognition/agency/schemas/repertoire.ts +19 -1
- package/src/cognition/agency/selection.scoring.ts +49 -0
- package/src/cognition/agency/settlement.ts +203 -0
- package/src/cognition/agency/types.ts +73 -1
- package/src/cognition/cache/composition.ts +232 -0
- package/src/cognition/cache/deliberation.cache.ts +219 -0
- package/src/cognition/cache/fingerprint.ts +120 -0
- package/src/cognition/cache/types.ts +105 -0
- package/src/cognition/config.mirror.entities.ts +108 -1
- package/src/cognition/event.schemas.ts +22 -8
- package/src/cognition/faculties/affective.blender.ts +21 -4
- package/src/cognition/faculties/autobiographical.narrator.ts +5 -10
- package/src/cognition/faculties/circadian.oscillator.ts +36 -7
- package/src/cognition/faculties/episodic.consolidator.ts +59 -3
- package/src/cognition/faculties/executive.engine/action.record.ts +129 -0
- package/src/cognition/faculties/executive.engine/commands.ts +189 -14
- package/src/cognition/faculties/executive.engine/context.ts +106 -46
- package/src/cognition/faculties/executive.engine/deliberate.reasoning.ts +1 -1
- package/src/cognition/faculties/executive.engine/engine.ts +714 -136
- package/src/cognition/faculties/executive.engine/escalation.buffer.ts +230 -45
- package/src/cognition/faculties/executive.engine/facet.supervisor.ts +320 -65
- package/src/cognition/faculties/executive.engine/facet.ts +81 -26
- package/src/cognition/faculties/executive.engine/gating.ts +14 -14
- package/src/cognition/faculties/executive.engine/parser.ts +120 -38
- package/src/cognition/faculties/executive.engine/prompt.factory.ts +331 -38
- package/src/cognition/faculties/executive.engine/types.ts +84 -12
- package/src/cognition/faculties/exteroception.ts +99 -98
- package/src/cognition/faculties/goal.manager.ts +144 -16
- package/src/cognition/faculties/known.entity.tracker.ts +287 -29
- package/src/cognition/faculties/moral.evaluator.ts +8 -3
- package/src/cognition/faculties/persona.consolidator.ts +141 -0
- package/src/cognition/faculties/planning.engine/engine.ts +4 -4
- package/src/cognition/faculties/planning.engine/plan.frontier.ts +1 -1
- package/src/cognition/faculties/reputation.tracker.ts +66 -2
- package/src/cognition/faculties/self.model.updater.ts +19 -12
- package/src/cognition/faculties/semantic.engine/integrator.ts +1 -1
- package/src/cognition/faculties/social.perception.ts +47 -3
- package/src/cognition/faculties/threat.evaluator.ts +7 -0
- package/src/cognition/faculties/working.memory.ts +16 -21
- package/src/cognition/identity.entity.ts +205 -0
- package/src/cognition/index.ts +7 -0
- package/src/cognition/instruction.handler.ts +1 -1
- package/src/cognition/memory/vector.adapter.ts +12 -3
- package/src/cognition/memory/vector.embedder.ts +45 -4
- package/src/cognition/percept.entity.ts +122 -0
- package/src/cognition/persona.prior.ts +6 -0
- package/src/cognition/sense.boundary.ts +176 -0
- package/src/cognition/senses/audition.engine/engine.ts +492 -53
- package/src/cognition/senses/base.sense.engine.ts +105 -7
- package/src/cognition/senses/index.ts +83 -13
- package/src/cognition/senses/provenance.ts +128 -0
- package/src/cognition/senses/somatosensation.engine.ts +117 -9
- package/src/cognition/social.identity.ts +296 -0
- package/src/cognition/utilities/token.tracker.ts +94 -10
- package/src/core/orchestrator.ts +51 -0
- package/src/index.ts +39 -5
- package/src/llm/gate.ts +48 -0
- package/src/llm/index.ts +127 -33
- package/src/llm/routing.ts +6 -0
- package/src/llm/summarizer.ts +1 -1
- package/src/llm/wire.contracts.ts +57 -0
- package/src/pma/index.ts +68 -54
- package/src/stem/assembly.audit.ts +1 -0
- package/src/stem/guards/identity.coherence.ts +1 -1
- package/src/stem/index.ts +171 -23
- package/src/stem/mind.ts +198 -57
- package/src/stem/policy/arbiter.ts +10 -0
- package/src/stem/profiles/built-in.ts +7 -0
- package/src/{profiles → stem/profiles}/companion.ts +1 -1
- package/src/{profiles → stem/profiles}/company-brain.ts +1 -1
- package/src/{profiles → stem/profiles}/customer-service.ts +1 -1
- package/src/{profiles → stem/profiles}/game-npc.ts +1 -1
- package/src/{profiles → stem/profiles}/index.ts +1 -1
- package/src/{profiles → stem/profiles}/smart-home.ts +1 -1
- package/src/stem/tracts/effector/escalation.lifecycle.ts +239 -0
- package/src/stem/tracts/effector/policy.enforcement.ts +261 -0
- package/src/stem/tracts/effector/types.ts +110 -0
- package/src/stem/tracts/effector.controller.ts +112 -401
- package/src/stem/tracts/outbox.controller.ts +30 -17
- package/src/stem/tracts/outbox.writer.ts +40 -2
- package/src/stem/tracts/sensory.controller.ts +6 -6
- package/src/stem/tracts/session.logger.ts +6 -1
- package/src/stem/tracts/transport/types.ts +20 -1
- package/src/stem/tracts/transport.controller.ts +33 -9
- package/src/surface/channels/discord.ts +609 -0
- package/src/{channels → surface/channels}/roster.ts +1 -1
- package/src/surface/channels/types.ts +136 -0
- package/src/{channels → surface/channels}/whatsapp.ts +22 -10
- package/src/{cli.ts → surface/cli.ts} +14 -9
- package/src/{host → surface/host}/boot.ts +3 -3
- package/src/{host → surface/host}/utterances.ts +2 -2
- package/src/{mcp → surface/mcp}/effectors.ts +19 -6
- package/src/{mcp → surface/mcp}/server.ts +19 -5
- package/src/{sdk → surface/sdk}/will.ts +194 -33
- package/src/{serve → surface/serve}/server.ts +11 -4
- package/src/types.ts +24 -3
- package/dist/channels/discord.d.ts +0 -69
- package/dist/channels/discord.js +0 -193
- package/dist/channels/discord.js.map +0 -1
- package/dist/channels/whatsapp.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/mcp/effectors.js.map +0 -1
- package/src/channels/discord.ts +0 -214
- package/src/channels/types.ts +0 -46
- package/src/cognition/faculties/executive.engine/messages.ts +0 -102
- package/src/extensions/livestream.ext.ts +0 -570
- package/src/extensions/time.ext.ts +0 -339
- package/src/profiles/built-in.ts +0 -7
- package/src/runners/coherence.runner.ts +0 -49
- package/src/runners/outreach.runner.ts +0 -155
- package/src/runners/social.runner.ts +0 -179
- package/src/runners/thin-shim.runner.ts +0 -205
- /package/dist/{cli.d.ts → surface/cli.d.ts} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/mcp/effectors.ts"],"names":[],"mappings":";;;;;AAwDA,IAAM,sBAAA,GAAyB,GAAA;AAE/B,IAAM,WAAA,GAAc,GAAA;AAOb,SAAS,gBAAiB,IAAA,EAA4B;AAC3D,EAAA,MAAM,KAAA,GAAW,IAAA,CAAK,WAAA,EAAa,UAAA,IAAc,EAAC;AAClD,EAAA,MAAM,WAAW,IAAI,GAAA,CAAK,KAAK,WAAA,EAAa,QAAA,IAAY,EAAG,CAAA;AAC3D,EAAA,MAAM,QAAA,GAAW,MAAA,CAAO,OAAA,CAAS,KAAM,CAAA,CAAE,GAAA,CAAK,CAAE,CAAE,GAAA,EAAK,CAAE,CAAA,KACvD,CAAA,EAAI,GAAI,CAAA,EAAI,QAAA,CAAS,GAAA,CAAK,GAAI,CAAA,GAAI,EAAA,GAAK,GAAI,CAAA,EAAI,CAAA,CAAE,WAAA,GAAc,CAAA,EAAA,EAAM,CAAA,CAAE,WAAY,CAAA,CAAA,GAAK,EAAG,CAAA,CAAE,CAAA;AAE/F,EAAA,MAAM,IAAA,GAAA,CAAS,IAAA,CAAK,WAAA,IAAe,CAAA,IAAA,EAAQ,IAAA,CAAK,IAAK,CAAA,MAAA,CAAA,EAAU,IAAA,EAAK,CAAE,OAAA,CAAS,MAAA,EAAQ,GAAG,CAAA;AAC1F,EAAA,MAAM,IAAA,GAAO,SAAS,MAAA,GAAS,CAAA,GAAI,iBAAa,QAAA,CAAS,IAAA,CAAK,IAAI,CAAE,CAAA,CAAA,CAAA,GAAM,EAAA;AAC1E,EAAA,MAAM,IAAA,GAAO,CAAA,EAAI,IAAK,CAAA,EAAI,IAAK,CAAA,CAAA;AAC/B,EAAA,OAAO,IAAA,CAAK,MAAA,GAAS,WAAA,GAAc,CAAA,EAAI,IAAA,CAAK,MAAO,CAAA,EAAG,WAAA,GAAc,CAAE,CAAE,CAAA,MAAA,CAAA,GAAM,IAAA;AAChF;AAOO,SAAS,eAAA,CAAiB,QAAgB,IAAA,EAAqC;AACpF,EAAA,OAAO,OAAQ,IAAA,KAAmC;AAChD,IAAA,MAAM,KAAA,GAAQ,KAAK,WAAA,EAAa,UAAA;AAGhC,IAAA,MAAM,WAAoC,EAAC;AAC3C,IAAA,KAAA,MAAW,CAAE,GAAG,CAAE,CAAA,IAAK,OAAO,OAAA,CAAS,IAAA,IAAQ,EAAG,CAAA;AAChD,MAAA,IAAI,CAAC,KAAA,IAAS,CAAA,IAAK,KAAA,EAAQ,QAAA,CAAU,CAAE,CAAA,GAAI,CAAA;AAE7C,IAAA,MAAM,OAAA,GAAA,CAAY,IAAA,CAAK,WAAA,EAAa,QAAA,IAAY,EAAC,EAAI,MAAA;AAAA,MACnD,OAAK,QAAA,CAAU,CAAE,MAAM,MAAA,IAAa,QAAA,CAAU,CAAE,CAAA,KAAM;AAAA,KAAE;AAC1D,IAAA,IAAI,QAAQ,MAAA,GAAS,CAAA;AACnB,MAAA,OAAO;AAAA,QACL,OAAA,EAAS,KAAA;AAAA,QACT,WAAA,EAAa,GAAI,IAAA,CAAK,IAAK,UAAW,OAAA,CAAQ,IAAA,CAAK,IAAI,CAAE,CAAA,mEAAA;AAAA,OAC3D;AAEF,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,GAAO,MAAM,MAAA,CAAO,QAAA,CAAU,EAAE,MAAM,IAAA,CAAK,IAAA,EAAM,SAAA,EAAW,QAAA,EAAW,CAAA;AAE7E,MAAA,MAAM,IAAA,GAAA,CAAS,GAAA,CAAI,OAAA,IAAW,EAAC,EAC5B,MAAA,CAAQ,CAAA,CAAA,KAAK,CAAA,CAAE,IAAA,KAAS,MAAA,IAAU,OAAO,CAAA,CAAE,IAAA,KAAS,QAAQ,CAAA,CAC5D,GAAA,CAAK,CAAA,CAAA,KAAK,CAAA,CAAE,IAAe,CAAA,CAC3B,IAAA,CAAK,IAAI,CAAA,CACT,IAAA,EAAK,KAAO,GAAA,CAAI,OAAA,GAAU,6BAAA,GAAgC,mBAAA,CAAA;AAC7D,MAAA,MAAM,OAAA,GAAU,IAAA,CAAK,MAAA,GAAS,sBAAA,GAAyB,CAAA,EAAI,IAAA,CAAK,KAAA,CAAO,CAAA,EAAG,sBAAA,GAAyB,CAAE,CAAE,CAAA,MAAA,CAAA,GAAM,IAAA;AAC7G,MAAA,OAAO,EAAE,OAAA,EAAS,CAAC,GAAA,CAAI,OAAA,EAAS,aAAa,OAAA,EAAQ;AAAA,IACvD,SACO,GAAA,EAAK;AACV,MAAA,OAAO,EAAE,OAAA,EAAS,KAAA,EAAO,WAAA,EAAa,GAAI,IAAA,CAAK,IAAK,CAAA,SAAA,EAAa,GAAA,YAAe,QAAQ,GAAA,CAAI,OAAA,GAAU,MAAA,CAAQ,GAAI,CAAE,CAAA,CAAA,EAAG;AAAA,IACzH;AAAA,EACF,CAAA;AACF;AAEA,eAAe,QAAS,MAAA,EAAsE;AAC5F,EAAA,IAAG,QAAA,IAAY,QAAS,OAAO,EAAE,QAAQ,MAAA,CAAO,MAAA,EAAQ,OAAO,KAAA,EAAM;AAErE,EAAA,MAAM,MAAA,GAAS,IAAI,MAAA,CAAQ,EAAE,MAAM,aAAA,EAAe,OAAA,EAAS,KAAM,CAAA;AACjE,EAAA,IAAG,KAAA,IAAS,MAAA;AACV,IAAA,MAAM,MAAA,CAAO,QAAS,IAAI,6BAAA,CAA+B,IAAI,GAAA,CAAK,MAAA,CAAO,GAAI,CAAE,CAAE,CAAA;AAAA;AAEjF,IAAA,MAAM,MAAA,CAAO,OAAA,CAAS,IAAI,oBAAA,CAAsB;AAAA,MAC9C,SAAS,MAAA,CAAO,OAAA;AAAA,MAChB,GAAK,OAAO,IAAA,GAAO,EAAE,MAAM,MAAA,CAAO,IAAA,KAAS,EAAC;AAAA;AAAA,MAE5C,GAAA,EAAK,EAAE,GAAG,qBAAA,IAAyB,GAAK,MAAA,CAAO,GAAA,IAAO,EAAC;AAAI,KAC3D,CAAE,CAAA;AACN,EAAA,OAAO,EAAE,MAAA,EAAQ,KAAA,EAAO,IAAA,EAAK;AAC/B;AAOA,eAAsB,mBAAA,CACpB,IAAA,EACA,MAAA,EACA,IAAA,GAA8B,EAAC,EAC2B;AAC1D,EAAA,MAAM,EAAE,MAAA,EAAQ,KAAA,EAAM,GAAI,MAAM,QAAS,MAAO,CAAA;AAChD,EAAA,MAAM,EAAE,KAAA,EAAM,GAAI,MAAM,OAAO,SAAA,EAAU;AAEzC,EAAA,MAAM,QAAkB,EAAC;AACzB,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,MAAM,OAAO,CAAA,EAAI,IAAA,CAAK,UAAU,EAAG,CAAA,EAAI,KAAK,IAAK,CAAA,CAAA;AACjD,IAAA,IAAA,CAAK,SAAU,IAAA,EAAM;AAAA,MACnB,WAAA,EAAa,gBAAiB,IAAK,CAAA;AAAA,MACnC,IAAA,EAAa,KAAK,IAAA,IAAQ,GAAA;AAAA,MAC1B,IAAA,EAAa,CAAE,KAAM,CAAA;AAAA,MACrB,OAAA,EAAa,eAAA,CAAiB,MAAA,EAAQ,IAAK;AAAA,KAC3C,CAAA;AACF,IAAA,KAAA,CAAM,KAAM,IAAK,CAAA;AAAA,EACnB;AAEA,EAAA,OAAO,EAAE,KAAA,EAAO,KAAA,EAAO,YAAY;AAAE,IAAA,IAAI,KAAA,EAAQ,MAAM,MAAA,CAAO,KAAA,EAAM;AAAA,EAAE,CAAA,EAAE;AAC1E","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"]}
|
package/src/channels/discord.ts
DELETED
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
// ─────────────────────────────────────────────────────────────
|
|
2
|
-
// src/channels/discord.ts — a Will present in a Discord server
|
|
3
|
-
// ─────────────────────────────────────────────────────────────
|
|
4
|
-
//
|
|
5
|
-
// The bridge relays both directions of the paradigm and nothing else:
|
|
6
|
-
//
|
|
7
|
-
// inbound guild/DM message → will.perceive({ from, speaker, text, thread })
|
|
8
|
-
// — every author is `discord:<userId>` (stable across guilds), the
|
|
9
|
-
// display name is *learned* by the mind, and each Discord channel
|
|
10
|
-
// is its own conversation thread.
|
|
11
|
-
// outbound will.on('message') → the addressee's last shared channel, else
|
|
12
|
-
// their DM, else the home channel. Proactive utterances (the mind
|
|
13
|
-
// speaking first) route the same way — that is the point.
|
|
14
|
-
//
|
|
15
|
-
// The Will decides when to speak. There is no command prefix and no forced
|
|
16
|
-
// reply: unaddressed chatter is perceived (salience-scored by audition) and
|
|
17
|
-
// silence is a valid outcome. `mentionOnly` narrows perception for busy
|
|
18
|
-
// servers; it does not turn the bridge into an ask() surface.
|
|
19
|
-
//
|
|
20
|
-
// discord.js is imported lazily inside `createDiscordClient` — tests (and any
|
|
21
|
-
// host that brings its own client) inject `client`, and the structural
|
|
22
|
-
// `DiscordLikeClient` type keeps the dependency out of the type graph.
|
|
23
|
-
// ─────────────────────────────────────────────────────────────
|
|
24
|
-
|
|
25
|
-
import type { Will, WillMessage } from '#sdk/will'
|
|
26
|
-
import { ChannelRoster } from '#channels/roster'
|
|
27
|
-
import { chunkText, type ChannelBridge } from '#channels/types'
|
|
28
|
-
|
|
29
|
-
const DISCORD_MESSAGE_LIMIT = 2000
|
|
30
|
-
|
|
31
|
-
// ── The slice of discord.js the bridge actually uses (structural) ───────────
|
|
32
|
-
|
|
33
|
-
export interface DiscordLikeChannel {
|
|
34
|
-
send( content: string ): Promise<unknown>
|
|
35
|
-
sendTyping?(): Promise<unknown>
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export interface DiscordLikeMessage {
|
|
39
|
-
content: string
|
|
40
|
-
cleanContent?: string
|
|
41
|
-
channelId: string
|
|
42
|
-
guildId?: string | null
|
|
43
|
-
author: { id: string; bot?: boolean; username?: string; displayName?: string }
|
|
44
|
-
member?: { displayName?: string } | null
|
|
45
|
-
mentions?: { has( userId: string ): boolean }
|
|
46
|
-
channel: DiscordLikeChannel
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export interface DiscordLikeClient {
|
|
50
|
-
user: { id: string; setPresence?( p: unknown ): void } | null
|
|
51
|
-
/** discord.js ≥14.22; polled so we needn't subscribe to the deprecated `ready`. */
|
|
52
|
-
isReady?(): boolean
|
|
53
|
-
on( event: 'messageCreate', fn: ( m: DiscordLikeMessage ) => void ): unknown
|
|
54
|
-
once( event: string, fn: () => void ): unknown
|
|
55
|
-
login( token: string ): Promise<unknown>
|
|
56
|
-
destroy(): Promise<unknown> | void
|
|
57
|
-
channels: { fetch( id: string ): Promise<unknown> }
|
|
58
|
-
users: { fetch( id: string ): Promise<{ send( content: string ): Promise<unknown> }> }
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// ── Options ──────────────────────────────────────────────────────────────────
|
|
62
|
-
|
|
63
|
-
export interface DiscordBridgeOptions {
|
|
64
|
-
/** Bot token (Discord developer portal). Unused when `client` is injected pre-logged-in. */
|
|
65
|
-
token?: string
|
|
66
|
-
/** Channel ids the Will inhabits. Unset = every channel it can see. */
|
|
67
|
-
channels?: string[]
|
|
68
|
-
/** Perceive guild messages only when the Will is @mentioned (DMs always perceived). */
|
|
69
|
-
mentionOnly?: boolean
|
|
70
|
-
/** Fallback channel for utterances with no reachable addressee. */
|
|
71
|
-
homeChannelId?: string
|
|
72
|
-
/** Roster path (default: ./.will/<willId>.discord.json). */
|
|
73
|
-
rosterPath?: string
|
|
74
|
-
/** Test / power-user seam: bring your own client; discord.js is never imported. */
|
|
75
|
-
client?: DiscordLikeClient
|
|
76
|
-
log?: ( msg: string ) => void
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// ── The bridge ───────────────────────────────────────────────────────────────
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Connect a Will to Discord. Resolves once the bridge is live (logged in and
|
|
83
|
-
* relaying). Close it via the returned `ChannelBridge.close()` — the Will
|
|
84
|
-
* itself is not stopped; it simply loses this surface.
|
|
85
|
-
*/
|
|
86
|
-
export async function connectDiscord( will: Will, opts: DiscordBridgeOptions ): Promise<ChannelBridge> {
|
|
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
|
-
const allowed = opts.channels?.length ? new Set( opts.channels ) : null
|
|
90
|
-
|
|
91
|
-
const client = opts.client ?? await createDiscordClient()
|
|
92
|
-
|
|
93
|
-
/** The most recently active allowed channel — last-resort proactive target. */
|
|
94
|
-
let lastActiveChannelId: string | null = opts.homeChannelId ?? null
|
|
95
|
-
|
|
96
|
-
// ── inbound: platform message → stimulus ──────────────────────────────────
|
|
97
|
-
client.on('messageCreate', message => { void onMessage( message ) } )
|
|
98
|
-
|
|
99
|
-
async function onMessage( message: DiscordLikeMessage ): Promise<void> {
|
|
100
|
-
const self = client.user
|
|
101
|
-
if( !self || message.author.id === self.id || message.author.bot ) return
|
|
102
|
-
|
|
103
|
-
const isDM = !message.guildId
|
|
104
|
-
if( !isDM && allowed && !allowed.has( message.channelId ) ) return
|
|
105
|
-
|
|
106
|
-
const addressed = isDM || ( message.mentions?.has( self.id ) ?? false )
|
|
107
|
-
if( opts.mentionOnly && !addressed ) return
|
|
108
|
-
|
|
109
|
-
const entityId = `discord:${ message.author.id }`
|
|
110
|
-
const speaker = message.member?.displayName ?? message.author.displayName ?? message.author.username
|
|
111
|
-
|
|
112
|
-
roster.record( {
|
|
113
|
-
entityId,
|
|
114
|
-
userId: message.author.id,
|
|
115
|
-
...( speaker ? { displayName: speaker } : {} ),
|
|
116
|
-
...( isDM ? { dmChannelId: message.channelId } : { lastChannelId: message.channelId } ),
|
|
117
|
-
} )
|
|
118
|
-
if( !isDM ) lastActiveChannelId = message.channelId
|
|
119
|
-
|
|
120
|
-
// Being addressed is the one moment a presence cue is honest — the mind
|
|
121
|
-
// may still choose silence, and typing expires on its own.
|
|
122
|
-
if( addressed ) await message.channel.sendTyping?.().catch( () => {} )
|
|
123
|
-
|
|
124
|
-
const text = message.cleanContent || message.content
|
|
125
|
-
if( !text.trim() ) return
|
|
126
|
-
|
|
127
|
-
await will.perceive( {
|
|
128
|
-
text,
|
|
129
|
-
from: entityId,
|
|
130
|
-
thread: `discord:${ message.channelId }`,
|
|
131
|
-
...( speaker ? { speaker } : {} ),
|
|
132
|
-
} )
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// ── outbound: projected utterance → the addressee ─────────────────────────
|
|
136
|
-
// The facade has no off(); the bridge gates its handler on `closed` instead.
|
|
137
|
-
let closed = false
|
|
138
|
-
will.on('message', ( m: WillMessage ) => { if( !closed ) void deliver( m ) } )
|
|
139
|
-
|
|
140
|
-
async function deliver( m: WillMessage ): Promise<void> {
|
|
141
|
-
const peer = m.to ? roster.resolve( m.to ) : undefined
|
|
142
|
-
const chunks = chunkText( m.content, DISCORD_MESSAGE_LIMIT )
|
|
143
|
-
|
|
144
|
-
// Preference order: where we last shared a room → their DM → home channel.
|
|
145
|
-
const channelIds = [ peer?.lastChannelId, peer?.dmChannelId, opts.homeChannelId ?? undefined, lastActiveChannelId ?? undefined ]
|
|
146
|
-
for( const id of channelIds ){
|
|
147
|
-
if( !id ) continue
|
|
148
|
-
try {
|
|
149
|
-
const channel = await client.channels.fetch( id ) as DiscordLikeChannel | null
|
|
150
|
-
if( !channel?.send ) continue
|
|
151
|
-
for( const chunk of chunks ) await channel.send( chunk )
|
|
152
|
-
return
|
|
153
|
-
}
|
|
154
|
-
catch { /* try the next route */ }
|
|
155
|
-
}
|
|
156
|
-
if( peer ){
|
|
157
|
-
try {
|
|
158
|
-
const user = await client.users.fetch( peer.userId )
|
|
159
|
-
for( const chunk of chunks ) await user.send( chunk )
|
|
160
|
-
return
|
|
161
|
-
}
|
|
162
|
-
catch { /* fall through */ }
|
|
163
|
-
}
|
|
164
|
-
log(`no route for utterance to '${ m.to }' — dropped (${ m.content.length } chars)`)
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// ── lifecycle ──────────────────────────────────────────────────────────────
|
|
168
|
-
const bridge: ChannelBridge = {
|
|
169
|
-
kind: 'discord',
|
|
170
|
-
async start(): Promise<void> {
|
|
171
|
-
if( !client.user ){
|
|
172
|
-
// discord.js ≥14.22 renamed `ready` → `clientReady`. Subscribing to the
|
|
173
|
-
// old name is what triggers its DeprecationWarning, so we take the new
|
|
174
|
-
// name and poll `isReady()` for older builds rather than listening.
|
|
175
|
-
const ready = new Promise<void>( resolve => {
|
|
176
|
-
let poll: ReturnType<typeof setInterval> | null = null
|
|
177
|
-
const done = (): void => { if( poll ) clearInterval( poll ); resolve() }
|
|
178
|
-
client.once('clientReady', done )
|
|
179
|
-
poll = setInterval( () => { if( client.isReady?.() ) done() }, 100 )
|
|
180
|
-
poll.unref?.()
|
|
181
|
-
} )
|
|
182
|
-
await client.login( opts.token ?? '')
|
|
183
|
-
await ready
|
|
184
|
-
}
|
|
185
|
-
log(`${ will.name } is present on Discord as user ${ client.user?.id }`)
|
|
186
|
-
},
|
|
187
|
-
async close(): Promise<void> {
|
|
188
|
-
if( closed ) return
|
|
189
|
-
closed = true
|
|
190
|
-
roster.flush()
|
|
191
|
-
await Promise.resolve( client.destroy() ).catch( () => {} )
|
|
192
|
-
},
|
|
193
|
-
}
|
|
194
|
-
return bridge
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/** Build a real discord.js client (lazy import keeps it out of non-Discord hosts). */
|
|
198
|
-
async function createDiscordClient(): Promise<DiscordLikeClient> {
|
|
199
|
-
let mod: typeof import('discord.js')
|
|
200
|
-
try { mod = await import('discord.js') }
|
|
201
|
-
catch {
|
|
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
|
-
}
|
|
204
|
-
const { Client, GatewayIntentBits, Partials } = mod
|
|
205
|
-
return new Client( {
|
|
206
|
-
intents: [
|
|
207
|
-
GatewayIntentBits.Guilds,
|
|
208
|
-
GatewayIntentBits.GuildMessages,
|
|
209
|
-
GatewayIntentBits.MessageContent,
|
|
210
|
-
GatewayIntentBits.DirectMessages,
|
|
211
|
-
],
|
|
212
|
-
partials: [ Partials.Channel ], // DMs arrive on uncached channels
|
|
213
|
-
} ) as unknown as DiscordLikeClient
|
|
214
|
-
}
|
package/src/channels/types.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
// ─────────────────────────────────────────────────────────────
|
|
2
|
-
// src/channels/types.ts — the channel-bridge contract
|
|
3
|
-
// ─────────────────────────────────────────────────────────────
|
|
4
|
-
//
|
|
5
|
-
// A channel bridge puts a Will *in a place where people already are* (Discord,
|
|
6
|
-
// Telegram, Slack, …). It is a host surface, not a cognition surface: it turns
|
|
7
|
-
// platform messages into `perceive` stimuli and delivers the Will's projected
|
|
8
|
-
// utterances back — nothing more. The paradigm survives the crossing:
|
|
9
|
-
//
|
|
10
|
-
// • every platform user is an entity the Will comes to know (`from`),
|
|
11
|
-
// with a *learned* name (`speaker`) — never a placeholder;
|
|
12
|
-
// • every platform channel/DM is a conversation thread (`thread`);
|
|
13
|
-
// • the Will decides when to speak. Silence is a valid outcome, so a
|
|
14
|
-
// bridge never fabricates a reply and never times a message out into
|
|
15
|
-
// an error.
|
|
16
|
-
//
|
|
17
|
-
// Bridges live at the same altitude as the MCP/HTTP hosts (src/mcp, src/serve):
|
|
18
|
-
// they wrap the SDK facade, not the stem.
|
|
19
|
-
// ─────────────────────────────────────────────────────────────
|
|
20
|
-
|
|
21
|
-
/** A running connection between one Will and one platform. */
|
|
22
|
-
export interface ChannelBridge {
|
|
23
|
-
/** Platform kind, e.g. 'discord'. */
|
|
24
|
-
readonly kind: string
|
|
25
|
-
/** Connect and start relaying. Resolves once the bridge is live. */
|
|
26
|
-
start(): Promise<void>
|
|
27
|
-
/** Disconnect and release resources. Idempotent. */
|
|
28
|
-
close(): Promise<void>
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** Split a message into platform-sized chunks on natural boundaries. */
|
|
32
|
-
export function chunkText( text: string, max: number ): string[] {
|
|
33
|
-
if( text.length <= max ) return [ text ]
|
|
34
|
-
const chunks: string[] = []
|
|
35
|
-
let rest = text
|
|
36
|
-
while( rest.length > max ){
|
|
37
|
-
// Prefer a paragraph break, then a line break, then a space — else hard-cut.
|
|
38
|
-
const window = rest.slice( 0, max )
|
|
39
|
-
const cut = Math.max( window.lastIndexOf('\n\n'), window.lastIndexOf('\n'), window.lastIndexOf(' ') )
|
|
40
|
-
const at = cut > max * 0.5 ? cut : max
|
|
41
|
-
chunks.push( rest.slice( 0, at ).trimEnd() )
|
|
42
|
-
rest = rest.slice( at ).trimStart()
|
|
43
|
-
}
|
|
44
|
-
if( rest ) chunks.push( rest )
|
|
45
|
-
return chunks
|
|
46
|
-
}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
// ─────────────────────────────────────────────────────────────
|
|
2
|
-
// src/cognition/faculties/executive.engine/messages.ts
|
|
3
|
-
// ─────────────────────────────────────────────────────────────
|
|
4
|
-
|
|
5
|
-
import { logger } from '#core/logger'
|
|
6
|
-
import type { Tick, ReadonlySimulationState } from '#core/types'
|
|
7
|
-
import type { PendingMessage } from '#faculties/executive.engine/types'
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Message queue state — tracks incoming communication entities,
|
|
11
|
-
* pending messages during in-flight LLM calls, and reply deduplication.
|
|
12
|
-
*/
|
|
13
|
-
export class MessageQueue {
|
|
14
|
-
/** Messages buffered while an LLM call is in-flight. */
|
|
15
|
-
pendingMessages: PendingMessage[] = []
|
|
16
|
-
|
|
17
|
-
/** Tick when the current (or most recent) LLM call started. */
|
|
18
|
-
pendingCallStartTick: number = -1
|
|
19
|
-
|
|
20
|
-
/** Entity IDs of communication entities that have been replied to this session. */
|
|
21
|
-
private _repliedEntityIds = new Set<string>()
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Scan the simulation state for unprocessed communication entities
|
|
25
|
-
* and queue them into pendingMessages. Call every tick.
|
|
26
|
-
*/
|
|
27
|
-
scanState( state: ReadonlySimulationState, tick: Tick ): void {
|
|
28
|
-
const seenIds = new Set( this.pendingMessages.map( m => m.id ) )
|
|
29
|
-
|
|
30
|
-
for( const [ id, entity ] of state.entities ){
|
|
31
|
-
if( entity.type !== 'communication') continue
|
|
32
|
-
if( entity.metadata?.processedByExecutive ) continue
|
|
33
|
-
if( seenIds.has( id ) ) continue
|
|
34
|
-
|
|
35
|
-
const msgTick = ( entity.metadata?.tick as number ) ?? 0
|
|
36
|
-
this.pendingMessages.push({
|
|
37
|
-
id,
|
|
38
|
-
content: (entity.metadata?.content as string) ?? '',
|
|
39
|
-
sender: (entity.metadata?.agentName as string) ?? 'unknown',
|
|
40
|
-
senderId: (entity.metadata?.keid as string) ?? 'unknown',
|
|
41
|
-
tick: msgTick
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
logger.info(`[executive] queued message from ${(entity.metadata?.agentName as string) ?? 'unknown'} (tick=${msgTick})`)
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Clear messages that were included in the most recent LLM call.
|
|
50
|
-
* Any that arrived after the call started remain for the next cycle.
|
|
51
|
-
*/
|
|
52
|
-
clearProcessedMessages(): void {
|
|
53
|
-
this.pendingMessages = this.pendingMessages.filter( m => m.tick > this.pendingCallStartTick )
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Check if we've already replied to a specific communication entity this session.
|
|
58
|
-
*/
|
|
59
|
-
hasRepliedTo( entityId: string ): boolean {
|
|
60
|
-
return this._repliedEntityIds.has( entityId )
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Mark a communication entity as replied to.
|
|
65
|
-
*/
|
|
66
|
-
markReplied( entityId: string ): void {
|
|
67
|
-
this._repliedEntityIds.add( entityId )
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Build the set of communication entity IDs visible in state within the
|
|
72
|
-
* 30-tick window, plus any pending messages. Used to mark them as processed.
|
|
73
|
-
*/
|
|
74
|
-
getVisibleMessageIds( state: ReadonlySimulationState, tick: Tick ): Set<string> {
|
|
75
|
-
const ids = new Set<string>( this.pendingMessages.map( m => m.id ) )
|
|
76
|
-
for( const [ id, entity ] of state.entities ){
|
|
77
|
-
if( entity.type !== 'communication') continue
|
|
78
|
-
|
|
79
|
-
const msgTick = (entity.metadata?.tick as number) ?? 0
|
|
80
|
-
if( tick - msgTick > 30 ) continue
|
|
81
|
-
|
|
82
|
-
ids.add( id )
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return ids
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Get stale communication entity IDs (>50 ticks old) for cleanup.
|
|
90
|
-
*/
|
|
91
|
-
getStaleMessageIds( state: ReadonlySimulationState, tick: Tick ): string[] {
|
|
92
|
-
const stale: string[] = []
|
|
93
|
-
for( const [ id, entity ] of state.entities ){
|
|
94
|
-
if( entity.type !== 'communication') continue
|
|
95
|
-
|
|
96
|
-
const msgTick = (entity.metadata?.tick as number) ?? 0
|
|
97
|
-
tick - msgTick > 50 && stale.push( id )
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return stale
|
|
101
|
-
}
|
|
102
|
-
}
|