@noopolis/daimon 0.1.1 → 0.1.2
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 +54 -10
- package/dist/core/siblingBuildFreshness.d.ts +21 -0
- package/dist/core/siblingBuildFreshness.d.ts.map +1 -0
- package/dist/core/siblingBuildFreshness.js +25 -0
- package/dist/core/types.d.ts +10 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/examples/exampleCausalId.d.ts +3 -0
- package/dist/examples/exampleCausalId.d.ts.map +1 -0
- package/dist/examples/exampleCausalId.js +8 -0
- package/dist/examples/jungian-play-org.js +3 -2
- package/dist/examples/jungian-triad-org.js +3 -2
- package/dist/examples/jungianPlayAgent.d.ts +1 -1
- package/dist/examples/jungianPlayAgent.d.ts.map +1 -1
- package/dist/examples/jungianPlayAgent.js +1 -1
- package/dist/examples/jungianProfiles.d.ts +1 -1
- package/dist/examples/jungianProfiles.d.ts.map +1 -1
- package/dist/examples/jungianTriadProfiles.d.ts +1 -1
- package/dist/examples/jungianTriadProfiles.d.ts.map +1 -1
- package/dist/examples/pi-agent.js +3 -2
- package/dist/examples/pi-memory-org.js +6 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/mcp/toolServer.d.ts +14 -0
- package/dist/mcp/toolServer.d.ts.map +1 -0
- package/dist/mcp/toolServer.js +105 -0
- package/dist/observability/causalEvents.d.ts +118 -0
- package/dist/observability/causalEvents.d.ts.map +1 -0
- package/dist/observability/causalEvents.js +135 -0
- package/dist/observability/controlCausal.d.ts +117 -0
- package/dist/observability/controlCausal.d.ts.map +1 -0
- package/dist/observability/controlCausal.js +107 -0
- package/dist/observability/emitCausalFixture.d.ts +25 -0
- package/dist/observability/emitCausalFixture.d.ts.map +1 -0
- package/dist/observability/emitCausalFixture.js +98 -0
- package/dist/observability/index.d.ts +2 -0
- package/dist/observability/index.d.ts.map +1 -1
- package/dist/observability/index.js +2 -0
- package/dist/pi/cliSession.d.ts +46 -0
- package/dist/pi/cliSession.d.ts.map +1 -0
- package/dist/pi/cliSession.js +224 -0
- package/dist/pi/index.d.ts +5 -0
- package/dist/pi/index.d.ts.map +1 -1
- package/dist/pi/index.js +5 -0
- package/dist/pi/memoryTools.d.ts +55 -1
- package/dist/pi/memoryTools.d.ts.map +1 -1
- package/dist/pi/memoryTools.js +144 -24
- package/dist/pi/modelRegistry.d.ts +13 -0
- package/dist/pi/modelRegistry.d.ts.map +1 -0
- package/dist/pi/modelRegistry.js +19 -0
- package/dist/pi/piAgentHandle.d.ts +45 -0
- package/dist/pi/piAgentHandle.d.ts.map +1 -0
- package/dist/pi/piAgentHandle.js +324 -0
- package/dist/pi/piAgentWakeSupport.d.ts +68 -0
- package/dist/pi/piAgentWakeSupport.d.ts.map +1 -0
- package/dist/pi/piAgentWakeSupport.js +147 -0
- package/dist/pi/piHarness.d.ts +30 -4
- package/dist/pi/piHarness.d.ts.map +1 -1
- package/dist/pi/piHarness.js +97 -255
- package/dist/pi/prompts.d.ts +9 -0
- package/dist/pi/prompts.d.ts.map +1 -0
- package/dist/pi/prompts.js +39 -0
- package/dist/pi/rawTrainingCapture.d.ts +59 -0
- package/dist/pi/rawTrainingCapture.d.ts.map +1 -0
- package/dist/pi/rawTrainingCapture.js +175 -0
- package/dist/pi/turnCausal.d.ts +73 -0
- package/dist/pi/turnCausal.d.ts.map +1 -0
- package/dist/pi/turnCausal.js +75 -0
- package/dist/pi/turnTrace.d.ts +128 -0
- package/dist/pi/turnTrace.d.ts.map +1 -0
- package/dist/pi/turnTrace.js +141 -0
- package/dist/pi/wakeAcceptance.d.ts +49 -0
- package/dist/pi/wakeAcceptance.d.ts.map +1 -0
- package/dist/pi/wakeAcceptance.js +281 -0
- package/dist/pi/wakeAcceptanceFs.d.ts +44 -0
- package/dist/pi/wakeAcceptanceFs.d.ts.map +1 -0
- package/dist/pi/wakeAcceptanceFs.js +318 -0
- package/dist/pi/wakeAcceptanceSchema.d.ts +75 -0
- package/dist/pi/wakeAcceptanceSchema.d.ts.map +1 -0
- package/dist/pi/wakeAcceptanceSchema.js +265 -0
- package/dist/pi/wakeModes.d.ts +9 -0
- package/dist/pi/wakeModes.d.ts.map +1 -0
- package/dist/pi/wakeModes.js +19 -0
- package/dist/pi/worldNudge.d.ts +22 -0
- package/dist/pi/worldNudge.d.ts.map +1 -0
- package/dist/pi/worldNudge.js +62 -0
- package/dist/pi/worldToolProtocol.d.ts +13 -0
- package/dist/pi/worldToolProtocol.d.ts.map +1 -0
- package/dist/pi/worldToolProtocol.js +49 -0
- package/dist/pi/worldTools.d.ts +32 -0
- package/dist/pi/worldTools.d.ts.map +1 -0
- package/dist/pi/worldTools.js +373 -0
- package/dist/pi/worldTrajectory.d.ts +52 -0
- package/dist/pi/worldTrajectory.d.ts.map +1 -0
- package/dist/pi/worldTrajectory.js +131 -0
- package/package.json +10 -13
- package/dist/examples/mixed-engine-org.d.ts +0 -2
- package/dist/examples/mixed-engine-org.d.ts.map +0 -1
- package/dist/examples/mixed-engine-org.js +0 -351
- package/dist/examples/mixedEngineCli.d.ts +0 -15
- package/dist/examples/mixedEngineCli.d.ts.map +0 -1
- package/dist/examples/mixedEngineCli.js +0 -174
package/README.md
CHANGED
|
@@ -5,9 +5,19 @@ Daimon is the Noopolis-native per-agent runtime harness.
|
|
|
5
5
|
It defines a small per-agent contract and currently implements that contract on
|
|
6
6
|
top of Pi. A Daimon runs one harnessed agent inside a caller-prepared workspace.
|
|
7
7
|
|
|
8
|
-
Spawnfile
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
Spawnfile compiles and deploys orgs, nested teams, member-owned schedules,
|
|
9
|
+
Moltnet wiring, and workspace resources. Daimon executes one agent runtime: it
|
|
10
|
+
accepts a wake selected by that runtime's organization policy and runs one
|
|
11
|
+
turn. It does not know the org graph, schedule other agents, or let Simfile or
|
|
12
|
+
a world service trigger cognition.
|
|
13
|
+
|
|
14
|
+
For a world-capable `kind: every` wake, the harness starts without a decision
|
|
15
|
+
token and privately calls `world_claim` before exposing any other world tool.
|
|
16
|
+
The claim binds authority to the schedule wake's run/request/wake identity; the
|
|
17
|
+
opaque token stays inside the harness. Subsequent observe/affordance/action
|
|
18
|
+
calls carry it without placing it in the model prompt or tool schema. Optional
|
|
19
|
+
world recommendations are ordinary observation fields discovered after the
|
|
20
|
+
independent wake and claim—they are never Daimon wake inputs.
|
|
11
21
|
|
|
12
22
|
## Install
|
|
13
23
|
|
|
@@ -15,6 +25,8 @@ should not know what an org is.
|
|
|
15
25
|
npm install @noopolis/daimon
|
|
16
26
|
```
|
|
17
27
|
|
|
28
|
+
The latest published version is 0.1.1; this README describes the source tree (0.1.2).
|
|
29
|
+
|
|
18
30
|
For Pi agents with memory enabled, install Mneme too:
|
|
19
31
|
|
|
20
32
|
```bash
|
|
@@ -37,10 +49,31 @@ Pi-specific exports live under the Pi subpath:
|
|
|
37
49
|
import { PiHarnessAdapter } from "@noopolis/daimon/pi";
|
|
38
50
|
```
|
|
39
51
|
|
|
52
|
+
By default, the in-process Mneme runtime uses the same path as each agent's
|
|
53
|
+
`runtimeHomePath`. If you need agents to keep separate Pi/runtime directories but
|
|
54
|
+
share one memory bank, pass an explicit `memory.runtimeHomePath` in
|
|
55
|
+
`PiHarnessOptions`.
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
const adapter = new PiHarnessAdapter({
|
|
59
|
+
authPath: "/tmp/daimon-auth.json",
|
|
60
|
+
memory: {
|
|
61
|
+
runtimeHomePath: "/shared/memory/bank"
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Pi agents receive Mneme tools in awake mode for normal work. Dream wakes use a
|
|
67
|
+
fresh one-off Pi session under `sessions/dream/<wake-id>-<random>` and inject
|
|
68
|
+
the Mneme dream guidance instead. Daimon does not automatically record every
|
|
69
|
+
turn as memory; agents write memories only by calling Mneme tools such as
|
|
70
|
+
`memory_register`, `memory_summarize`, and `memory_forget`.
|
|
71
|
+
|
|
40
72
|
## Tests
|
|
41
73
|
|
|
42
|
-
The package has a non-live test suite
|
|
43
|
-
generation
|
|
74
|
+
The package has a non-live test suite covering auth seeding, Pi model config
|
|
75
|
+
generation, the harness contract, memory tool wiring, wake and turn traces, and
|
|
76
|
+
the org observer:
|
|
44
77
|
|
|
45
78
|
```bash
|
|
46
79
|
npm test
|
|
@@ -68,6 +101,10 @@ upstream-documented dummy `ollama` value.
|
|
|
68
101
|
The Pi E2E uses the local Codex CLI subscription auth file to seed an ignored Pi
|
|
69
102
|
`auth.json` under `.runtime/`.
|
|
70
103
|
|
|
104
|
+
These are live runs: they spend real tokens and require local engine auth
|
|
105
|
+
(`~/.codex/auth.json` for Pi/Codex; mixed-engine and triad additionally need
|
|
106
|
+
authenticated `grok` and `agy` CLIs on PATH). They are not part of `npm test`.
|
|
107
|
+
|
|
71
108
|
```bash
|
|
72
109
|
npm install
|
|
73
110
|
npm run e2e:pi-agent
|
|
@@ -107,8 +144,9 @@ archetype gets consulted.
|
|
|
107
144
|
## Design Notes
|
|
108
145
|
|
|
109
146
|
- `MEMORY-SYSTEM.md` describes the implemented scoped memory runtime.
|
|
110
|
-
- `ENGINE-SYSTEM.md` describes the
|
|
111
|
-
|
|
147
|
+
- `ENGINE-SYSTEM.md` describes the engine abstraction plan: Pi, local/API
|
|
148
|
+
model providers, and CLI-backed engines such as `codex`, `claude`, `grok`,
|
|
149
|
+
and `agy`.
|
|
112
150
|
- Mneme is a sibling package, `@noopolis/mneme`, published separately and used
|
|
113
151
|
by Daimon in-process for Pi agents. Other runtimes can use Mneme through its
|
|
114
152
|
MCP server. The agent-facing tools stay named `memory_search`,
|
|
@@ -117,16 +155,22 @@ archetype gets consulted.
|
|
|
117
155
|
|
|
118
156
|
## Runtime Artifact Image
|
|
119
157
|
|
|
120
|
-
Daimon
|
|
158
|
+
Daimon defines a local copy-only runtime artifact image build for Spawnfile:
|
|
121
159
|
|
|
122
160
|
```bash
|
|
123
161
|
npm run image:runtime:local
|
|
124
162
|
```
|
|
125
163
|
|
|
164
|
+
Status: this build currently fails against the public npm registry. The
|
|
165
|
+
Dockerfile pins `@noopolis/daimon@0.1.2` and `@noopolis/mneme@0.1.1`, and
|
|
166
|
+
neither is published yet (registry has daimon 0.1.1 and mneme 0.1.0). Works
|
|
167
|
+
only after those versions publish or against a registry that has them. Treat
|
|
168
|
+
as pending publish.
|
|
169
|
+
|
|
126
170
|
This creates:
|
|
127
171
|
|
|
128
172
|
```text
|
|
129
|
-
noopolis/spawnfile-runtime-daimon:0.1.
|
|
173
|
+
noopolis/spawnfile-runtime-daimon:0.1.2-local
|
|
130
174
|
```
|
|
131
175
|
|
|
132
176
|
The image is not a full organization image and is not intended to be run
|
|
@@ -139,7 +183,7 @@ directly. It contains only:
|
|
|
139
183
|
Spawnfile can copy that path into generated organization images:
|
|
140
184
|
|
|
141
185
|
```bash
|
|
142
|
-
SPAWNFILE_DAIMON_RUNTIME_IMAGE=noopolis/spawnfile-runtime-daimon:0.1.
|
|
186
|
+
SPAWNFILE_DAIMON_RUNTIME_IMAGE=noopolis/spawnfile-runtime-daimon:0.1.2-local \
|
|
143
187
|
spawnfile build ./agentic-org
|
|
144
188
|
```
|
|
145
189
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type SiblingFileFact = {
|
|
2
|
+
path: string;
|
|
3
|
+
mtimeMs: number;
|
|
4
|
+
};
|
|
5
|
+
export type SiblingBuildFacts = {
|
|
6
|
+
packageName: string;
|
|
7
|
+
packageDirectory: string;
|
|
8
|
+
hasSourceDirectory: boolean;
|
|
9
|
+
sourceFiles: SiblingFileFact[];
|
|
10
|
+
outputFiles: SiblingFileFact[];
|
|
11
|
+
};
|
|
12
|
+
export type SiblingBuildFreshness = {
|
|
13
|
+
packageName: string;
|
|
14
|
+
linked: boolean;
|
|
15
|
+
sourcesScanned: number;
|
|
16
|
+
outputsScanned: number;
|
|
17
|
+
ok: boolean;
|
|
18
|
+
message?: string;
|
|
19
|
+
};
|
|
20
|
+
export declare function checkSiblingBuildFreshness(facts: SiblingBuildFacts): SiblingBuildFreshness;
|
|
21
|
+
//# sourceMappingURL=siblingBuildFreshness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"siblingBuildFreshness.d.ts","sourceRoot":"","sources":["../../src/core/siblingBuildFreshness.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,WAAW,EAAE,eAAe,EAAE,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAIF,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,iBAAiB,GAAG,qBAAqB,CA2B1F"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const excludedSource = /(?:\.test\.ts|\.test-helper\.ts|\.d\.ts)$/u;
|
|
2
|
+
export function checkSiblingBuildFreshness(facts) {
|
|
3
|
+
if (!facts.hasSourceDirectory) {
|
|
4
|
+
return { packageName: facts.packageName, linked: false, sourcesScanned: 0, outputsScanned: 0, ok: true };
|
|
5
|
+
}
|
|
6
|
+
const sourceFiles = facts.sourceFiles.filter(({ path }) => !excludedSource.test(path));
|
|
7
|
+
const outputFiles = facts.outputFiles.filter(({ path }) => path.endsWith(".js"));
|
|
8
|
+
const command = `run "npm run build" in ${facts.packageDirectory}`;
|
|
9
|
+
if (sourceFiles.length === 0) {
|
|
10
|
+
return failure(facts.packageName, sourceFiles.length, outputFiles.length, `linked package ${facts.packageName} scanned zero source files; ${command}`);
|
|
11
|
+
}
|
|
12
|
+
if (outputFiles.length === 0) {
|
|
13
|
+
return failure(facts.packageName, sourceFiles.length, outputFiles.length, `linked package ${facts.packageName} has no emitted JavaScript; ${command}`);
|
|
14
|
+
}
|
|
15
|
+
const newestSource = sourceFiles.reduce((newest, file) => file.mtimeMs > newest.mtimeMs ? file : newest);
|
|
16
|
+
const oldestOutput = outputFiles.reduce((oldest, file) => file.mtimeMs < oldest.mtimeMs ? file : oldest);
|
|
17
|
+
if (newestSource.mtimeMs > oldestOutput.mtimeMs) {
|
|
18
|
+
return failure(facts.packageName, sourceFiles.length, outputFiles.length, `linked package ${facts.packageName} is stale: newest source ${newestSource.path} (${newestSource.mtimeMs}) `
|
|
19
|
+
+ `is newer than oldest output ${oldestOutput.path} (${oldestOutput.mtimeMs}); ${command}`);
|
|
20
|
+
}
|
|
21
|
+
return { packageName: facts.packageName, linked: true, sourcesScanned: sourceFiles.length, outputsScanned: outputFiles.length, ok: true };
|
|
22
|
+
}
|
|
23
|
+
function failure(packageName, sourcesScanned, outputsScanned, message) {
|
|
24
|
+
return { packageName, linked: true, sourcesScanned, outputsScanned, ok: false, message };
|
|
25
|
+
}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -20,9 +20,15 @@ export interface HarnessModelSpec {
|
|
|
20
20
|
name: string;
|
|
21
21
|
provider: string;
|
|
22
22
|
}
|
|
23
|
+
export interface WakeDeliveryMetadata {
|
|
24
|
+
eventId: string;
|
|
25
|
+
sender: string;
|
|
26
|
+
target: string;
|
|
27
|
+
contextId: string;
|
|
28
|
+
}
|
|
23
29
|
export interface WakeEvent {
|
|
24
30
|
id: string;
|
|
25
|
-
kind: "manual" | "message" | "schedule";
|
|
31
|
+
kind: "manual" | "message" | "schedule" | "dream";
|
|
26
32
|
from?: string;
|
|
27
33
|
text: string;
|
|
28
34
|
context?: {
|
|
@@ -35,6 +41,9 @@ export interface WakeEvent {
|
|
|
35
41
|
pairPeers?: string[];
|
|
36
42
|
artifactPaths?: string[];
|
|
37
43
|
};
|
|
44
|
+
delivery?: WakeDeliveryMetadata;
|
|
45
|
+
/** Exact transport body retained outside the runtime-enriched model prompt. */
|
|
46
|
+
transportText?: string;
|
|
38
47
|
}
|
|
39
48
|
export interface WakeResult {
|
|
40
49
|
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;
|
|
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,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,OAAO,CAAC;IAClD,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;IACF,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,+EAA+E;IAC/E,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;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":"exampleCausalId.d.ts","sourceRoot":"","sources":["../../src/examples/exampleCausalId.ts"],"names":[],"mappings":"AAEA,+EAA+E;AAC/E,eAAO,MAAM,eAAe,GAAI,SAAS,MAAM,KAAG,MAKjD,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
const LOCAL_CAUSAL_ID = /^[a-z0-9][a-z0-9._-]{0,255}$/u;
|
|
2
|
+
/** Namespaces caller-authored example wakes for the shared causal contract. */
|
|
3
|
+
export const exampleCausalId = (localId) => {
|
|
4
|
+
if (!LOCAL_CAUSAL_ID.test(localId)) {
|
|
5
|
+
throw new Error("Daimon example causal id must be a bounded local id");
|
|
6
|
+
}
|
|
7
|
+
return `daimon:${localId}`;
|
|
8
|
+
};
|
|
@@ -4,6 +4,7 @@ import { fileURLToPath } from "node:url";
|
|
|
4
4
|
import { JsonlMemoryStore } from "@noopolis/mneme";
|
|
5
5
|
import { OrgObserver } from "../observability/index.js";
|
|
6
6
|
import { beatsFor, defaultDialogueTurns, selectVoicesForBeat } from "./jungianConversationPlan.js";
|
|
7
|
+
import { exampleCausalId } from "./exampleCausalId.js";
|
|
7
8
|
import { JungianVoice, runLimited } from "./jungianPlayAgent.js";
|
|
8
9
|
import { jungianSelves, playScenario } from "./jungianProfiles.js";
|
|
9
10
|
import { JungianTrace, parseInnerUsed, parseSpeakLine } from "./jungianTrace.js";
|
|
@@ -132,7 +133,7 @@ const runCouncil = async (self, eventId, focus, transcript, voices) => {
|
|
|
132
133
|
console.log(`\n== ${self.profile.name} inner council: ${focus} ==`);
|
|
133
134
|
const turns = await runLimited(voices, councilConcurrency, async (voice) => {
|
|
134
135
|
const event = {
|
|
135
|
-
id: `${eventId}-${voice.config.id}
|
|
136
|
+
id: exampleCausalId(`${eventId}-${voice.config.id}`),
|
|
136
137
|
kind: "manual",
|
|
137
138
|
context: roomContext,
|
|
138
139
|
text: councilPrompt(self.profile, focus, transcript)
|
|
@@ -155,7 +156,7 @@ const runCouncil = async (self, eventId, focus, transcript, voices) => {
|
|
|
155
156
|
};
|
|
156
157
|
const runRepresentative = async (self, eventId, focus, transcript, counsel) => {
|
|
157
158
|
const event = {
|
|
158
|
-
id: eventId,
|
|
159
|
+
id: exampleCausalId(eventId),
|
|
159
160
|
kind: "manual",
|
|
160
161
|
context: roomContext,
|
|
161
162
|
text: representativePrompt(self.profile, focus, transcript, counsel)
|
|
@@ -4,6 +4,7 @@ import { fileURLToPath } from "node:url";
|
|
|
4
4
|
import { JsonlMemoryStore } from "@noopolis/mneme";
|
|
5
5
|
import { OrgObserver } from "../observability/index.js";
|
|
6
6
|
import { PiHarnessAdapter } from "../pi/piHarness.js";
|
|
7
|
+
import { exampleCausalId } from "./exampleCausalId.js";
|
|
7
8
|
import { JungianPiRepresentative, seedPiCodexAuth } from "./jungianPiRepresentative.js";
|
|
8
9
|
import { JungianVoice, runLimited } from "./jungianPlayAgent.js";
|
|
9
10
|
import { JungianTrace, parseInnerUsed, parseSpeakLine } from "./jungianTrace.js";
|
|
@@ -134,7 +135,7 @@ const runCouncil = async (self, eventBase, focus, transcript, voices) => {
|
|
|
134
135
|
console.log(`\n== ${self.profile.name} inner counsel ==`);
|
|
135
136
|
return runLimited(voices, 2, async (voice) => {
|
|
136
137
|
const event = {
|
|
137
|
-
id: `${eventBase}-council-${voice.config.id}
|
|
138
|
+
id: exampleCausalId(`${eventBase}-council-${voice.config.id}`),
|
|
138
139
|
kind: "manual",
|
|
139
140
|
context: roomContext,
|
|
140
141
|
text: councilPrompt(self.profile, focus, transcript)
|
|
@@ -171,7 +172,7 @@ const assertUsedCounsel = (self, eventId, output, counsel) => {
|
|
|
171
172
|
};
|
|
172
173
|
const runRepresentative = async (self, eventBase, focus, transcript, counsel) => {
|
|
173
174
|
const event = {
|
|
174
|
-
id: `${eventBase}-speaks
|
|
175
|
+
id: exampleCausalId(`${eventBase}-speaks`),
|
|
175
176
|
kind: "manual",
|
|
176
177
|
context: roomContext,
|
|
177
178
|
text: representativePrompt(self.profile, focus, transcript, counsel)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { WakeEvent } from "../core/types.js";
|
|
2
2
|
import type { MemoryRecallAudit } from "@noopolis/mneme";
|
|
3
|
-
import { type EngineKind, type EngineRunResult } from "
|
|
3
|
+
import { type CliEngineKind as EngineKind, type EngineRunResult } from "../pi/cliSession.js";
|
|
4
4
|
export interface JungianVoiceConfig {
|
|
5
5
|
archetype?: string;
|
|
6
6
|
engine: EngineKind;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jungianPlayAgent.d.ts","sourceRoot":"","sources":["../../src/examples/jungianPlayAgent.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,KAAK,EAAE,iBAAiB,EAAiB,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAqB,KAAK,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"jungianPlayAgent.d.ts","sourceRoot":"","sources":["../../src/examples/jungianPlayAgent.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,KAAK,EAAE,iBAAiB,EAAiB,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAqB,KAAK,aAAa,IAAI,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEhH,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,UAAU,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,eAAe,CAAC;IAC9B,KAAK,EAAE,SAAS,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,kBAAkB,CAAC;CAC3B;AAUD,qBAAa,YAAY;IAKX,QAAQ,CAAC,MAAM,EAAE,kBAAkB;IAJ/C,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;gBAElB,MAAM,EAAE,kBAAkB;IAWzC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAkBxB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,gBAAgB,CAAC;CA4DxD;AAED,eAAO,MAAM,UAAU,GAAU,CAAC,EAAE,CAAC,EACnC,QAAQ,SAAS,CAAC,EAAE,EACpB,OAAO,MAAM,EACb,KAAK,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,KAC5B,OAAO,CAAC,CAAC,EAAE,CAYb,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { createMemoryRuntime } from "@noopolis/mneme";
|
|
4
|
-
import { runEngineDetailed } from "
|
|
4
|
+
import { runEngineDetailed } from "../pi/cliSession.js";
|
|
5
5
|
const maxMemoryChars = 2200;
|
|
6
6
|
const truncate = (text, maxChars) => text.length <= maxChars ? text : `${text.slice(0, maxChars).trim()}\n[truncated]`;
|
|
7
7
|
const memorySections = (turnPrompt) => truncate(turnPrompt, maxMemoryChars);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jungianProfiles.d.ts","sourceRoot":"","sources":["../../src/examples/jungianProfiles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"jungianProfiles.d.ts","sourceRoot":"","sources":["../../src/examples/jungianProfiles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEvE,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE;QACd,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,uBAAuB,EAAE,CAAC;CACnC;AA+JD,eAAO,MAAM,aAAa,EAAE,kBAAkB,EAiC7C,CAAC;AAEF,eAAO,MAAM,YAAY;;;;CAMxB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jungianTriadProfiles.d.ts","sourceRoot":"","sources":["../../src/examples/jungianTriadProfiles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"jungianTriadProfiles.d.ts","sourceRoot":"","sources":["../../src/examples/jungianTriadProfiles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEvE,MAAM,MAAM,oBAAoB,GAAG,UAAU,GAAG,IAAI,CAAC;AAErD,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,oBAAoB,CAAC;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE;QACd,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,qBAAqB,EAAE,CAAC;CACjC;AAsDD,eAAO,MAAM,WAAW,EAAE,gBAAgB,EAqCzC,CAAC;AAEF,eAAO,MAAM,aAAa;;;;CAMzB,CAAC"}
|
|
@@ -3,6 +3,7 @@ import path from "node:path";
|
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { seedPiOpenAICodexAuthFromCodex } from "../pi/auth.js";
|
|
5
5
|
import { PiHarnessAdapter } from "../pi/piHarness.js";
|
|
6
|
+
import { exampleCausalId } from "./exampleCausalId.js";
|
|
6
7
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
8
|
const daimonRoot = path.resolve(__dirname, "../..");
|
|
8
9
|
const runtimeRoot = path.join(daimonRoot, ".runtime", "pi-agent");
|
|
@@ -66,7 +67,7 @@ const run = async () => {
|
|
|
66
67
|
try {
|
|
67
68
|
console.log("started", JSON.stringify([mapper.status(), reviewer.status()], null, 2));
|
|
68
69
|
const mapped = await mapper.wake({
|
|
69
|
-
id: "wake-mapper-1",
|
|
70
|
+
id: exampleCausalId("wake-mapper-1"),
|
|
70
71
|
kind: "manual",
|
|
71
72
|
from: "caller",
|
|
72
73
|
text: [
|
|
@@ -78,7 +79,7 @@ const run = async () => {
|
|
|
78
79
|
});
|
|
79
80
|
console.log("mapper", JSON.stringify(mapped, null, 2));
|
|
80
81
|
const reviewed = await reviewer.wake({
|
|
81
|
-
id: "wake-reviewer-1",
|
|
82
|
+
id: exampleCausalId("wake-reviewer-1"),
|
|
82
83
|
kind: "message",
|
|
83
84
|
from: "mapper",
|
|
84
85
|
text: [
|
|
@@ -4,6 +4,7 @@ import { fileURLToPath } from "node:url";
|
|
|
4
4
|
import { JsonlMemoryStore } from "@noopolis/mneme";
|
|
5
5
|
import { seedPiOpenAICodexAuthFromCodex } from "../pi/auth.js";
|
|
6
6
|
import { PiHarnessAdapter } from "../pi/piHarness.js";
|
|
7
|
+
import { exampleCausalId } from "./exampleCausalId.js";
|
|
7
8
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
9
|
const daimonRoot = path.resolve(__dirname, "../..");
|
|
9
10
|
const runtimeRoot = path.join(daimonRoot, ".runtime", "pi-memory-org");
|
|
@@ -92,7 +93,7 @@ const seedPrivateMemories = async (handles) => {
|
|
|
92
93
|
console.log("\n== Private seed phase ==");
|
|
93
94
|
for (const agent of demoAgents) {
|
|
94
95
|
const result = await handles.get(agent.id)?.wake({
|
|
95
|
-
id: `seed-${agent.id}
|
|
96
|
+
id: exampleCausalId(`seed-${agent.id}`),
|
|
96
97
|
kind: "manual",
|
|
97
98
|
text: [
|
|
98
99
|
"Private memory seed.",
|
|
@@ -107,7 +108,7 @@ const runRoomConversation = async (handles) => {
|
|
|
107
108
|
console.log("\n== Room recall phase ==");
|
|
108
109
|
const transcript = [];
|
|
109
110
|
const atlas = await wake(handles.get("atlas"), {
|
|
110
|
-
id: "room-atlas-1",
|
|
111
|
+
id: exampleCausalId("room-atlas-1"),
|
|
111
112
|
kind: "manual",
|
|
112
113
|
context: roomContext,
|
|
113
114
|
text: [
|
|
@@ -121,7 +122,7 @@ const runRoomConversation = async (handles) => {
|
|
|
121
122
|
assertIncludes("atlas room reply", atlas.text, "ATLAS_BLUE_17");
|
|
122
123
|
transcript.push(`atlas: ${atlas.text}`);
|
|
123
124
|
const oracle = await wake(handles.get("oracle"), {
|
|
124
|
-
id: "room-oracle-1",
|
|
125
|
+
id: exampleCausalId("room-oracle-1"),
|
|
125
126
|
kind: "manual",
|
|
126
127
|
context: roomContext,
|
|
127
128
|
text: [
|
|
@@ -135,7 +136,7 @@ const runRoomConversation = async (handles) => {
|
|
|
135
136
|
assertIncludes("oracle room reply", oracle.text, "ATLAS_BLUE_17");
|
|
136
137
|
transcript.push(`oracle: ${oracle.text}`);
|
|
137
138
|
const keeper = await wake(handles.get("keeper"), {
|
|
138
|
-
id: "room-keeper-1",
|
|
139
|
+
id: exampleCausalId("room-keeper-1"),
|
|
139
140
|
kind: "manual",
|
|
140
141
|
context: roomContext,
|
|
141
142
|
text: [
|
|
@@ -162,7 +163,7 @@ const verifyFreshSessionRecall = async (adapter, previousKeeper) => {
|
|
|
162
163
|
const keeper = await startAgent(adapter, demoAgents.find((agent) => agent.id === "keeper"));
|
|
163
164
|
try {
|
|
164
165
|
const event = {
|
|
165
|
-
id: "room-keeper-2",
|
|
166
|
+
id: exampleCausalId("room-keeper-2"),
|
|
166
167
|
kind: "manual",
|
|
167
168
|
context: roomContext,
|
|
168
169
|
text: [
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,0BAA0B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import type { ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
export declare class McpToolTurnLimitError extends Error {
|
|
4
|
+
constructor(maxToolTurns: number);
|
|
5
|
+
}
|
|
6
|
+
export declare class McpWakeDeadlineError extends Error {
|
|
7
|
+
constructor();
|
|
8
|
+
}
|
|
9
|
+
export interface PiToolMcpServerOptions {
|
|
10
|
+
readonly maxToolTurns: number;
|
|
11
|
+
readonly wakeDeadline: number;
|
|
12
|
+
}
|
|
13
|
+
export declare const createPiToolMcpServer: (tools: ToolDefinition[], options: PiToolMcpServerOptions) => Server;
|
|
14
|
+
//# sourceMappingURL=toolServer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolServer.d.ts","sourceRoot":"","sources":["../../src/mcp/toolServer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAYnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEtE,qBAAa,qBAAsB,SAAQ,KAAK;gBAC3B,YAAY,EAAE,MAAM;CAIxC;AAED,qBAAa,oBAAqB,SAAQ,KAAK;;CAK9C;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAqCD,eAAO,MAAM,qBAAqB,GAChC,OAAO,cAAc,EAAE,EACvB,SAAS,sBAAsB,KAC9B,MAmEF,CAAC"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { Ajv2020 } from "ajv/dist/2020.js";
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
export class McpToolTurnLimitError extends Error {
|
|
5
|
+
constructor(maxToolTurns) {
|
|
6
|
+
super(`McpToolTurnLimitError: maximum ${maxToolTurns} calls per wake`);
|
|
7
|
+
this.name = "McpToolTurnLimitError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export class McpWakeDeadlineError extends Error {
|
|
11
|
+
constructor() {
|
|
12
|
+
super("McpWakeDeadlineError: wake deadline exceeded");
|
|
13
|
+
this.name = "McpWakeDeadlineError";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const jsonSchema = (parameters) => {
|
|
17
|
+
if (parameters === null || typeof parameters !== "object" || Array.isArray(parameters)) {
|
|
18
|
+
throw new TypeError("Pi tool parameters must be a JSON schema object");
|
|
19
|
+
}
|
|
20
|
+
return Object.fromEntries(Object.entries(parameters));
|
|
21
|
+
};
|
|
22
|
+
const toolResult = (result) => ({
|
|
23
|
+
content: result.content,
|
|
24
|
+
...(result.details !== undefined && typeof result.details === "object" && result.details !== null
|
|
25
|
+
? { structuredContent: Object.fromEntries(Object.entries(result.details)) }
|
|
26
|
+
: {})
|
|
27
|
+
});
|
|
28
|
+
const toolError = (error) => ({
|
|
29
|
+
content: [{ type: "text", text: error instanceof Error ? `${error.name}: ${error.message}` : String(error) }],
|
|
30
|
+
isError: true
|
|
31
|
+
});
|
|
32
|
+
// Pi's ExtensionContext has no meaning outside a Pi session. Mounted tools
|
|
33
|
+
// must not read it; this named value documents the explicit absence.
|
|
34
|
+
// The MCP mount deliberately has no Pi session context; `never` preserves typed positional checks.
|
|
35
|
+
const NO_PI_EXTENSION_CONTEXT = undefined;
|
|
36
|
+
const validateOptions = (options) => {
|
|
37
|
+
if (!Number.isSafeInteger(options.maxToolTurns) || options.maxToolTurns < 1) {
|
|
38
|
+
throw new TypeError("maxToolTurns must be a positive safe integer");
|
|
39
|
+
}
|
|
40
|
+
if (!Number.isFinite(options.wakeDeadline)) {
|
|
41
|
+
throw new TypeError("wakeDeadline must be a finite epoch-millisecond deadline");
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
export const createPiToolMcpServer = (tools, options) => {
|
|
45
|
+
validateOptions(options);
|
|
46
|
+
const server = new Server({ name: "daimon-pi-tools", version: "0.1.2" });
|
|
47
|
+
const validators = new Map(tools.map((tool) => {
|
|
48
|
+
const schema = jsonSchema(tool.parameters);
|
|
49
|
+
return [tool.name, new Ajv2020({ strict: false }).compile(schema)];
|
|
50
|
+
}));
|
|
51
|
+
let toolTurns = 0;
|
|
52
|
+
server.registerCapabilities({ tools: { listChanged: true } });
|
|
53
|
+
server.setRequestHandler(ListToolsRequestSchema, () => ({
|
|
54
|
+
tools: tools.map((tool) => ({
|
|
55
|
+
name: tool.name,
|
|
56
|
+
title: tool.label,
|
|
57
|
+
description: tool.description,
|
|
58
|
+
inputSchema: jsonSchema(tool.parameters)
|
|
59
|
+
}))
|
|
60
|
+
}));
|
|
61
|
+
server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
62
|
+
try {
|
|
63
|
+
if (Date.now() >= options.wakeDeadline)
|
|
64
|
+
throw new McpWakeDeadlineError();
|
|
65
|
+
if (toolTurns >= options.maxToolTurns)
|
|
66
|
+
throw new McpToolTurnLimitError(options.maxToolTurns);
|
|
67
|
+
const tool = tools.find((candidate) => candidate.name === request.params.name);
|
|
68
|
+
const validator = validators.get(request.params.name);
|
|
69
|
+
if (tool === undefined || validator === undefined) {
|
|
70
|
+
throw new McpError(ErrorCode.InvalidParams, `Unknown tool: ${request.params.name}`);
|
|
71
|
+
}
|
|
72
|
+
const args = request.params.arguments ?? {};
|
|
73
|
+
if (!validator(args)) {
|
|
74
|
+
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for tool ${tool.name}`);
|
|
75
|
+
}
|
|
76
|
+
toolTurns += 1;
|
|
77
|
+
// Ajv validated this value against this tool's own schema immediately above.
|
|
78
|
+
const validatedArgs = args;
|
|
79
|
+
const deadlineController = new AbortController();
|
|
80
|
+
const remainingMs = Math.max(0, options.wakeDeadline - Date.now());
|
|
81
|
+
const deadlineTimer = setTimeout(() => deadlineController.abort(), remainingMs);
|
|
82
|
+
const signal = extra.signal === undefined
|
|
83
|
+
? deadlineController.signal
|
|
84
|
+
: AbortSignal.any([extra.signal, deadlineController.signal]);
|
|
85
|
+
const deadline = new Promise((_resolve, reject) => {
|
|
86
|
+
deadlineController.signal.addEventListener("abort", () => reject(new McpWakeDeadlineError()), { once: true });
|
|
87
|
+
});
|
|
88
|
+
let result;
|
|
89
|
+
try {
|
|
90
|
+
result = await Promise.race([
|
|
91
|
+
tool.execute(`mcp-tool-turn-${toolTurns}`, validatedArgs, signal, undefined, NO_PI_EXTENSION_CONTEXT),
|
|
92
|
+
deadline
|
|
93
|
+
]);
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
clearTimeout(deadlineTimer);
|
|
97
|
+
}
|
|
98
|
+
return toolResult(result);
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
return toolError(error);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
return server;
|
|
105
|
+
};
|