@noopolis/daimon 0.1.0 → 0.1.1
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 +92 -0
- package/dist/core/types.d.ts +10 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/examples/jungian-play-org.d.ts +2 -0
- package/dist/examples/jungian-play-org.d.ts.map +1 -0
- package/dist/examples/jungian-play-org.js +280 -0
- package/dist/examples/jungian-triad-org.d.ts +2 -0
- package/dist/examples/jungian-triad-org.d.ts.map +1 -0
- package/dist/examples/jungian-triad-org.js +280 -0
- package/dist/examples/jungianConversationPlan.d.ts +10 -0
- package/dist/examples/jungianConversationPlan.d.ts.map +1 -0
- package/dist/examples/jungianConversationPlan.js +64 -0
- package/dist/examples/jungianPiRepresentative.d.ts +36 -0
- package/dist/examples/jungianPiRepresentative.d.ts.map +1 -0
- package/dist/examples/jungianPiRepresentative.js +81 -0
- package/dist/examples/jungianPlayAgent.d.ts +36 -0
- package/dist/examples/jungianPlayAgent.d.ts.map +1 -0
- package/dist/examples/jungianPlayAgent.js +111 -0
- package/dist/examples/jungianProfiles.d.ts +34 -0
- package/dist/examples/jungianProfiles.d.ts.map +1 -0
- package/dist/examples/jungianProfiles.js +190 -0
- package/dist/examples/jungianTrace.d.ts +47 -0
- package/dist/examples/jungianTrace.d.ts.map +1 -0
- package/dist/examples/jungianTrace.js +101 -0
- package/dist/examples/jungianTriadProfiles.d.ts +35 -0
- package/dist/examples/jungianTriadProfiles.d.ts.map +1 -0
- package/dist/examples/jungianTriadProfiles.js +81 -0
- package/dist/examples/mixed-engine-org.d.ts +2 -0
- package/dist/examples/mixed-engine-org.d.ts.map +1 -0
- package/dist/examples/mixed-engine-org.js +351 -0
- package/dist/examples/mixedEngineCli.d.ts +15 -0
- package/dist/examples/mixedEngineCli.d.ts.map +1 -0
- package/dist/examples/mixedEngineCli.js +174 -0
- package/dist/examples/pi-memory-org.d.ts +2 -0
- package/dist/examples/pi-memory-org.d.ts.map +1 -0
- package/dist/examples/pi-memory-org.js +230 -0
- package/dist/index.d.ts +1 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -3
- package/dist/observability/index.d.ts +2 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +1 -0
- package/dist/observability/orgObserver.d.ts +98 -0
- package/dist/observability/orgObserver.d.ts.map +1 -0
- package/dist/observability/orgObserver.js +181 -0
- package/dist/pi/index.d.ts +4 -0
- package/dist/pi/index.d.ts.map +1 -0
- package/dist/pi/index.js +3 -0
- package/dist/pi/memoryTools.d.ts +15 -0
- package/dist/pi/memoryTools.d.ts.map +1 -0
- package/dist/pi/memoryTools.js +70 -0
- package/dist/pi/piHarness.d.ts +8 -0
- package/dist/pi/piHarness.d.ts.map +1 -1
- package/dist/pi/piHarness.js +137 -6
- package/package.json +26 -4
package/README.md
CHANGED
|
@@ -15,6 +15,28 @@ should not know what an org is.
|
|
|
15
15
|
npm install @noopolis/daimon
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
For Pi agents with memory enabled, install Mneme too:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @noopolis/daimon @noopolis/mneme
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
During local incubation, use the sibling Mneme checkout:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@noopolis/mneme": "file:../mneme"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Pi-specific exports live under the Pi subpath:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import { PiHarnessAdapter } from "@noopolis/daimon/pi";
|
|
38
|
+
```
|
|
39
|
+
|
|
18
40
|
## Tests
|
|
19
41
|
|
|
20
42
|
The package has a non-live test suite for auth seeding and Pi model config
|
|
@@ -49,8 +71,78 @@ The Pi E2E uses the local Codex CLI subscription auth file to seed an ignored Pi
|
|
|
49
71
|
```bash
|
|
50
72
|
npm install
|
|
51
73
|
npm run e2e:pi-agent
|
|
74
|
+
npm run e2e:pi-memory-org
|
|
75
|
+
npm run e2e:mixed-engine-org
|
|
76
|
+
npm run e2e:jungian-play-org
|
|
77
|
+
npm run e2e:jungian-triad-org
|
|
52
78
|
```
|
|
53
79
|
|
|
54
80
|
The example starts two harnessed Pi agents from plain caller code. The example
|
|
55
81
|
creates the workspaces and shared resource itself to demonstrate the intended
|
|
56
82
|
boundary: the caller prepares files, the harness runs agent turns.
|
|
83
|
+
|
|
84
|
+
The memory-org example starts three harnessed Pi agents, gives each agent a
|
|
85
|
+
private marker memory, clears Pi session history, then runs a room-style recall
|
|
86
|
+
conversation. It restarts one agent again before the final check, so the final
|
|
87
|
+
answer must come from Daimon's persisted memory rather than Pi's live chat
|
|
88
|
+
session.
|
|
89
|
+
|
|
90
|
+
The mixed-engine example starts a small org backed by real local CLIs:
|
|
91
|
+
Navigator uses Codex, Cartographer uses Grok, and Sentinel uses Agy. Each engine
|
|
92
|
+
invents its own private signal, then the room conversation verifies that later
|
|
93
|
+
turns recall those LLM-generated signals through Daimon memory.
|
|
94
|
+
|
|
95
|
+
The Jungian play example starts two selves as characters in a play. Each self
|
|
96
|
+
has archetype voices such as Persona, Shadow, Anima/Animus, Wise One, Great
|
|
97
|
+
Mother, Hero, and Trickster. The inner voices run first, the representative self
|
|
98
|
+
then speaks externally, and the run writes play traces plus memory/latency
|
|
99
|
+
telemetry under `.runtime/jungian-play-org/`.
|
|
100
|
+
|
|
101
|
+
The Jungian triad example uses three complete Jungian selves in one
|
|
102
|
+
conversation: Maya speaks through Codex CLI, Leo speaks through Grok, and Priya
|
|
103
|
+
speaks through a Pi agent seeded from local Codex subscription auth. All three
|
|
104
|
+
selves carry the same full archetype set, rotated through the run so every
|
|
105
|
+
archetype gets consulted.
|
|
106
|
+
|
|
107
|
+
## Design Notes
|
|
108
|
+
|
|
109
|
+
- `MEMORY-SYSTEM.md` describes the implemented scoped memory runtime.
|
|
110
|
+
- `ENGINE-SYSTEM.md` describes the next engine abstraction plan: Pi, Ollama,
|
|
111
|
+
API providers, and CLI-backed engines such as `agy`, `grok`, and `gemini`.
|
|
112
|
+
- Mneme is a sibling package, `@noopolis/mneme`, published separately and used
|
|
113
|
+
by Daimon in-process for Pi agents. Other runtimes can use Mneme through its
|
|
114
|
+
MCP server. The agent-facing tools stay named `memory_search`,
|
|
115
|
+
`memory_register`, and `memory.*` at the protocol boundary because those names
|
|
116
|
+
are clearer to agents.
|
|
117
|
+
|
|
118
|
+
## Runtime Artifact Image
|
|
119
|
+
|
|
120
|
+
Daimon can build a local copy-only runtime artifact image for Spawnfile:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
npm run image:runtime:local
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
This creates:
|
|
127
|
+
|
|
128
|
+
```text
|
|
129
|
+
noopolis/spawnfile-runtime-daimon:0.1.1-local
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
The image is not a full organization image and is not intended to be run
|
|
133
|
+
directly. It contains only:
|
|
134
|
+
|
|
135
|
+
```text
|
|
136
|
+
/opt/spawnfile/runtime-installs/daimon
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Spawnfile can copy that path into generated organization images:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
SPAWNFILE_DAIMON_RUNTIME_IMAGE=noopolis/spawnfile-runtime-daimon:0.1.1-local \
|
|
143
|
+
spawnfile build ./agentic-org
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
This keeps mixed-runtime organizations composable: a generated image can copy
|
|
147
|
+
Daimon, OpenClaw, PicoClaw, or any future runtime artifact independently instead
|
|
148
|
+
of depending on one prebuilt image for every runtime combination.
|
package/dist/core/types.d.ts
CHANGED
|
@@ -25,6 +25,16 @@ export interface WakeEvent {
|
|
|
25
25
|
kind: "manual" | "message" | "schedule";
|
|
26
26
|
from?: string;
|
|
27
27
|
text: string;
|
|
28
|
+
context?: {
|
|
29
|
+
networkId?: string;
|
|
30
|
+
roomId?: string;
|
|
31
|
+
teamId?: string;
|
|
32
|
+
taskId?: string;
|
|
33
|
+
roleId?: string;
|
|
34
|
+
from?: string;
|
|
35
|
+
pairPeers?: string[];
|
|
36
|
+
artifactPaths?: string[];
|
|
37
|
+
};
|
|
28
38
|
}
|
|
29
39
|
export interface WakeResult {
|
|
30
40
|
agentId: AgentId;
|
package/dist/core/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAE7B,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,WAAW,GAAG,QAAQ,CAAC;CACvC;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE;QACL,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,OAAO,GAAG,MAAM,CAAC;KACtD,CAAC;IACF,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAE7B,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,WAAW,GAAG,QAAQ,CAAC;CACvC;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE;QACL,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,OAAO,GAAG,MAAM,CAAC;KACtD,CAAC;IACF,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE;QACR,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;QACrB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;KAC1B,CAAC;CACH;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAC5C,MAAM,IAAI,WAAW,CAAC;IACtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;CAC1D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jungian-play-org.d.ts","sourceRoot":"","sources":["../../src/examples/jungian-play-org.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { JsonlMemoryStore } from "@noopolis/mneme";
|
|
5
|
+
import { OrgObserver } from "../observability/index.js";
|
|
6
|
+
import { beatsFor, defaultDialogueTurns, selectVoicesForBeat } from "./jungianConversationPlan.js";
|
|
7
|
+
import { JungianVoice, runLimited } from "./jungianPlayAgent.js";
|
|
8
|
+
import { jungianSelves, playScenario } from "./jungianProfiles.js";
|
|
9
|
+
import { JungianTrace, parseInnerUsed, parseSpeakLine } from "./jungianTrace.js";
|
|
10
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const daimonRoot = path.resolve(__dirname, "../..");
|
|
12
|
+
const runtimeRoot = path.join(daimonRoot, ".runtime", "jungian-play-org");
|
|
13
|
+
const runId = `run-${Date.now().toString(36)}`;
|
|
14
|
+
const councilConcurrency = Number.parseInt(process.env.DAIMON_JUNGIAN_COUNCIL_CONCURRENCY ?? "3", 10);
|
|
15
|
+
const dialogueTurnCount = Math.max(10, Number.parseInt(process.env.DAIMON_JUNGIAN_TURNS ?? String(defaultDialogueTurns), 10));
|
|
16
|
+
const voicesPerTurn = Math.max(1, Number.parseInt(process.env.DAIMON_JUNGIAN_VOICES_PER_TURN ?? "2", 10));
|
|
17
|
+
const observer = new OrgObserver({ orgId: "jungian-play-org", runId });
|
|
18
|
+
const trace = new JungianTrace({ runId, title: playScenario.title });
|
|
19
|
+
const roomContext = {
|
|
20
|
+
networkId: "jungian-sitcom",
|
|
21
|
+
roomId: "community-kitchen",
|
|
22
|
+
teamId: "jungian-play-org",
|
|
23
|
+
participants: jungianSelves.map((self) => self.representative.id)
|
|
24
|
+
};
|
|
25
|
+
const observeTurn = (turn) => {
|
|
26
|
+
observer.recordTurn({
|
|
27
|
+
agent: turn.voice.id,
|
|
28
|
+
engine: turn.voice.engine,
|
|
29
|
+
event: turn.event.id,
|
|
30
|
+
eventText: turn.event.text,
|
|
31
|
+
totalMs: turn.totalMs,
|
|
32
|
+
memoryPrepareMs: turn.memoryPrepareMs,
|
|
33
|
+
engineMs: turn.engineResult.durationMs,
|
|
34
|
+
memoryRecordMs: turn.memoryRecordMs,
|
|
35
|
+
promptChars: turn.engineResult.promptChars,
|
|
36
|
+
outputChars: turn.engineResult.outputChars,
|
|
37
|
+
outputText: turn.outputText,
|
|
38
|
+
recall: turn.recall
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
const archetypeInstructions = (self, voice) => [
|
|
42
|
+
`You are ${voice.name}, the ${voice.archetype} voice inside ${self.name}.`,
|
|
43
|
+
`Agenda: ${voice.agenda}`,
|
|
44
|
+
`Fear: ${voice.fear}`,
|
|
45
|
+
`Gift: ${voice.gift}`,
|
|
46
|
+
`Wound: ${voice.wound}`,
|
|
47
|
+
"You are an inner counselor, not the public speaker.",
|
|
48
|
+
"Keep counsel psychologically specific but grounded in ordinary human behavior.",
|
|
49
|
+
"No mythic language, no grand symbols, no fantasy imagery.",
|
|
50
|
+
"Use recalled memory if the same conversation pattern returns."
|
|
51
|
+
];
|
|
52
|
+
const representativeInstructions = (self) => [
|
|
53
|
+
`You are ${self.representative.name}, the outward speaking Self for ${self.name}.`,
|
|
54
|
+
`Play role: ${self.playRole}`,
|
|
55
|
+
`Public mask: ${self.publicMask}`,
|
|
56
|
+
`Backstory: ${self.backstory}`,
|
|
57
|
+
`Core conflict: ${self.conflict}`,
|
|
58
|
+
`Secret want: ${self.secretWant}`,
|
|
59
|
+
`Style: ${self.representative.style}`,
|
|
60
|
+
"You speak externally after listening to inner archetypal counsel.",
|
|
61
|
+
"Sound like a real person in a grounded TV drama or workplace sitcom.",
|
|
62
|
+
"Use short, conversational lines. No speeches. No poetic metaphors.",
|
|
63
|
+
"Do not mention the machinery of prompts, engines, memory, or tests.",
|
|
64
|
+
"Return the requested SPEAK/STAGE/INNER_USED shape exactly."
|
|
65
|
+
];
|
|
66
|
+
const buildSelf = (profile) => ({
|
|
67
|
+
profile,
|
|
68
|
+
representative: new JungianVoice({
|
|
69
|
+
id: profile.representative.id,
|
|
70
|
+
name: profile.representative.name,
|
|
71
|
+
selfId: profile.id,
|
|
72
|
+
selfName: profile.name,
|
|
73
|
+
engine: profile.engine,
|
|
74
|
+
runtimeRoot,
|
|
75
|
+
instructions: representativeInstructions(profile)
|
|
76
|
+
}),
|
|
77
|
+
archetypes: profile.voices.map((voice) => new JungianVoice({
|
|
78
|
+
id: `${profile.id}-${voice.id}`,
|
|
79
|
+
name: voice.name,
|
|
80
|
+
selfId: profile.id,
|
|
81
|
+
selfName: profile.name,
|
|
82
|
+
archetype: voice.archetype,
|
|
83
|
+
engine: voice.engine,
|
|
84
|
+
runtimeRoot,
|
|
85
|
+
instructions: archetypeInstructions(profile, voice)
|
|
86
|
+
}))
|
|
87
|
+
});
|
|
88
|
+
const selves = jungianSelves.map(buildSelf);
|
|
89
|
+
const transcriptText = (transcript) => transcript.length === 0 ? "(no external dialogue yet)" : transcript.join("\n");
|
|
90
|
+
const councilPrompt = (self, focus, transcript) => [
|
|
91
|
+
`Scene: ${playScenario.title}`,
|
|
92
|
+
`Premise: ${playScenario.premise}`,
|
|
93
|
+
`Set: ${playScenario.openingImage}`,
|
|
94
|
+
"",
|
|
95
|
+
`Self under counsel: ${self.name}`,
|
|
96
|
+
`Current focus: ${focus}`,
|
|
97
|
+
"",
|
|
98
|
+
"External dialogue so far:",
|
|
99
|
+
transcriptText(transcript),
|
|
100
|
+
"",
|
|
101
|
+
"Respond as this archetype only.",
|
|
102
|
+
"Keep it plain and practical. No poetic stage images.",
|
|
103
|
+
"Use exactly this shape:",
|
|
104
|
+
"ARCHETYPE: <archetype name>",
|
|
105
|
+
"COUNSEL: <one short practical note>",
|
|
106
|
+
"RISK: <what could go wrong emotionally>",
|
|
107
|
+
"LINE_NOTE: <what the representative should sound like>"
|
|
108
|
+
].join("\n");
|
|
109
|
+
const representativePrompt = (self, focus, transcript, counsel) => [
|
|
110
|
+
`Scene: ${playScenario.title}`,
|
|
111
|
+
`Premise: ${playScenario.premise}`,
|
|
112
|
+
`Set: ${playScenario.openingImage}`,
|
|
113
|
+
"",
|
|
114
|
+
"External dialogue so far:",
|
|
115
|
+
transcriptText(transcript),
|
|
116
|
+
"",
|
|
117
|
+
`Current focus: ${focus}`,
|
|
118
|
+
"",
|
|
119
|
+
"Inner council counsel:",
|
|
120
|
+
...counsel.map((turn) => `- ${turn.voice.id}: ${turn.outputText.replace(/\n/g, " ")}`),
|
|
121
|
+
"",
|
|
122
|
+
"Choose one natural next line of dialogue.",
|
|
123
|
+
"The line must sound like a normal person, not a symbol or narrator.",
|
|
124
|
+
"Keep SPEAK under 22 words. Avoid words like ledger, mercy, truth, blood, crown, soul, destiny, wound unless the transcript already used them.",
|
|
125
|
+
"Do not use Markdown, bullets, bold markers, or quotation marks inside SPEAK.",
|
|
126
|
+
"Use exactly this shape:",
|
|
127
|
+
"SPEAK: <one natural spoken line, without wrapping quotation marks>",
|
|
128
|
+
"STAGE: <one tiny normal action>",
|
|
129
|
+
`INNER_USED: <comma-separated ids from ${self.voices.map((voice) => voice.id).join(", ")}>`
|
|
130
|
+
].join("\n");
|
|
131
|
+
const runCouncil = async (self, eventId, focus, transcript, voices) => {
|
|
132
|
+
console.log(`\n== ${self.profile.name} inner council: ${focus} ==`);
|
|
133
|
+
const turns = await runLimited(voices, councilConcurrency, async (voice) => {
|
|
134
|
+
const event = {
|
|
135
|
+
id: `${eventId}-${voice.config.id}`,
|
|
136
|
+
kind: "manual",
|
|
137
|
+
context: roomContext,
|
|
138
|
+
text: councilPrompt(self.profile, focus, transcript)
|
|
139
|
+
};
|
|
140
|
+
const turn = await voice.wake(event);
|
|
141
|
+
observeTurn(turn);
|
|
142
|
+
trace.addCounsel({
|
|
143
|
+
archetype: voice.config.archetype ?? "unknown",
|
|
144
|
+
engine: voice.config.engine,
|
|
145
|
+
event: event.id,
|
|
146
|
+
output: turn.outputText,
|
|
147
|
+
recallSelected: turn.recall.selectedEventIds.length,
|
|
148
|
+
self: self.profile.name,
|
|
149
|
+
voice: voice.config.id
|
|
150
|
+
});
|
|
151
|
+
console.log(`${voice.config.id}: ${turn.outputText.split("\n")[0]}`);
|
|
152
|
+
return turn;
|
|
153
|
+
});
|
|
154
|
+
return turns;
|
|
155
|
+
};
|
|
156
|
+
const runRepresentative = async (self, eventId, focus, transcript, counsel) => {
|
|
157
|
+
const event = {
|
|
158
|
+
id: eventId,
|
|
159
|
+
kind: "manual",
|
|
160
|
+
context: roomContext,
|
|
161
|
+
text: representativePrompt(self.profile, focus, transcript, counsel)
|
|
162
|
+
};
|
|
163
|
+
const turn = await self.representative.wake(event);
|
|
164
|
+
observeTurn(turn);
|
|
165
|
+
const used = parseInnerUsed(turn.outputText);
|
|
166
|
+
const selectedVoiceIds = new Set(counsel.flatMap((entry) => [
|
|
167
|
+
entry.voice.id,
|
|
168
|
+
entry.voice.id.replace(`${self.profile.id}-`, "")
|
|
169
|
+
]));
|
|
170
|
+
const passed = used.some((voiceId) => selectedVoiceIds.has(voiceId));
|
|
171
|
+
observer.recordAssertion({
|
|
172
|
+
detail: `${self.profile.representative.id} should declare at least one selected inner voice used`,
|
|
173
|
+
event: event.id,
|
|
174
|
+
kind: "consultation",
|
|
175
|
+
passed
|
|
176
|
+
});
|
|
177
|
+
if (!passed) {
|
|
178
|
+
throw new Error(`${self.profile.representative.id} did not declare a selected INNER_USED voice:\n${turn.outputText}`);
|
|
179
|
+
}
|
|
180
|
+
trace.addDialogue({
|
|
181
|
+
engine: self.profile.engine,
|
|
182
|
+
event: event.id,
|
|
183
|
+
innerUsed: used,
|
|
184
|
+
output: turn.outputText,
|
|
185
|
+
recallSelected: turn.recall.selectedEventIds.length,
|
|
186
|
+
self: self.profile.name,
|
|
187
|
+
speaker: self.profile.representative.name
|
|
188
|
+
});
|
|
189
|
+
const line = `${self.profile.name}: ${parseSpeakLine(turn.outputText)}`;
|
|
190
|
+
transcript.push(line);
|
|
191
|
+
console.log(`\n${line}`);
|
|
192
|
+
return line;
|
|
193
|
+
};
|
|
194
|
+
const prepareAll = async () => {
|
|
195
|
+
await rm(runtimeRoot, { recursive: true, force: true });
|
|
196
|
+
await Promise.all(selves.flatMap((self) => [
|
|
197
|
+
self.representative.prepare(),
|
|
198
|
+
...self.archetypes.map((voice) => voice.prepare())
|
|
199
|
+
]));
|
|
200
|
+
await writeFile(path.join(runtimeRoot, "org.json"), JSON.stringify({
|
|
201
|
+
scenario: playScenario,
|
|
202
|
+
selves: jungianSelves.map((self) => ({
|
|
203
|
+
id: self.id,
|
|
204
|
+
name: self.name,
|
|
205
|
+
engine: self.engine,
|
|
206
|
+
representative: self.representative,
|
|
207
|
+
voices: self.voices.map((voice) => ({
|
|
208
|
+
id: voice.id,
|
|
209
|
+
archetype: voice.archetype,
|
|
210
|
+
engine: voice.engine,
|
|
211
|
+
name: voice.name
|
|
212
|
+
}))
|
|
213
|
+
}))
|
|
214
|
+
}, null, 2));
|
|
215
|
+
};
|
|
216
|
+
const writeMemoryCounts = async () => {
|
|
217
|
+
const rows = await Promise.all(selves.flatMap((self) => [
|
|
218
|
+
self.representative,
|
|
219
|
+
...self.archetypes
|
|
220
|
+
]).map(async (voice) => {
|
|
221
|
+
const events = await new JsonlMemoryStore(voice.runtimeHomePath).read();
|
|
222
|
+
const counts = events.reduce((memo, event) => {
|
|
223
|
+
memo[event.type] = (memo[event.type] ?? 0) + 1;
|
|
224
|
+
return memo;
|
|
225
|
+
}, {});
|
|
226
|
+
return {
|
|
227
|
+
voice: voice.config.id,
|
|
228
|
+
self: voice.config.selfId,
|
|
229
|
+
engine: voice.config.engine,
|
|
230
|
+
counts
|
|
231
|
+
};
|
|
232
|
+
}));
|
|
233
|
+
await writeFile(path.join(runtimeRoot, "play-memory-counts.jsonl"), `${rows.map((row) => JSON.stringify(row)).join("\n")}\n`);
|
|
234
|
+
};
|
|
235
|
+
const writePlaySummary = async () => {
|
|
236
|
+
const behavior = observer.behaviorSummary();
|
|
237
|
+
const byRole = observer.turns.reduce((memo, turn) => {
|
|
238
|
+
const role = turn.agent.endsWith("-self") ? "representative" : "archetype";
|
|
239
|
+
const current = memo[role] ?? { count: 0, engineMs: 0, recallSelected: 0 };
|
|
240
|
+
current.count += 1;
|
|
241
|
+
current.engineMs += turn.engineMs;
|
|
242
|
+
current.recallSelected += turn.recallSelected;
|
|
243
|
+
memo[role] = current;
|
|
244
|
+
return memo;
|
|
245
|
+
}, {});
|
|
246
|
+
await writeFile(path.join(runtimeRoot, "play-summary.json"), `${JSON.stringify({ behavior, by_role: byRole, run_id: runId, schema: "daimon.jungian-play.summary/v1" }, null, 2)}\n`);
|
|
247
|
+
};
|
|
248
|
+
const run = async () => {
|
|
249
|
+
await prepareAll();
|
|
250
|
+
const selfById = new Map(selves.map((self) => [self.profile.id, self]));
|
|
251
|
+
const transcript = [];
|
|
252
|
+
const beats = beatsFor(dialogueTurnCount);
|
|
253
|
+
for (const [index, beat] of beats.entries()) {
|
|
254
|
+
const self = selfById.get(beat.speakerId);
|
|
255
|
+
if (!self) {
|
|
256
|
+
throw new Error(`Unknown Jungian self ${beat.speakerId}`);
|
|
257
|
+
}
|
|
258
|
+
const selectedProfiles = selectVoicesForBeat(self.profile, index, voicesPerTurn);
|
|
259
|
+
const selectedIds = new Set(selectedProfiles.map((voice) => `${self.profile.id}-${voice.id}`));
|
|
260
|
+
const selectedVoices = self.archetypes.filter((voice) => selectedIds.has(voice.config.id));
|
|
261
|
+
const eventBase = `${self.profile.id}-turn-${String(index + 1).padStart(2, "0")}`;
|
|
262
|
+
const counsel = await runCouncil(self, `${eventBase}-council`, beat.focus, transcript, selectedVoices);
|
|
263
|
+
await runRepresentative(self, `${eventBase}-speaks`, beat.focus, transcript, counsel);
|
|
264
|
+
}
|
|
265
|
+
await observer.write(runtimeRoot);
|
|
266
|
+
await trace.write(runtimeRoot);
|
|
267
|
+
await writeMemoryCounts();
|
|
268
|
+
await writePlaySummary();
|
|
269
|
+
console.log("\nExternal dialogue:");
|
|
270
|
+
for (const line of transcript) {
|
|
271
|
+
console.log(line);
|
|
272
|
+
}
|
|
273
|
+
console.log(`\nTrace: ${path.join(runtimeRoot, "jungian-trace.md")}`);
|
|
274
|
+
console.log(`Telemetry: ${path.join(runtimeRoot, "trace.md")}`);
|
|
275
|
+
console.log("\ne2e:jungian-play-org ok");
|
|
276
|
+
};
|
|
277
|
+
run().catch((error) => {
|
|
278
|
+
console.error(error instanceof Error ? error.stack ?? error.message : String(error));
|
|
279
|
+
process.exitCode = 1;
|
|
280
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jungian-triad-org.d.ts","sourceRoot":"","sources":["../../src/examples/jungian-triad-org.ts"],"names":[],"mappings":""}
|