@noopolis/daimon 0.1.0 → 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.
Files changed (116) hide show
  1. package/README.md +141 -5
  2. package/dist/core/siblingBuildFreshness.d.ts +21 -0
  3. package/dist/core/siblingBuildFreshness.d.ts.map +1 -0
  4. package/dist/core/siblingBuildFreshness.js +25 -0
  5. package/dist/core/types.d.ts +20 -1
  6. package/dist/core/types.d.ts.map +1 -1
  7. package/dist/examples/exampleCausalId.d.ts +3 -0
  8. package/dist/examples/exampleCausalId.d.ts.map +1 -0
  9. package/dist/examples/exampleCausalId.js +8 -0
  10. package/dist/examples/jungian-play-org.d.ts +2 -0
  11. package/dist/examples/jungian-play-org.d.ts.map +1 -0
  12. package/dist/examples/jungian-play-org.js +281 -0
  13. package/dist/examples/jungian-triad-org.d.ts +2 -0
  14. package/dist/examples/jungian-triad-org.d.ts.map +1 -0
  15. package/dist/examples/jungian-triad-org.js +281 -0
  16. package/dist/examples/jungianConversationPlan.d.ts +10 -0
  17. package/dist/examples/jungianConversationPlan.d.ts.map +1 -0
  18. package/dist/examples/jungianConversationPlan.js +64 -0
  19. package/dist/examples/jungianPiRepresentative.d.ts +36 -0
  20. package/dist/examples/jungianPiRepresentative.d.ts.map +1 -0
  21. package/dist/examples/jungianPiRepresentative.js +81 -0
  22. package/dist/examples/jungianPlayAgent.d.ts +36 -0
  23. package/dist/examples/jungianPlayAgent.d.ts.map +1 -0
  24. package/dist/examples/jungianPlayAgent.js +111 -0
  25. package/dist/examples/jungianProfiles.d.ts +34 -0
  26. package/dist/examples/jungianProfiles.d.ts.map +1 -0
  27. package/dist/examples/jungianProfiles.js +190 -0
  28. package/dist/examples/jungianTrace.d.ts +47 -0
  29. package/dist/examples/jungianTrace.d.ts.map +1 -0
  30. package/dist/examples/jungianTrace.js +101 -0
  31. package/dist/examples/jungianTriadProfiles.d.ts +35 -0
  32. package/dist/examples/jungianTriadProfiles.d.ts.map +1 -0
  33. package/dist/examples/jungianTriadProfiles.js +81 -0
  34. package/dist/examples/pi-agent.js +3 -2
  35. package/dist/examples/pi-memory-org.d.ts +2 -0
  36. package/dist/examples/pi-memory-org.d.ts.map +1 -0
  37. package/dist/examples/pi-memory-org.js +231 -0
  38. package/dist/index.d.ts +2 -3
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +2 -3
  41. package/dist/mcp/toolServer.d.ts +14 -0
  42. package/dist/mcp/toolServer.d.ts.map +1 -0
  43. package/dist/mcp/toolServer.js +105 -0
  44. package/dist/observability/causalEvents.d.ts +118 -0
  45. package/dist/observability/causalEvents.d.ts.map +1 -0
  46. package/dist/observability/causalEvents.js +135 -0
  47. package/dist/observability/controlCausal.d.ts +117 -0
  48. package/dist/observability/controlCausal.d.ts.map +1 -0
  49. package/dist/observability/controlCausal.js +107 -0
  50. package/dist/observability/emitCausalFixture.d.ts +25 -0
  51. package/dist/observability/emitCausalFixture.d.ts.map +1 -0
  52. package/dist/observability/emitCausalFixture.js +98 -0
  53. package/dist/observability/index.d.ts +4 -0
  54. package/dist/observability/index.d.ts.map +1 -0
  55. package/dist/observability/index.js +3 -0
  56. package/dist/observability/orgObserver.d.ts +98 -0
  57. package/dist/observability/orgObserver.d.ts.map +1 -0
  58. package/dist/observability/orgObserver.js +181 -0
  59. package/dist/pi/cliSession.d.ts +46 -0
  60. package/dist/pi/cliSession.d.ts.map +1 -0
  61. package/dist/pi/cliSession.js +224 -0
  62. package/dist/pi/index.d.ts +9 -0
  63. package/dist/pi/index.d.ts.map +1 -0
  64. package/dist/pi/index.js +8 -0
  65. package/dist/pi/memoryTools.d.ts +69 -0
  66. package/dist/pi/memoryTools.d.ts.map +1 -0
  67. package/dist/pi/memoryTools.js +190 -0
  68. package/dist/pi/modelRegistry.d.ts +13 -0
  69. package/dist/pi/modelRegistry.d.ts.map +1 -0
  70. package/dist/pi/modelRegistry.js +19 -0
  71. package/dist/pi/piAgentHandle.d.ts +45 -0
  72. package/dist/pi/piAgentHandle.d.ts.map +1 -0
  73. package/dist/pi/piAgentHandle.js +324 -0
  74. package/dist/pi/piAgentWakeSupport.d.ts +68 -0
  75. package/dist/pi/piAgentWakeSupport.d.ts.map +1 -0
  76. package/dist/pi/piAgentWakeSupport.js +147 -0
  77. package/dist/pi/piHarness.d.ts +36 -2
  78. package/dist/pi/piHarness.d.ts.map +1 -1
  79. package/dist/pi/piHarness.js +100 -127
  80. package/dist/pi/prompts.d.ts +9 -0
  81. package/dist/pi/prompts.d.ts.map +1 -0
  82. package/dist/pi/prompts.js +39 -0
  83. package/dist/pi/rawTrainingCapture.d.ts +59 -0
  84. package/dist/pi/rawTrainingCapture.d.ts.map +1 -0
  85. package/dist/pi/rawTrainingCapture.js +175 -0
  86. package/dist/pi/turnCausal.d.ts +73 -0
  87. package/dist/pi/turnCausal.d.ts.map +1 -0
  88. package/dist/pi/turnCausal.js +75 -0
  89. package/dist/pi/turnTrace.d.ts +128 -0
  90. package/dist/pi/turnTrace.d.ts.map +1 -0
  91. package/dist/pi/turnTrace.js +141 -0
  92. package/dist/pi/wakeAcceptance.d.ts +49 -0
  93. package/dist/pi/wakeAcceptance.d.ts.map +1 -0
  94. package/dist/pi/wakeAcceptance.js +281 -0
  95. package/dist/pi/wakeAcceptanceFs.d.ts +44 -0
  96. package/dist/pi/wakeAcceptanceFs.d.ts.map +1 -0
  97. package/dist/pi/wakeAcceptanceFs.js +318 -0
  98. package/dist/pi/wakeAcceptanceSchema.d.ts +75 -0
  99. package/dist/pi/wakeAcceptanceSchema.d.ts.map +1 -0
  100. package/dist/pi/wakeAcceptanceSchema.js +265 -0
  101. package/dist/pi/wakeModes.d.ts +9 -0
  102. package/dist/pi/wakeModes.d.ts.map +1 -0
  103. package/dist/pi/wakeModes.js +19 -0
  104. package/dist/pi/worldNudge.d.ts +22 -0
  105. package/dist/pi/worldNudge.d.ts.map +1 -0
  106. package/dist/pi/worldNudge.js +62 -0
  107. package/dist/pi/worldToolProtocol.d.ts +13 -0
  108. package/dist/pi/worldToolProtocol.d.ts.map +1 -0
  109. package/dist/pi/worldToolProtocol.js +49 -0
  110. package/dist/pi/worldTools.d.ts +32 -0
  111. package/dist/pi/worldTools.d.ts.map +1 -0
  112. package/dist/pi/worldTools.js +373 -0
  113. package/dist/pi/worldTrajectory.d.ts +52 -0
  114. package/dist/pi/worldTrajectory.d.ts.map +1 -0
  115. package/dist/pi/worldTrajectory.js +131 -0
  116. package/package.json +23 -4
