@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,5 +1,5 @@
|
|
|
1
1
|
// ─────────────────────────────────────────────────────────────
|
|
2
|
-
// src/sdk/will.ts — the ergonomic facade
|
|
2
|
+
// src/surface/sdk/will.ts — the ergonomic facade
|
|
3
3
|
// ─────────────────────────────────────────────────────────────
|
|
4
4
|
//
|
|
5
5
|
// `WillStem` is the full, powerful contract (tick listeners, the outbox drain,
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
// const will = await Will.create({ name: 'Aria', identity: {...} })
|
|
10
10
|
// will.on('message', m => console.log(m.content))
|
|
11
11
|
// will.effector('search_docs', async a => await myDb.search(a.query))
|
|
12
|
-
// await will.
|
|
12
|
+
// await will.sense({ from: 'ada', text: 'What should we work on?', provenance: 'exafferent' })
|
|
13
13
|
// const reply = await will.nextUtterance({ to: 'ada' }) // WillMessage | null
|
|
14
14
|
// const pma = await will.save() // non-destructive; keeps ticking
|
|
15
15
|
//
|
|
16
|
-
// A Will is a *subject*, not a function: you `
|
|
16
|
+
// A Will is a *subject*, not a function: you `sense` stimuli to it and observe
|
|
17
17
|
// its *projections* (message / effector / emotion / state) — you never `await`
|
|
18
18
|
// a computed return. `nextUtterance` is a thin, honest adapter for callers that
|
|
19
19
|
// want a reply-shaped await; `null` means the Will chose silence, not an error.
|
|
@@ -33,12 +33,14 @@ import { PROVIDER_KEY_ENV, providerKeyFromEnv, type LLMProvider } from '#llm/ind
|
|
|
33
33
|
import type { PMASnapshot } from '#pma/index'
|
|
34
34
|
import type { effectorInvocation } from '#types'
|
|
35
35
|
import type { EffectorDeclaration, SchemaPrecondition } from '#agency/types'
|
|
36
|
+
import type { PolicyArbiter } from '#stem/policy/arbiter'
|
|
37
|
+
import type { SignalProvenance } from '#senses/provenance'
|
|
36
38
|
|
|
37
39
|
// ── Public surface ────────────────────────────────────────────
|
|
38
40
|
|
|
39
41
|
/**
|
|
40
42
|
* A stimulus entering the Will's sensory field. A Will is a subject, not a
|
|
41
|
-
* function: you don't *call* it with input and await a return — you `
|
|
43
|
+
* function: you don't *call* it with input and await a return — you `sense`
|
|
42
44
|
* something to it, and it *may* project a response later (see `nextUtterance`),
|
|
43
45
|
* coloured by its current state. Silence is a valid, meaningful outcome.
|
|
44
46
|
*/
|
|
@@ -54,8 +56,39 @@ export interface Stimulus {
|
|
|
54
56
|
* name, the Will knows the person as "someone" until it learns one.
|
|
55
57
|
*/
|
|
56
58
|
speaker?: string
|
|
59
|
+
/**
|
|
60
|
+
* Whose doing this was: `'exafferent'` (the world), `'reafferent'` (the Will's
|
|
61
|
+
* own act, coming back), or `'unknown'` (you cannot tell).
|
|
62
|
+
*
|
|
63
|
+
* REQUIRED, as of P3. Only you can answer it — nothing inside the mind can
|
|
64
|
+
* tell the echo of its own utterance from a stranger saying the same words,
|
|
65
|
+
* which is why this is asserted at the boundary and never inferred behind it.
|
|
66
|
+
* Most inbound traffic is `'exafferent'`; use `say()`/`tell()`, whose verbs
|
|
67
|
+
* already make that claim, when that is all you mean. Reach for `'reafferent'`
|
|
68
|
+
* when you are feeding back the result of something the Will did — an
|
|
69
|
+
* ability's output, a webhook fired by its own write, a platform echo of a
|
|
70
|
+
* message it sent — and pass `sourceIntentId` if you have it.
|
|
71
|
+
*
|
|
72
|
+
* It was optional for exactly one epoch, defaulting to `'exafferent'`, so that
|
|
73
|
+
* a host migrated ONCE — at P3, alongside the `perceive()` → `sense()` rename
|
|
74
|
+
* — rather than twice. That default was the last surviving instance of the
|
|
75
|
+
* four-state hole the internal types exist to forbid: an omission silently
|
|
76
|
+
* became a claim nobody made. It is gone.
|
|
77
|
+
*
|
|
78
|
+
* If you genuinely cannot tell, say `'unknown'`. That is a different statement
|
|
79
|
+
* from `'exafferent'` and the mind treats it as one: the rupture gate in
|
|
80
|
+
* `action.selector` counts only `'exafferent'` percepts, so a mislabelled echo
|
|
81
|
+
* can interrupt a mind's train of thought with its own words.
|
|
82
|
+
*/
|
|
83
|
+
provenance: SignalProvenance
|
|
84
|
+
/** The intent whose enaction caused this, when `provenance` is `'reafferent'`. */
|
|
85
|
+
sourceIntentId?: string
|
|
57
86
|
/** Conversation/thread id (default = `from`). */
|
|
58
87
|
thread?: string
|
|
88
|
+
/** True when `thread` is private — just this someone and the Will. See TextMessage.direct. */
|
|
89
|
+
direct?: boolean
|
|
90
|
+
/** What the room is called, e.g. `#general`. A label, not an address. See TextMessage.threadName. */
|
|
91
|
+
threadName?: string
|
|
59
92
|
}
|
|
60
93
|
|
|
61
94
|
/** A message the Will emitted to someone. */
|
|
@@ -66,6 +99,20 @@ export interface WillMessage {
|
|
|
66
99
|
content: string
|
|
67
100
|
/** Entity id the Will addressed (the speaker you used in say()/tell(), or a bond). */
|
|
68
101
|
to: string
|
|
102
|
+
/**
|
|
103
|
+
* The conversation this belongs to — the `thread` from the `sense()` that
|
|
104
|
+
* prompted it. Absent when the Will spoke unprompted, which genuinely has no
|
|
105
|
+
* thread.
|
|
106
|
+
*
|
|
107
|
+
* WHERE, not just to whom. The engine knew this the whole way down —
|
|
108
|
+
* `OutboxMessage.threadId` carries it — and the projection dropped it here, so
|
|
109
|
+
* a channel adapter had nothing to answer INTO and had to guess from a roster.
|
|
110
|
+
* Observed live: a DM arrived on `discord:1532693…`, she answered it correctly
|
|
111
|
+
* and in seconds, and the reply went to the shared server channel because that
|
|
112
|
+
* was the last room the roster had seen this person in. From the operator's
|
|
113
|
+
* side she had simply ignored him.
|
|
114
|
+
*/
|
|
115
|
+
thread?: string
|
|
69
116
|
}
|
|
70
117
|
|
|
71
118
|
/**
|
|
@@ -97,14 +144,75 @@ export interface WillAffect {
|
|
|
97
144
|
*/
|
|
98
145
|
export type EffectorResult = string | {
|
|
99
146
|
success: boolean
|
|
147
|
+
/** How the act WENT — its fate. What the Will learns competence from. */
|
|
100
148
|
description: string
|
|
149
|
+
/**
|
|
150
|
+
* What the act REVEALED — new information about the world (SIGNAL_BOUNDARY P2).
|
|
151
|
+
*
|
|
152
|
+
* Return it and the Will *perceives* it: it arrives as a reafferent percept
|
|
153
|
+
* tied to the act by `sourceIntentId`, so it is remembered and recallable,
|
|
154
|
+
* not merely learned from. A lookup, a listing, a snapshot has one; a kick or
|
|
155
|
+
* a warning does not — those only have a fate.
|
|
156
|
+
*
|
|
157
|
+
* return { success: true,
|
|
158
|
+
* description: 'Looked up Ada.', // how it went
|
|
159
|
+
* observation: 'Ada joined 3 months ago, …' } // what I found
|
|
160
|
+
*
|
|
161
|
+
* Any shape — a string, a record, a list — and carried WHOLE.
|
|
162
|
+
*
|
|
163
|
+
* SEND WHAT YOU HAVE, NOT WHAT IT MEANS. You are not asked to summarise, and
|
|
164
|
+
* you should not: making meaning by connecting pieces of information is the
|
|
165
|
+
* mind's entire job, and a host that hands over a conclusion has done that
|
|
166
|
+
* work on the wrong side of the boundary. A robot's vision layer reports
|
|
167
|
+
* `{ object: 'ball', confidence: 0.9, bbox: […] }`; it has no business
|
|
168
|
+
* deciding whether that is worth reacting to.
|
|
169
|
+
*
|
|
170
|
+
* The mind labels it with the ability's own name, and sees the data itself in
|
|
171
|
+
* its percepts — state, working memory and the prompt all carry it. If you
|
|
172
|
+
* happen to have a one-line `summary` on your object it is used as the label,
|
|
173
|
+
* but that is a convenience and never a requirement.
|
|
174
|
+
*
|
|
175
|
+
* SIZE IS YOURS TO JUDGE, AND NOTHING TRUNCATES IT. What you send lands in
|
|
176
|
+
* the mind's percepts and, briefly, its prompt — so a very large payload
|
|
177
|
+
* costs tokens on the ticks it is alive. The engine will not second-guess you
|
|
178
|
+
* by cutting it: a cap here decides for a mind how much of an answer it may
|
|
179
|
+
* have, and that decision is not the engine's to make. Send the record; send
|
|
180
|
+
* the field you would want it to notice.
|
|
181
|
+
*
|
|
182
|
+
* Before this, a host with facts to hand back had to return the ack AND call
|
|
183
|
+
* `sense()` separately — two calls for one act, and the second one had to
|
|
184
|
+
* pretend somebody had spoken.
|
|
185
|
+
*/
|
|
186
|
+
observation?: unknown
|
|
101
187
|
metrics?: Record<string, number>
|
|
102
188
|
}
|
|
103
189
|
|
|
104
190
|
/** Your implementation of an ability the Will can choose to use. */
|
|
105
191
|
export type EffectorHandler = (
|
|
106
192
|
args: Record<string, unknown>,
|
|
107
|
-
ctx: {
|
|
193
|
+
ctx: {
|
|
194
|
+
reasoning: string
|
|
195
|
+
/**
|
|
196
|
+
* The `agency.intent` this handler is running under — the correlation handle
|
|
197
|
+
* the Will will match an ack to (SIGNAL_BOUNDARY P1).
|
|
198
|
+
*
|
|
199
|
+
* Pass it as `sourceIntentId` on any `sense()` you make from inside a
|
|
200
|
+
* handler, and the resulting percept is a *reafference* the mind can tie back
|
|
201
|
+
* to the act that caused it, rather than an unexplained arrival. Before this
|
|
202
|
+
* existed, `discord_inspect_channel` had to say so in English — a bracketed
|
|
203
|
+
* `[I looked into #general: …]` that only the LLM could read — because the
|
|
204
|
+
* fact had nowhere structural to live.
|
|
205
|
+
*/
|
|
206
|
+
intentId: string
|
|
207
|
+
targetEntityId?: string
|
|
208
|
+
/**
|
|
209
|
+
* The addresses this host knows `targetEntityId` by — a channel id, a user id.
|
|
210
|
+
* `targetEntityId` itself is an opaque anchor (who something IS); these are
|
|
211
|
+
* where to find it. Resolved inside the Will, where the alias table lives.
|
|
212
|
+
*/
|
|
213
|
+
targetAddresses?: readonly string[]
|
|
214
|
+
description?: string
|
|
215
|
+
},
|
|
108
216
|
) => EffectorResult | Promise<EffectorResult>
|
|
109
217
|
|
|
110
218
|
/**
|
|
@@ -164,12 +272,6 @@ export interface CreateWillOptions {
|
|
|
164
272
|
identity: Partial<WillIdentity> & { prompt: string }
|
|
165
273
|
/** 'mind' (default: the whole architecture) | 'reflex' (no-LLM shell). */
|
|
166
274
|
anatomy?: Anatomy
|
|
167
|
-
/** Concrete LLM model id, or a per-role map ({ executive, summarizer?,
|
|
168
|
-
* deliberation?, embedding? } — unset thinking roles fall back to executive).
|
|
169
|
-
* Unset → env / provider default.
|
|
170
|
-
* @deprecated Pass `llmConfig: { model }` instead — model and transport are
|
|
171
|
-
* one concern. Still honoured; an explicit `llmConfig.model` wins. */
|
|
172
|
-
model?: string | WillModelConfig
|
|
173
275
|
/** Per-Will LLM config: provider, model(s), BYO apiKey, baseUrl, caps.
|
|
174
276
|
* Unset fields fall back to WILL_LLM_* envs. apiKey stays in memory only.
|
|
175
277
|
* (Named llmConfig because `llm` is the provider MODE switch.) */
|
|
@@ -319,17 +421,29 @@ export class Will {
|
|
|
319
421
|
pma: PMASnapshot,
|
|
320
422
|
opts: Omit<CreateWillOptions, 'identity'> & { identity?: Partial<WillIdentity> },
|
|
321
423
|
): Promise<Will> {
|
|
322
|
-
|
|
424
|
+
// Waking IS being the same mind, so the id continues from the artifact unless the
|
|
425
|
+
// caller overrides it. The id is the path key for everything durable that lives
|
|
426
|
+
// OUTSIDE the artifact — `data/wills/<id>/vector_index`, snapshots, session logs.
|
|
427
|
+
// Minting a fresh `name-<random>` here gave a woken mind a brand-new, empty vector
|
|
428
|
+
// store every boot: identity/beliefs/goals returned (those are in the artifact) while
|
|
429
|
+
// episodic recall came back permanently empty, and the orphaned index was left behind
|
|
430
|
+
// on disk. Observed: four boots, four `lora-*` directories, four 4KB indexes, and a
|
|
431
|
+
// mind that concluded at 100% confidence that its own channel might not be viable
|
|
432
|
+
// because it could not recall a conversation it had just had.
|
|
433
|
+
const id = opts.id ?? pma.willId ?? `${slug( opts.name )}-${Math.random().toString( 36 ).slice( 2, 8 )}`
|
|
323
434
|
const stem = new WillStem()
|
|
324
435
|
const will = new Will( stem, id, opts.name )
|
|
325
436
|
|
|
326
437
|
for( const [ name, entry ] of Object.entries( opts.effectors ?? {} ) )
|
|
327
438
|
will._register( name, entry )
|
|
328
439
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
440
|
+
const config = will._buildConfig( id, { ...opts, identity: { prompt: '', ...opts.identity } } )
|
|
441
|
+
// Say out loud that this identity is a placeholder. Otherwise the creation
|
|
442
|
+
// guard warns about the empty persona we just constructed on purpose — three
|
|
443
|
+
// alarms per wake, drowning the real check that runs when the artifact loads.
|
|
444
|
+
config.identityFromArtifact = true
|
|
445
|
+
|
|
446
|
+
await stem.createWill( config, true /* startPaused — load the artifact before the first tick */ )
|
|
333
447
|
stem.loadPMA( id, pma )
|
|
334
448
|
stem.resumeWill( id )
|
|
335
449
|
will._attach()
|
|
@@ -344,10 +458,18 @@ export class Will {
|
|
|
344
458
|
* *delivered*, NOT once the Will has responded: a response (if any) is a
|
|
345
459
|
* projection that arrives later on the `message` event, or via
|
|
346
460
|
* `nextUtterance()`. The Will may also stay silent — that is not an error.
|
|
461
|
+
*
|
|
462
|
+
* NAMED `sense`, NOT `perceive` (SIGNAL_BOUNDARY P3). What happens here is
|
|
463
|
+
* *transduction*: a signal crosses into the mind. PERCEPTION is what a sense
|
|
464
|
+
* engine does afterwards — audition runs, judges salience, and produces a
|
|
465
|
+
* `Percept`, which may not resemble what arrived and may not happen at all
|
|
466
|
+
* (a gated sense drops it). Calling the door `perceive` said the caller had
|
|
467
|
+
* already done the mind's work, and it misled every reader of this flow,
|
|
468
|
+
* including the first draft of the epoch that renamed it.
|
|
347
469
|
*/
|
|
348
|
-
async
|
|
470
|
+
async sense( stimulus: Stimulus ): Promise<void> {
|
|
349
471
|
const from = stimulus.from ?? 'user'
|
|
350
|
-
await this.stem.
|
|
472
|
+
await this.stem.senseText( this.id, {
|
|
351
473
|
kind: 'text',
|
|
352
474
|
entityId: from,
|
|
353
475
|
threadId: stimulus.thread ?? from,
|
|
@@ -358,17 +480,34 @@ export class Will {
|
|
|
358
480
|
// the Will knows the person as "someone" until a real one is learned. (The live
|
|
359
481
|
// conversation focus still falls back to the entity id for its Speaker line.)
|
|
360
482
|
...( stimulus.speaker ? { speakerName: stimulus.speaker } : {} ),
|
|
483
|
+
// Omitted rather than defaulted: an unknown room is not known to be public.
|
|
484
|
+
...( stimulus.direct !== undefined ? { direct: stimulus.direct } : {} ),
|
|
485
|
+
// Omitted when the channel does not know — a room with no name stays
|
|
486
|
+
// unnamed, the same way a person does, rather than being labelled with its id.
|
|
487
|
+
...( stimulus.threadName ? { threadName: stimulus.threadName } : {} ),
|
|
488
|
+
// No default. `Stimulus.provenance` is required now, so there is nothing
|
|
489
|
+
// left to fall back to — the four-state hole is closed at every door into
|
|
490
|
+
// this mind, which was the point of the epoch.
|
|
491
|
+
provenance: stimulus.provenance,
|
|
492
|
+
...( stimulus.sourceIntentId ? { sourceIntentId: stimulus.sourceIntentId } : {} ),
|
|
361
493
|
} )
|
|
362
494
|
}
|
|
363
495
|
|
|
364
|
-
/**
|
|
496
|
+
/**
|
|
497
|
+
* Sense from the default user. Sugar over `sense`.
|
|
498
|
+
*
|
|
499
|
+
* Supplies `provenance: 'exafferent'` — that is not a default sneaking back
|
|
500
|
+
* in, it is what the verb MEANS. "Say" is somebody speaking to the Will; a
|
|
501
|
+
* caller who wants to feed back the Will's own act reaches for `sense` and
|
|
502
|
+
* says so. The assertion lives in the function name.
|
|
503
|
+
*/
|
|
365
504
|
async say( text: string ): Promise<void> {
|
|
366
|
-
return this.
|
|
505
|
+
return this.sense( { text, from: 'user', provenance: 'exafferent' } )
|
|
367
506
|
}
|
|
368
507
|
|
|
369
|
-
/**
|
|
508
|
+
/** Sense from a specific interlocutor (multi-party). Sugar over `sense` — see `say` on provenance. */
|
|
370
509
|
async tell( entityId: string, speakerName: string, text: string ): Promise<void> {
|
|
371
|
-
return this.
|
|
510
|
+
return this.sense( { text, from: entityId, speaker: speakerName, provenance: 'exafferent' } )
|
|
372
511
|
}
|
|
373
512
|
|
|
374
513
|
/**
|
|
@@ -378,7 +517,7 @@ export class Will {
|
|
|
378
517
|
* 5000). `null` is a real outcome — the Will chose not to speak — not a
|
|
379
518
|
* failure. Pass `to` to only accept an utterance addressed to that entity.
|
|
380
519
|
*
|
|
381
|
-
* await will.
|
|
520
|
+
* await will.sense( { from: 'ada', text: 'Hi!', provenance: 'exafferent' } )
|
|
382
521
|
* const reply = await will.nextUtterance( { to: 'ada', within: 3000 } )
|
|
383
522
|
* // reply is a WillMessage, or null if Ada got the silent treatment.
|
|
384
523
|
*/
|
|
@@ -485,6 +624,19 @@ export class Will {
|
|
|
485
624
|
pause(): void { this.stem.pauseWill( this.id ) }
|
|
486
625
|
resume(): void { this.stem.resumeWill( this.id ) }
|
|
487
626
|
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Install the Policy Decision Point consulted before every effector
|
|
630
|
+
* invocation this Will hands to the host (POLICY_REAFFERENCE P0). `null`
|
|
631
|
+
* restores the no-op default. See `WillStem.setArbiter` for the one scoping
|
|
632
|
+
* caveat (per-stem, not per-Will id) — irrelevant here since `Will.create()`
|
|
633
|
+
* gives this instance its own dedicated stem.
|
|
634
|
+
*/
|
|
635
|
+
setArbiter( arbiter: PolicyArbiter | null ): this {
|
|
636
|
+
this.stem.setArbiter( arbiter )
|
|
637
|
+
return this
|
|
638
|
+
}
|
|
639
|
+
|
|
488
640
|
/**
|
|
489
641
|
* Checkpoint the living mind into a portable PMA artifact — NON-destructive.
|
|
490
642
|
* The Will keeps ticking; the snapshot is a point-in-time copy you can archive
|
|
@@ -525,16 +677,14 @@ export class Will {
|
|
|
525
677
|
const mode = opts.llm ?? detectProvider()
|
|
526
678
|
const useMock = mode === 'mock'
|
|
527
679
|
// `llm` selects the provider; an explicit llmConfig.provider still wins.
|
|
528
|
-
// Model rides with the transport
|
|
529
|
-
// top-level `
|
|
530
|
-
|
|
680
|
+
// Model rides with the transport — `llmConfig.model`, and only that. The
|
|
681
|
+
// top-level `model` spelling is gone: two ways to say one thing is how a
|
|
682
|
+
// config grows a precedence rule nobody can remember.
|
|
683
|
+
const llmConfig: WillLLMConfig | undefined = useMock && !opts.llmConfig
|
|
531
684
|
? undefined
|
|
532
685
|
: {
|
|
533
686
|
...( mode !== 'mock' ? { provider: mode } : {} ),
|
|
534
687
|
...opts.llmConfig,
|
|
535
|
-
...( opts.llmConfig?.model !== undefined ? { model: opts.llmConfig.model }
|
|
536
|
-
: opts.model !== undefined ? { model: opts.model }
|
|
537
|
-
: {} ),
|
|
538
688
|
}
|
|
539
689
|
return {
|
|
540
690
|
id, name: opts.name,
|
|
@@ -561,7 +711,10 @@ export class Will {
|
|
|
561
711
|
this._unsub = this.stem.addTickListener( this.id, ( _snapshot, _tick, outbox, invocations ) => {
|
|
562
712
|
// Outbound messages → event + auto delivery-ack.
|
|
563
713
|
for( const msg of outbox ){
|
|
564
|
-
this._emitMessage( {
|
|
714
|
+
this._emitMessage( {
|
|
715
|
+
id: msg.id, content: msg.content, to: msg.targetEntityId,
|
|
716
|
+
...( msg.threadId ? { thread: msg.threadId } : {} ),
|
|
717
|
+
} )
|
|
565
718
|
try { this.stem.confirmMessageDelivery( this.id, msg.id, true ) } catch { /* best-effort */ }
|
|
566
719
|
}
|
|
567
720
|
// Effector invocations → project the motor act, then run the handler → ack.
|
|
@@ -589,7 +742,7 @@ export class Will {
|
|
|
589
742
|
if( !handler ){
|
|
590
743
|
// The Will chose an effector we have no handler for — report it as failed so
|
|
591
744
|
// the reafference loop learns it doesn't work, rather than hanging on the await.
|
|
592
|
-
this.stem.confirmEffectorExecution( this.id, inv.
|
|
745
|
+
this.stem.confirmEffectorExecution( this.id, inv.intentId, {
|
|
593
746
|
success: false, description: `No handler registered for effector "${inv.effectorName}"`,
|
|
594
747
|
} )
|
|
595
748
|
return
|
|
@@ -598,15 +751,23 @@ export class Will {
|
|
|
598
751
|
try {
|
|
599
752
|
const raw = await handler( inv.parameters, {
|
|
600
753
|
reasoning: inv.reasoning,
|
|
754
|
+
// The correlation handle, so a handler that feeds its own result back in
|
|
755
|
+
// can say WHICH act caused it. This line used to read
|
|
756
|
+
// `intentId: inv.decisionRecordId` — the translation that proved the
|
|
757
|
+
// wire name was already wrong.
|
|
758
|
+
intentId: inv.intentId,
|
|
601
759
|
targetEntityId: inv.targetEntityId,
|
|
760
|
+
// Which of the host's own ids that referent is — without it a handler
|
|
761
|
+
// gets an opaque anchor and nothing it can look up.
|
|
762
|
+
...( inv.targetAddresses?.length ? { targetAddresses: inv.targetAddresses } : {} ),
|
|
602
763
|
...( inv.description ? { description: inv.description } : {} ),
|
|
603
764
|
} )
|
|
604
765
|
const result = typeof raw === 'string' ? { success: true, description: raw } : raw
|
|
605
|
-
this.stem.confirmEffectorExecution( this.id, inv.
|
|
766
|
+
this.stem.confirmEffectorExecution( this.id, inv.intentId, result )
|
|
606
767
|
}
|
|
607
768
|
catch( err ){
|
|
608
769
|
this._emitError( err instanceof Error ? err : new Error( String( err ) ) )
|
|
609
|
-
this.stem.confirmEffectorExecution( this.id, inv.
|
|
770
|
+
this.stem.confirmEffectorExecution( this.id, inv.intentId, {
|
|
610
771
|
success: false, description: `Effector "${inv.effectorName}" threw: ${( err as Error ).message}`,
|
|
611
772
|
} )
|
|
612
773
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// ─────────────────────────────────────────────────────────────
|
|
2
|
-
// src/serve/server.ts — a Will, exposed over plain HTTP (the sidecar)
|
|
2
|
+
// src/surface/serve/server.ts — a Will, exposed over plain HTTP (the sidecar)
|
|
3
3
|
// ─────────────────────────────────────────────────────────────
|
|
4
4
|
//
|
|
5
5
|
// `will serve` for hosts that aren't Node and aren't MCP clients — a Python
|
|
@@ -23,8 +23,9 @@
|
|
|
23
23
|
import { createServer, type Server, type IncomingMessage, type ServerResponse } from 'node:http'
|
|
24
24
|
import { mkdirSync, writeFileSync } from 'node:fs'
|
|
25
25
|
import { dirname } from 'node:path'
|
|
26
|
-
import type { Will } from '#sdk/will'
|
|
27
|
-
import {
|
|
26
|
+
import type { Will } from '#surface/sdk/will'
|
|
27
|
+
import { asProvenance } from '#senses/provenance'
|
|
28
|
+
import { UtteranceTap } from '#surface/host/utterances'
|
|
28
29
|
|
|
29
30
|
export interface WillHttpOptions {
|
|
30
31
|
/** Where POST /save writes the PMA artifact. */
|
|
@@ -98,10 +99,16 @@ export function buildWillHttpServer( will: Will, opts: WillHttpOptions = {} ): S
|
|
|
98
99
|
const body = await readJsonBody( req )
|
|
99
100
|
const text = typeof body.text === 'string' ? body.text : ''
|
|
100
101
|
if( !text ) return json( res, 400, { error: 'text is required' } )
|
|
101
|
-
await will.
|
|
102
|
+
await will.sense( {
|
|
102
103
|
text,
|
|
104
|
+
// Untyped ingress — a JSON body cannot be type-checked, and a client
|
|
105
|
+
// that predates the field has not claimed anything. asProvenance()
|
|
106
|
+
// owns the direction; see its comment for why 'exafferent' and not
|
|
107
|
+
// the tidier-looking 'unknown'.
|
|
108
|
+
provenance: asProvenance( body.provenance ),
|
|
103
109
|
...( typeof body.from === 'string' ? { from: body.from } : {} ),
|
|
104
110
|
...( typeof body.speaker === 'string' ? { speaker: body.speaker } : {} ),
|
|
111
|
+
...( typeof body.sourceIntentId === 'string' ? { sourceIntentId: body.sourceIntentId } : {} ),
|
|
105
112
|
} )
|
|
106
113
|
// 202: delivered into the sensory field — NOT answered. A response, if
|
|
107
114
|
// any, arrives on /utterances or /next-utterance; silence is valid.
|
package/src/types.ts
CHANGED
|
@@ -22,12 +22,33 @@ export interface effectorInvocation {
|
|
|
22
22
|
id: string
|
|
23
23
|
/** Correlation handle — the awaiting `agency.intent` id. Echo it when POSTing to
|
|
24
24
|
* `POST /v1/wills/:id/effectors/invoked/ack`; the Will reconciles the result onto
|
|
25
|
-
* that intent.
|
|
26
|
-
*
|
|
27
|
-
decisionRecordId
|
|
25
|
+
* that intent.
|
|
26
|
+
*
|
|
27
|
+
* Called `decisionRecordId` until the aliases came out. It stopped being a
|
|
28
|
+
* `decision.record` id at the agency cutover and was kept anyway "for
|
|
29
|
+
* wire-contract stability" — so the wire was stable and wrong, naming a
|
|
30
|
+
* record type that no longer exists, while the SDK translated it to
|
|
31
|
+
* `intentId` one hop later for the handler. Stability that preserves a false
|
|
32
|
+
* name is preserving the wrong thing. */
|
|
33
|
+
intentId: string
|
|
28
34
|
effectorName: string
|
|
29
35
|
parameters: Record<string, unknown>
|
|
30
36
|
targetEntityId: string | undefined
|
|
37
|
+
/**
|
|
38
|
+
* The addresses the world knows `targetEntityId` by.
|
|
39
|
+
*
|
|
40
|
+
* `targetEntityId` is an ANCHOR (`ke:1sqlkux`) — since 0.9.0 identity is opaque
|
|
41
|
+
* and deliberately not an address, because who someone is and where to find them
|
|
42
|
+
* are different facts. A host holds channel ids and knows nothing of anchors, so
|
|
43
|
+
* an invocation naming only the referent is one no surface can act on. Resolved
|
|
44
|
+
* inside, where the alias table lives, exactly as the outbox resolves a message
|
|
45
|
+
* addressed to a referent.
|
|
46
|
+
*
|
|
47
|
+
* Outbound only: this is the mind saying which of its own handles it means.
|
|
48
|
+
* Nothing here lets a surface name someone TO the mind — that is what perception
|
|
49
|
+
* is for.
|
|
50
|
+
*/
|
|
51
|
+
targetAddresses?: readonly string[]
|
|
31
52
|
reasoning: string
|
|
32
53
|
/** The ability's declared meaning (from its EffectorDeclaration), when present. */
|
|
33
54
|
description?: string
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { W as Will } from '../will-cS6k4uiJ.js';
|
|
2
|
-
import { C as ChannelBridge } from '../types-E9-HV-SW.js';
|
|
3
|
-
|
|
4
|
-
interface DiscordLikeChannel {
|
|
5
|
-
send(content: string): Promise<unknown>;
|
|
6
|
-
sendTyping?(): Promise<unknown>;
|
|
7
|
-
}
|
|
8
|
-
interface DiscordLikeMessage {
|
|
9
|
-
content: string;
|
|
10
|
-
cleanContent?: string;
|
|
11
|
-
channelId: string;
|
|
12
|
-
guildId?: string | null;
|
|
13
|
-
author: {
|
|
14
|
-
id: string;
|
|
15
|
-
bot?: boolean;
|
|
16
|
-
username?: string;
|
|
17
|
-
displayName?: string;
|
|
18
|
-
};
|
|
19
|
-
member?: {
|
|
20
|
-
displayName?: string;
|
|
21
|
-
} | null;
|
|
22
|
-
mentions?: {
|
|
23
|
-
has(userId: string): boolean;
|
|
24
|
-
};
|
|
25
|
-
channel: DiscordLikeChannel;
|
|
26
|
-
}
|
|
27
|
-
interface DiscordLikeClient {
|
|
28
|
-
user: {
|
|
29
|
-
id: string;
|
|
30
|
-
setPresence?(p: unknown): void;
|
|
31
|
-
} | null;
|
|
32
|
-
/** discord.js ≥14.22; polled so we needn't subscribe to the deprecated `ready`. */
|
|
33
|
-
isReady?(): boolean;
|
|
34
|
-
on(event: 'messageCreate', fn: (m: DiscordLikeMessage) => void): unknown;
|
|
35
|
-
once(event: string, fn: () => void): unknown;
|
|
36
|
-
login(token: string): Promise<unknown>;
|
|
37
|
-
destroy(): Promise<unknown> | void;
|
|
38
|
-
channels: {
|
|
39
|
-
fetch(id: string): Promise<unknown>;
|
|
40
|
-
};
|
|
41
|
-
users: {
|
|
42
|
-
fetch(id: string): Promise<{
|
|
43
|
-
send(content: string): Promise<unknown>;
|
|
44
|
-
}>;
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
interface DiscordBridgeOptions {
|
|
48
|
-
/** Bot token (Discord developer portal). Unused when `client` is injected pre-logged-in. */
|
|
49
|
-
token?: string;
|
|
50
|
-
/** Channel ids the Will inhabits. Unset = every channel it can see. */
|
|
51
|
-
channels?: string[];
|
|
52
|
-
/** Perceive guild messages only when the Will is @mentioned (DMs always perceived). */
|
|
53
|
-
mentionOnly?: boolean;
|
|
54
|
-
/** Fallback channel for utterances with no reachable addressee. */
|
|
55
|
-
homeChannelId?: string;
|
|
56
|
-
/** Roster path (default: ./.will/<willId>.discord.json). */
|
|
57
|
-
rosterPath?: string;
|
|
58
|
-
/** Test / power-user seam: bring your own client; discord.js is never imported. */
|
|
59
|
-
client?: DiscordLikeClient;
|
|
60
|
-
log?: (msg: string) => void;
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Connect a Will to Discord. Resolves once the bridge is live (logged in and
|
|
64
|
-
* relaying). Close it via the returned `ChannelBridge.close()` — the Will
|
|
65
|
-
* itself is not stopped; it simply loses this surface.
|
|
66
|
-
*/
|
|
67
|
-
declare function connectDiscord(will: Will, opts: DiscordBridgeOptions): Promise<ChannelBridge>;
|
|
68
|
-
|
|
69
|
-
export { type DiscordBridgeOptions, type DiscordLikeChannel, type DiscordLikeClient, type DiscordLikeMessage, connectDiscord };
|