@@ -1,128 +1,31 @@
1
1
  import { mkdir } from "node:fs/promises";
2
2
  import path from "node:path";
3
- import { AuthStorage, createAgentSession, createExtensionRuntime, ModelRegistry, SessionManager, SettingsManager } from "@earendil-works/pi-coding-agent";
3
+ import { AuthStorage, createAgentSession, SessionManager, SettingsManager } from "@earendil-works/pi-coding-agent";
4
+ import { createMemoryRuntime } from "@noopolis/mneme";
4
5
  import { resolvePiHarnessModel } from "./modelConfig.js";
5
- const createModelRegistry = (authStorage, options) => {
6
- const registry = options.modelsPath
7
- ? ModelRegistry.create(authStorage, options.modelsPath)
8
- : ModelRegistry.inMemory(authStorage);
9
- if (!options.modelsPath && options.model?.endpoint) {
10
- const { modelsConfig } = resolvePiHarnessModel(options.model);
11
- for (const [provider, config] of Object.entries(modelsConfig.providers)) {
12
- registry.registerProvider(provider, {
13
- api: config.api,
14
- apiKey: config.apiKey,
15
- baseUrl: config.baseUrl,
16
- models: config.models
17
- });
18
- }
19
- }
20
- return registry;
21
- };
22
- class PiAgentHandle {
23
- id;
24
- session;
25
- state = "idle";
26
- lastWakeAt;
27
- lastError;
28
- constructor(id, session) {
29
- this.id = id;
30
- this.session = session;
31
- }
32
- async wake(event) {
33
- const startedAt = Date.now();
34
- const chunks = [];
35
- this.state = "running";
36
- this.lastWakeAt = new Date().toISOString();
37
- this.lastError = undefined;
38
- const unsubscribe = this.session.subscribe((piEvent) => {
39
- if (piEvent.type !== "turn_end") {
40
- return;
41
- }
42
- const message = piEvent.message;
43
- const content = message.content;
44
- if (typeof content === "string") {
45
- chunks.push(content);
46
- }
47
- else if (Array.isArray(content)) {
48
- chunks.push(content
49
- .filter((item) => {
50
- const candidate = item;
51
- return candidate.type === "text" && typeof candidate.text === "string";
52
- })
53
- .map((item) => item.text)
54
- .join(""));
55
- }
56
- });
57
- try {
58
- await this.session.prompt(formatWakePrompt(event), { expandPromptTemplates: false });
59
- this.state = "idle";
60
- return {
61
- agentId: this.id,
62
- text: chunks.join("\n").trim(),
63
- durationMs: Date.now() - startedAt
64
- };
65
- }
66
- catch (error) {
67
- this.state = "failed";
68
- this.lastError = error instanceof Error ? error.message : String(error);
69
- throw error;
70
- }
71
- finally {
72
- unsubscribe();
73
- }
74
- }
75
- status() {
76
- return {
77
- agentId: this.id,
78
- state: this.state,
79
- lastWakeAt: this.lastWakeAt,
80
- lastError: this.lastError
81
- };
82
- }
83
- async stop() {
84
- this.session.dispose();
85
- this.state = "stopped";
86
- }
87
- }
88
- const formatWakePrompt = (event) => `Wake event:
89
- - id: ${event.id}
90
- - kind: ${event.kind}
91
- - from: ${event.from ?? "operator"}
92
-
93
- ${event.text}`;
94
- const createResourceLoader = (input) => {
95
- const systemPrompt = [
96
- `You are ${input.name} (${input.id}).`,
97
- input.instructions,
98
- "You are running inside a harnessed workspace prepared by the caller.",
99
- "Use the available coding tools when asked to read, write, edit, or inspect files.",
100
- "Keep responses brief and report the exact files you created or modified."
101
- ].join("\n\n");
102
- return {
103
- getExtensions: () => ({ extensions: [], errors: [], runtime: createExtensionRuntime() }),
104
- getSkills: () => ({ skills: [], diagnostics: [] }),
105
- getPrompts: () => ({ prompts: [], diagnostics: [] }),
106
- getThemes: () => ({ themes: [], diagnostics: [] }),
107
- getAgentsFiles: () => ({ agentsFiles: [] }),
108
- getSystemPrompt: () => systemPrompt,
109
- getAppendSystemPrompt: () => [],
110
- extendResources: () => { },
111
- reload: async () => { }
112
- };
113
- };
6
+ import { createPiModelRegistry } from "./modelRegistry.js";
7
+ import { createPiMemoryTools, piMemoryToolNames } from "./memoryTools.js";
8
+ import { createResourceLoader } from "./prompts.js";
9
+ import { PiAgentHandle } from "./piAgentHandle.js";
10
+ import { createPiWorldTools, piWorldToolNames } from "./worldTools.js";
11
+ import { bindPiRawTrainingCapture, validatePiRawTrainingCaptureOptions } from "./rawTrainingCapture.js";
114
12
  export class PiHarnessAdapter {
115
13
  options;
116
14
  authStorage;
117
15
  modelRegistry;
16
+ sessionFactory;
118
17
  constructor(options) {
119
18
  this.options = options;
120
19
  this.authStorage = AuthStorage.create(options.authPath);
121
- this.modelRegistry = createModelRegistry(this.authStorage, options);
20
+ this.modelRegistry = createPiModelRegistry(this.authStorage, options);
21
+ this.sessionFactory = options.sessionFactory ?? createAgentSession;
122
22
  }
123
23
  async startAgent(input) {
24
+ validatePiRawTrainingCaptureOptions(this.options.rawTrainingCapture);
124
25
  await mkdir(input.runtimeHomePath, { recursive: true });
125
26
  await mkdir(input.workspacePath, { recursive: true });
27
+ const memoryRuntimeHomePath = this.options.memory?.runtimeHomePath ?? input.runtimeHomePath;
28
+ await mkdir(memoryRuntimeHomePath, { recursive: true });
126
29
  const modelSpec = this.options.model ?? {
127
30
  auth: { method: "codex" },
128
31
  provider: "openai",
@@ -133,21 +36,91 @@ export class PiHarnessAdapter {
133
36
  if (!model) {
134
37
  throw new Error(`Pi model not found: ${resolvedModel.provider}/${resolvedModel.name}`);
135
38
  }
136
- const { session } = await createAgentSession({
137
- cwd: input.workspacePath,
138
- agentDir: input.runtimeHomePath,
139
- authStorage: this.authStorage,
140
- modelRegistry: this.modelRegistry,
141
- model,
142
- thinkingLevel: "off",
143
- resourceLoader: createResourceLoader(input),
144
- tools: input.tools ?? ["read", "write", "edit", "bash", "grep", "find", "ls"],
145
- sessionManager: SessionManager.create(input.workspacePath, path.join(input.runtimeHomePath, "sessions")),
146
- settingsManager: SettingsManager.inMemory({
147
- compaction: { enabled: false },
148
- retry: { enabled: true, maxRetries: 1 }
149
- })
150
- });
151
- return new PiAgentHandle(input.id, session);
39
+ const memory = this.options.memory === undefined
40
+ ? undefined
41
+ : createMemoryRuntime({
42
+ agentId: input.id,
43
+ authority: this.options.memory.authority,
44
+ embeddingProvider: this.options.memory.embeddingProvider,
45
+ runtimeHomePath: memoryRuntimeHomePath,
46
+ source: this.options.memory.source,
47
+ tokenBudget: this.options.memory.tokenBudget
48
+ });
49
+ const memoryToolContext = memory === undefined ? undefined : {};
50
+ const worldToolContext = this.options.world === undefined ? undefined : {};
51
+ const rawTrainingCaptureRef = this.options.rawTrainingCapture === undefined ? undefined : {};
52
+ const sessionInput = (mode, sessionDirectory) => {
53
+ const memoryTools = memory === undefined || memoryToolContext === undefined
54
+ ? []
55
+ : createPiMemoryTools({
56
+ agentId: input.id,
57
+ memory,
58
+ contextRef: memoryToolContext,
59
+ mode
60
+ });
61
+ const worldTools = this.options.world === undefined
62
+ ? undefined
63
+ : createPiWorldTools({
64
+ world: this.options.world,
65
+ contextRef: worldToolContext
66
+ });
67
+ const toolNames = [
68
+ ...(input.tools ?? ["read", "write", "edit", "bash", "grep", "find", "ls"]),
69
+ ...piMemoryToolNames(memoryTools),
70
+ ...(worldTools === undefined ? [] : piWorldToolNames(worldTools))
71
+ ];
72
+ return {
73
+ cwd: input.workspacePath,
74
+ agentDir: input.runtimeHomePath,
75
+ daimonSecretEnvironmentNames: this.options.world === undefined ? [] : [this.options.world.tokenEnv],
76
+ authStorage: this.authStorage,
77
+ modelRegistry: this.modelRegistry,
78
+ model,
79
+ thinkingLevel: this.options.thinkingLevel ?? "off",
80
+ resourceLoader: createResourceLoader(input, mode, {
81
+ memory: memory !== undefined,
82
+ world: worldTools !== undefined
83
+ }),
84
+ tools: [...new Set(toolNames)],
85
+ customTools: worldTools === undefined ? memoryTools : [...memoryTools, ...worldTools],
86
+ sessionManager: SessionManager.create(input.workspacePath, sessionDirectory),
87
+ settingsManager: SettingsManager.inMemory({
88
+ compaction: { enabled: false },
89
+ retry: { enabled: true, maxRetries: 1 }
90
+ })
91
+ };
92
+ };
93
+ if (this.options.rawTrainingCapture !== undefined) {
94
+ const createSession = async (mode, sessionDirectory) => {
95
+ const { session } = await createAgentSession(sessionInput(mode, sessionDirectory));
96
+ if (rawTrainingCaptureRef !== undefined) {
97
+ bindPiRawTrainingCapture(session, rawTrainingCaptureRef);
98
+ }
99
+ return session;
100
+ };
101
+ const session = await createSession("awake", path.join(input.runtimeHomePath, "sessions"));
102
+ return new PiAgentHandle(input.id, session, createSession, input.runtimeHomePath, {
103
+ authMethod: modelSpec.auth?.method ?? "none",
104
+ model: resolvedModel.name,
105
+ provider: resolvedModel.provider
106
+ }, memory, memoryToolContext, {}, worldToolContext, rawTrainingCaptureRef, this.options.rawTrainingCapture, worldToolContext === undefined
107
+ ? undefined
108
+ : { instructions: input.instructions, thinkingLevel: this.options.thinkingLevel ?? "off" }, session);
109
+ }
110
+ const createSession = async (mode, sessionDirectory) => {
111
+ const { session } = await (this.options.sessionFactory ?? createAgentSession)(sessionInput(mode, sessionDirectory));
112
+ return session;
113
+ };
114
+ const session = await createSession("awake", path.join(input.runtimeHomePath, "sessions"));
115
+ return new PiAgentHandle(input.id, session, createSession, input.runtimeHomePath, {
116
+ authMethod: modelSpec.auth?.method ?? "none",
117
+ model: resolvedModel.name,
118
+ provider: resolvedModel.provider
119
+ }, memory, memoryToolContext, {}, worldToolContext, undefined, undefined, worldToolContext === undefined
120
+ ? undefined
121
+ : {
122
+ instructions: input.instructions,
123
+ thinkingLevel: this.options.thinkingLevel ?? "off"
124
+ }, undefined);
152
125
  }
153
126
  }
@@ -0,0 +1,9 @@
1
+ import { type ResourceLoader } from "@earendil-works/pi-coding-agent";
2
+ import { type MemoryWakeMode } from "@noopolis/mneme";
3
+ import type { AgentStartInput, WakeEvent } from "../core/types.js";
4
+ export declare const formatWakePrompt: (event: WakeEvent) => string;
5
+ export declare const createResourceLoader: (input: AgentStartInput, mode: MemoryWakeMode, capabilities: Readonly<{
6
+ memory: boolean;
7
+ world: boolean;
8
+ }>) => ResourceLoader;
9
+ //# sourceMappingURL=prompts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/pi/prompts.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EACpB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAEL,KAAK,cAAc,EACpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAEnE,eAAO,MAAM,gBAAgB,GAAI,OAAO,SAAS,KAAG,MAKtC,CAAC;AAEf,eAAO,MAAM,oBAAoB,GAC/B,OAAO,eAAe,EACtB,MAAM,cAAc,EACpB,cAAc,QAAQ,CAAC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC,KAC1D,cA+BF,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { createExtensionRuntime } from "@earendil-works/pi-coding-agent";
2
+ import { getMemorySkillTextForMode } from "@noopolis/mneme";
3
+ export const formatWakePrompt = (event) => `Wake event:
4
+ - id: ${event.id}
5
+ - kind: ${event.kind}
6
+ - from: ${event.from === undefined ? "[no attribution supplied] (absence)" : JSON.stringify(event.from)}
7
+
8
+ ${event.text}`;
9
+ export const createResourceLoader = (input, mode, capabilities) => {
10
+ const systemPrompt = [
11
+ `You are ${input.name} (${input.id}).`,
12
+ input.instructions,
13
+ "You are running inside a harnessed workspace prepared by the caller.",
14
+ ...(input.tools === undefined || input.tools.length > 0
15
+ ? [
16
+ "Use the available coding tools when asked to read, write, edit, or inspect files.",
17
+ "Keep responses brief and report the exact files you created or modified."
18
+ ]
19
+ : []),
20
+ ...(capabilities.memory ? [getMemorySkillTextForMode(mode)] : []),
21
+ ...(capabilities.world
22
+ ? [
23
+ "Use only the authenticated world tools and standing instructions to perceive and act. "
24
+ + "The harness binds wake authority and request identity; choose only the sense, affordance, target, and typed action input exposed by tool schemas."
25
+ ]
26
+ : [])
27
+ ].filter((section) => section.length > 0).join("\n\n");
28
+ return {
29
+ getExtensions: () => ({ extensions: [], errors: [], runtime: createExtensionRuntime() }),
30
+ getSkills: () => ({ skills: [], diagnostics: [] }),
31
+ getPrompts: () => ({ prompts: [], diagnostics: [] }),
32
+ getThemes: () => ({ themes: [], diagnostics: [] }),
33
+ getAgentsFiles: () => ({ agentsFiles: [] }),
34
+ getSystemPrompt: () => systemPrompt,
35
+ getAppendSystemPrompt: () => [],
36
+ extendResources: () => { },
37
+ reload: async () => { }
38
+ };
39
+ };
@@ -0,0 +1,59 @@
1
+ import type { createAgentSession } from "@earendil-works/pi-coding-agent";
2
+ import type { PiWorldTurnContext } from "./worldNudge.js";
3
+ export declare const PI_RAW_TRAINING_CAPTURE_SCHEMA: "daimon.pi.raw_training_capture.v2";
4
+ export interface PiRawTrainingCaptureOptions {
5
+ enabled: true;
6
+ retention: {
7
+ maxTurns: number;
8
+ };
9
+ }
10
+ export interface PiRawTrainingCapture {
11
+ readonly events: string[];
12
+ readonly requests: Array<{
13
+ model: unknown;
14
+ payload: unknown;
15
+ requested_at: string;
16
+ response?: unknown;
17
+ response_at?: string;
18
+ sequence: number;
19
+ }>;
20
+ }
21
+ export interface PiRawTrainingCaptureRef {
22
+ current?: PiRawTrainingCapture;
23
+ }
24
+ type PiNativeSession = Awaited<ReturnType<typeof createAgentSession>>["session"];
25
+ type PiRawTrainingSession = Pick<PiNativeSession, "agent" | "model" | "sessionFile" | "sessionId" | "thinkingLevel">;
26
+ export interface PersistPiRawTrainingCaptureInput {
27
+ agentId: string;
28
+ capture: PiRawTrainingCapture;
29
+ completedAt: Date;
30
+ options: PiRawTrainingCaptureOptions;
31
+ runtimeHomePath: string;
32
+ session: PiRawTrainingSession;
33
+ startedAt: Date;
34
+ status: "completed" | "failed";
35
+ totalMs: number;
36
+ turnId: string;
37
+ world?: PiWorldTurnContext;
38
+ }
39
+ /**
40
+ * Installs a transparent recorder at Pi's native provider-payload seam.
41
+ *
42
+ * The recorder returns the exact result of any pre-existing hook, so enabling
43
+ * capture cannot alter the request sent to the model.
44
+ */
45
+ export declare const bindPiRawTrainingCapture: (session: PiRawTrainingSession, ref: PiRawTrainingCaptureRef) => void;
46
+ export declare const createPiRawTrainingCapture: () => PiRawTrainingCapture;
47
+ export declare const capturePiRawTrainingEvent: (capture: PiRawTrainingCapture, event: unknown, now?: Date) => void;
48
+ export declare const validatePiRawTrainingCaptureOptions: (options: PiRawTrainingCaptureOptions | undefined) => void;
49
+ /**
50
+ * Persists a deliberately private, unredacted training artifact.
51
+ *
52
+ * `pi-session.jsonl` is copied byte-for-byte from Pi's SessionManager. The
53
+ * request payloads come from Pi AI's `onPayload` seam after any prior payload
54
+ * transform, so they contain the exact structured provider input, including
55
+ * system/character prompts, messages, tool schemas, and sampling fields.
56
+ */
57
+ export declare const persistPiRawTrainingCapture: (input: PersistPiRawTrainingCaptureInput) => Promise<string>;
58
+ export {};
59
+ //# sourceMappingURL=rawTrainingCapture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rawTrainingCapture.d.ts","sourceRoot":"","sources":["../../src/pi/rawTrainingCapture.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAE1E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAG1D,eAAO,MAAM,8BAA8B,EACzC,mCAA4C,CAAC;AAE/C,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,IAAI,CAAC;IACd,SAAS,EAAE;QACT,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC;QACvB,KAAK,EAAE,OAAO,CAAC;QACf,OAAO,EAAE,OAAO,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,CAAC,EAAE,oBAAoB,CAAC;CAChC;AAED,KAAK,eAAe,GAClB,OAAO,CAAC,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AAC5D,KAAK,oBAAoB,GAAG,IAAI,CAC9B,eAAe,EACf,OAAO,GAAG,OAAO,GAAG,aAAa,GAAG,WAAW,GAAG,eAAe,CAClE,CAAC;AAEF,MAAM,WAAW,gCAAgC;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,oBAAoB,CAAC;IAC9B,WAAW,EAAE,IAAI,CAAC;IAClB,OAAO,EAAE,2BAA2B,CAAC;IACrC,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,oBAAoB,CAAC;IAC9B,SAAS,EAAE,IAAI,CAAC;IAChB,MAAM,EAAE,WAAW,GAAG,QAAQ,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,kBAAkB,CAAC;CAC5B;AAMD;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,GACnC,SAAS,oBAAoB,EAC7B,KAAK,uBAAuB,KAC3B,IA4BF,CAAC;AAEF,eAAO,MAAM,0BAA0B,QAAO,oBAG5C,CAAC;AAEH,eAAO,MAAM,yBAAyB,GACpC,SAAS,oBAAoB,EAC7B,OAAO,OAAO,EACd,UAAgB,KACf,IAKF,CAAC;AAEF,eAAO,MAAM,mCAAmC,GAC9C,SAAS,2BAA2B,GAAG,SAAS,KAC/C,IAUF,CAAC;AAaF;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,GACtC,OAAO,gCAAgC,KACtC,OAAO,CAAC,MAAM,CAoIhB,CAAC"}
@@ -0,0 +1,175 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { chmod, readFile, readdir, rename, rm, mkdir, writeFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { sanitizeTraceFileId } from "./turnTrace.js";
5
+ export const PI_RAW_TRAINING_CAPTURE_SCHEMA = "daimon.pi.raw_training_capture.v2";
6
+ const json = (value) => JSON.stringify(value);
7
+ const sha256 = (value) => createHash("sha256").update(value).digest("hex");
8
+ /**
9
+ * Installs a transparent recorder at Pi's native provider-payload seam.
10
+ *
11
+ * The recorder returns the exact result of any pre-existing hook, so enabling
12
+ * capture cannot alter the request sent to the model.
13
+ */
14
+ export const bindPiRawTrainingCapture = (session, ref) => {
15
+ const previousPayload = session.agent.onPayload;
16
+ const previousResponse = session.agent.onResponse;
17
+ session.agent.onPayload = async (payload, model) => {
18
+ const transformed = await previousPayload?.(payload, model);
19
+ const effectivePayload = transformed === undefined ? payload : transformed;
20
+ const capture = ref.current;
21
+ if (capture !== undefined) {
22
+ capture.requests.push({
23
+ model: structuredClone(model),
24
+ payload: structuredClone(effectivePayload),
25
+ requested_at: new Date().toISOString(),
26
+ sequence: capture.requests.length
27
+ });
28
+ }
29
+ return transformed;
30
+ };
31
+ session.agent.onResponse = async (response, model) => {
32
+ await previousResponse?.(response, model);
33
+ const capture = ref.current;
34
+ const request = capture?.requests.at(-1);
35
+ if (request !== undefined) {
36
+ request.response = structuredClone(response);
37
+ request.response_at = new Date().toISOString();
38
+ }
39
+ };
40
+ };
41
+ export const createPiRawTrainingCapture = () => ({
42
+ events: [],
43
+ requests: []
44
+ });
45
+ export const capturePiRawTrainingEvent = (capture, event, now = new Date()) => {
46
+ // Serialize at event time so later mutation cannot change the captured event.
47
+ capture.events.push(`{"recorded_at":${json(now.toISOString())},"event":${json(event)}}`);
48
+ };
49
+ export const validatePiRawTrainingCaptureOptions = (options) => {
50
+ if (options === undefined)
51
+ return;
52
+ if (options.enabled !== true
53
+ || !Number.isSafeInteger(options.retention.maxTurns)
54
+ || options.retention.maxTurns < 1
55
+ || options.retention.maxTurns > 100_000) {
56
+ throw new Error("Pi raw training capture requires enabled: true and retention.maxTurns between 1 and 100000");
57
+ }
58
+ };
59
+ const pruneTurns = async (turnsPath, maxTurns) => {
60
+ const names = (await readdir(turnsPath, { withFileTypes: true }))
61
+ .filter((entry) => entry.isDirectory())
62
+ .map((entry) => entry.name)
63
+ .sort();
64
+ await Promise.all(names.slice(0, Math.max(0, names.length - maxTurns))
65
+ .map((name) => rm(path.join(turnsPath, name), { force: true, recursive: true })));
66
+ };
67
+ /**
68
+ * Persists a deliberately private, unredacted training artifact.
69
+ *
70
+ * `pi-session.jsonl` is copied byte-for-byte from Pi's SessionManager. The
71
+ * request payloads come from Pi AI's `onPayload` seam after any prior payload
72
+ * transform, so they contain the exact structured provider input, including
73
+ * system/character prompts, messages, tool schemas, and sampling fields.
74
+ */
75
+ export const persistPiRawTrainingCapture = async (input) => {
76
+ validatePiRawTrainingCaptureOptions(input.options);
77
+ if (input.session.sessionFile === undefined) {
78
+ throw new Error("Pi raw training capture requires a persisted native session");
79
+ }
80
+ const nativeSessionBytes = await readFile(input.session.sessionFile);
81
+ const privateTrainingPath = path.join(input.runtimeHomePath, "private-training");
82
+ const piPath = path.join(privateTrainingPath, "pi");
83
+ const root = path.join(piPath, "raw");
84
+ const turnsPath = path.join(root, "turns");
85
+ const turnPath = path.join(turnsPath, `${String(input.startedAt.getTime()).padStart(13, "0")}-${sanitizeTraceFileId(input.turnId)}`);
86
+ const partialTurnPath = path.join(turnsPath, `.partial-${path.basename(turnPath)}-${randomUUID()}`);
87
+ await mkdir(turnsPath, { mode: 0o700, recursive: true });
88
+ await Promise.all([privateTrainingPath, piPath, root, turnsPath]
89
+ .map((directory) => chmod(directory, 0o700)));
90
+ await mkdir(partialTurnPath, { mode: 0o700 });
91
+ const providerExchange = {
92
+ model: structuredClone(input.session.model),
93
+ requests: input.capture.requests,
94
+ session_id: input.session.sessionId,
95
+ thinking_level: input.session.thinkingLevel
96
+ };
97
+ const eventsBytes = Buffer.from(input.capture.events.length === 0 ? "" : `${input.capture.events.join("\n")}\n`, "utf8");
98
+ const providerExchangeBytes = Buffer.from(`${JSON.stringify(providerExchange, null, 2)}\n`, "utf8");
99
+ const manifest = {
100
+ access: {
101
+ classification: "private_raw_training",
102
+ export_by_default: false,
103
+ contains_unredacted_model_context: true
104
+ },
105
+ agent_id: input.agentId,
106
+ completed_at: input.completedAt.toISOString(),
107
+ files: {
108
+ events: "events.ndjson",
109
+ native_pi_session: "pi-session.jsonl",
110
+ provider_exchange: "provider-exchange.json"
111
+ },
112
+ integrity: {
113
+ capture_boundary: "post_turn",
114
+ files: {
115
+ events: {
116
+ bytes: eventsBytes.byteLength,
117
+ records: input.capture.events.length,
118
+ sha256: sha256(eventsBytes)
119
+ },
120
+ native_pi_session: {
121
+ bytes: nativeSessionBytes.byteLength,
122
+ sha256: sha256(nativeSessionBytes)
123
+ },
124
+ provider_exchange: {
125
+ bytes: providerExchangeBytes.byteLength,
126
+ requests: input.capture.requests.length,
127
+ sha256: sha256(providerExchangeBytes)
128
+ }
129
+ }
130
+ },
131
+ join: input.world === undefined ? undefined : {
132
+ run_id: input.world.runId,
133
+ tick: input.world.tick,
134
+ wake_id: input.world.wakeId
135
+ },
136
+ native_session: {
137
+ id: input.session.sessionId
138
+ },
139
+ retention: {
140
+ max_turns: input.options.retention.maxTurns
141
+ },
142
+ schema: PI_RAW_TRAINING_CAPTURE_SCHEMA,
143
+ started_at: input.startedAt.toISOString(),
144
+ terminal_status: input.status,
145
+ timings_ms: {
146
+ total: input.totalMs
147
+ },
148
+ turn_id: input.turnId
149
+ };
150
+ const files = [
151
+ "manifest.json",
152
+ "provider-exchange.json",
153
+ "events.ndjson",
154
+ "pi-session.jsonl"
155
+ ];
156
+ try {
157
+ await Promise.all([
158
+ writeFile(path.join(partialTurnPath, "manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`, { encoding: "utf8", mode: 0o600 }),
159
+ writeFile(path.join(partialTurnPath, "provider-exchange.json"), providerExchangeBytes, { mode: 0o600 }),
160
+ writeFile(path.join(partialTurnPath, "events.ndjson"), eventsBytes, { mode: 0o600 }),
161
+ writeFile(path.join(partialTurnPath, "pi-session.jsonl"), nativeSessionBytes, { mode: 0o600 })
162
+ ]);
163
+ await Promise.all([
164
+ chmod(partialTurnPath, 0o700),
165
+ ...files.map((name) => chmod(path.join(partialTurnPath, name), 0o600))
166
+ ]);
167
+ await rename(partialTurnPath, turnPath);
168
+ }
169
+ catch (error) {
170
+ await rm(partialTurnPath, { force: true, recursive: true });
171
+ throw error;
172
+ }
173
+ await pruneTurns(turnsPath, input.options.retention.maxTurns);
174
+ return turnPath;
175
+ };
@@ -0,0 +1,73 @@
1
+ import type { MemoryPrepareTurnResult } from "@noopolis/mneme";
2
+ import type { WakeEvent } from "../core/types.js";
3
+ import { type CausalEvent, type TurnInputSubmittedPayload, type TurnOutputCompletedPayload } from "../observability/causalEvents.js";
4
+ /**
5
+ * Principal grammar per `specs/CAUSAL.md` §3
6
+ * (`^(agent|operator|system):.+`): the authenticated agent identity this
7
+ * harness instance was started under, never a bare id and never model
8
+ * output. See `stampTurnInputSubmitted`/`stampTurnOutputCompleted` below for
9
+ * why `agentId` itself is trustworthy at this layer.
10
+ */
11
+ export declare const agentPrincipalId: (agentId: string) => string;
12
+ export interface StampTurnInputSubmittedInput {
13
+ agentId: string;
14
+ event: WakeEvent;
15
+ prepared?: MemoryPrepareTurnResult;
16
+ promptText: string;
17
+ runtimeHomePath: string;
18
+ }
19
+ /**
20
+ * Stamps `turn.input.submitted` for one Pi turn, wiring piHarness's own
21
+ * variables into `@noopolis/daimon`'s causal envelope (`../observability/
22
+ * causalEvents.ts`).
23
+ *
24
+ * - `principal_id` is `agent:<agentId>` (`agentPrincipalId`, per the
25
+ * `specs/CAUSAL.md` §3 principal grammar). `src/pi/auth.ts` scopes LLM
26
+ * provider auth (Codex / Claude / API key) per harness instance rather
27
+ * than exposing a separate network identity token, so `agentId` — the
28
+ * identity this harness instance was started under (`AgentStartInput.id`,
29
+ * which also scopes its own `authPath`/`runtimeHomePath`) — is the
30
+ * truthful authenticated identity available at this layer.
31
+ * - `cause_event_ids` chains to `event.id` (the WakeEvent id) plus any mneme
32
+ * recall ids from `prepared.recalledCausalEventIds`. `event.id` is no
33
+ * longer a same-process stand-in for the upstream moltnet
34
+ * `message.accepted` id: moltnet's bridge control POST now carries a real
35
+ * `event_id` (`protocol.MessageEventID`-shaped, `"moltnet:<messageID>"`)
36
+ * for every non-bootstrap wake, and the Pi control source
37
+ * (`src/runtime/pi/appControlSource.ts` `formatControlEventId`) threads
38
+ * that value verbatim into `WakeEvent.id` in preference to its own
39
+ * `context_id`+timestamp fallback. `WakeEvent` still does not carry a
40
+ * separately namespaced moltnet field — Daimon stays detached from
41
+ * Moltnet wiring (see repo `AGENTS.md`) — but `event.id` is now the same
42
+ * id moltnet itself stamped on `message.accepted`, so this chain is
43
+ * id-joined across authorities rather than merely locally consistent.
44
+ * `prepared.recalledCausalEventIds` (not `prepared.recall.selectedEventIds`)
45
+ * for the same reason: `recall.selectedEventIds` are mneme's raw
46
+ * kernel-log recall ids (`evt_<...>`), a different id namespace than the
47
+ * `mneme:<uuid>` ids mneme's own `memory.recalled` causal events are
48
+ * stamped under (`contract/causal.ts` `mnemeCausalEventId`). Chaining the
49
+ * raw recall id would never resolve against mneme's causal stream; the
50
+ * `recalledCausalEventIds` mneme exposes on `MemoryPrepareTurnResult` are
51
+ * the actual `event_id`s of the `memory.recalled` events it appended for
52
+ * this turn, so this cause link is id-joined the same way the moltnet
53
+ * link above is.
54
+ * - `run_id` always comes from `resolveRunId()` (`NOOPOLIS_RUN_ID`), never
55
+ * from `event` or model output.
56
+ */
57
+ export declare const stampTurnInputSubmitted: (input: StampTurnInputSubmittedInput) => Promise<CausalEvent<TurnInputSubmittedPayload>>;
58
+ export interface StampTurnOutputCompletedInput {
59
+ agentId: string;
60
+ causeEventId: string;
61
+ outputText: string;
62
+ runtimeHomePath: string;
63
+ turnId: string;
64
+ }
65
+ /**
66
+ * Stamps `turn.output.completed` once a Pi turn finishes successfully,
67
+ * chained back via `cause_event_ids` to the `turn.input.submitted` id for
68
+ * the same turn. `output_sha256` is over the harness-extracted reply text;
69
+ * the model has no path to influence `cause_event_ids`, `run_id`, or
70
+ * `principal_id` here.
71
+ */
72
+ export declare const stampTurnOutputCompleted: (input: StampTurnOutputCompletedInput) => Promise<CausalEvent<TurnOutputCompletedPayload>>;
73
+ //# sourceMappingURL=turnCausal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turnCausal.d.ts","sourceRoot":"","sources":["../../src/pi/turnCausal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE/D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAKL,KAAK,WAAW,EAChB,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAChC,MAAM,kCAAkC,CAAC;AAG1C;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,GAAI,SAAS,MAAM,KAAG,MAA4B,CAAC;AAEhF,MAAM,WAAW,4BAA4B;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,SAAS,CAAC;IACjB,QAAQ,CAAC,EAAE,uBAAuB,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,eAAO,MAAM,uBAAuB,GAClC,OAAO,4BAA4B,KAClC,OAAO,CAAC,WAAW,CAAC,yBAAyB,CAAC,CAW7C,CAAC;AAEL,MAAM,WAAW,6BAA6B;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,GACnC,OAAO,6BAA6B,KACnC,OAAO,CAAC,WAAW,CAAC,0BAA0B,CAAC,CAS9C,CAAC"}