@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.
- package/README.md +141 -5
- 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 +20 -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.d.ts +2 -0
- package/dist/examples/jungian-play-org.d.ts.map +1 -0
- package/dist/examples/jungian-play-org.js +281 -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 +281 -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/pi-agent.js +3 -2
- 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 +231 -0
- package/dist/index.d.ts +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- 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 +4 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +3 -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/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 +9 -0
- package/dist/pi/index.d.ts.map +1 -0
- package/dist/pi/index.js +8 -0
- package/dist/pi/memoryTools.d.ts +69 -0
- package/dist/pi/memoryTools.d.ts.map +1 -0
- package/dist/pi/memoryTools.js +190 -0
- 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 +36 -2
- package/dist/pi/piHarness.d.ts.map +1 -1
- package/dist/pi/piHarness.js +100 -127
- 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 +23 -4
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { type CausalEvent } from "./causalEvents.js";
|
|
2
|
+
/**
|
|
3
|
+
* Control-wake causal emitters for root's two wake-acceptance surfaces
|
|
4
|
+
* (`src/runtime/pi/appControlSource.ts`, root repo, out of scope here):
|
|
5
|
+
* the operator-only control endpoint and the Moltnet loopback delivery
|
|
6
|
+
* endpoint (`/agents/:slug/wake`). Both stamp `control.wake.accepted` —
|
|
7
|
+
* enforcement point #3 in `specs/CAUSAL.md` §"Enforcement points" — but with
|
|
8
|
+
* different `principal_id` authorities, per the B62 fix (Option B): the
|
|
9
|
+
* operator endpoint stamps `operator:<name>` (`emitControlWakeAccepted`),
|
|
10
|
+
* the delivery endpoint stamps the fixed `system:moltnet` authority
|
|
11
|
+
* (`emitDeliveryWakeAccepted`), never a caller-supplied `from`/agent field.
|
|
12
|
+
* `control.wake.denied` (`emitControlWakeDenied`) stays operator-only: the
|
|
13
|
+
* delivery endpoint has no bearer-token deny path to stamp. Root's generated
|
|
14
|
+
* app calls these directly; this file has no knowledge of HTTP, tokens, or
|
|
15
|
+
* the request shape, only the causal envelope contract.
|
|
16
|
+
*/
|
|
17
|
+
export declare const CONTROL_WAKE_ACCEPTED_TYPE: "control.wake.accepted";
|
|
18
|
+
export declare const CONTROL_WAKE_DENIED_TYPE: "control.wake.denied";
|
|
19
|
+
/** Payload for `control.wake.accepted`. */
|
|
20
|
+
export interface ControlWakeAcceptedPayload extends Record<string, unknown> {
|
|
21
|
+
target_agent_id: string;
|
|
22
|
+
wake_kind: string;
|
|
23
|
+
}
|
|
24
|
+
/** Payload for `control.wake.denied`. */
|
|
25
|
+
export interface ControlWakeDeniedPayload extends Record<string, unknown> {
|
|
26
|
+
reason: string;
|
|
27
|
+
target_agent_id: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Payload for a Moltnet-delivered `control.wake.accepted` (same event type
|
|
31
|
+
* as the operator path — it is still an accepted wake; `principal_id` is
|
|
32
|
+
* what distinguishes authority). Adds `delivered_by` so downstream readers
|
|
33
|
+
* can tell a delivery-stamped event from an operator-stamped one without
|
|
34
|
+
* inspecting `principal_id`.
|
|
35
|
+
*/
|
|
36
|
+
export interface DeliveryWakeAcceptedPayload extends ControlWakeAcceptedPayload {
|
|
37
|
+
delivered_by: "moltnet";
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Principal grammar per `specs/CAUSAL.md` §3 (`^(agent|operator|system):.+`):
|
|
41
|
+
* an authenticated operator identity, e.g. `operator:control`. Callers must
|
|
42
|
+
* pass the identity behind the verified bearer token, never a value read
|
|
43
|
+
* from the request body or model output.
|
|
44
|
+
*/
|
|
45
|
+
export declare const operatorPrincipalId: (operatorName: string) => string;
|
|
46
|
+
/**
|
|
47
|
+
* Fixed principal for wakes accepted through the Moltnet loopback delivery
|
|
48
|
+
* endpoint (`/agents/:slug/wake`): the delivering authority itself, never an
|
|
49
|
+
* identity derived from a caller-supplied `from`/agent field on the request.
|
|
50
|
+
*/
|
|
51
|
+
export declare const DELIVERY_PRINCIPAL_ID: "system:moltnet";
|
|
52
|
+
/** Deterministic, non-model-derived event id for a control wake acceptance. */
|
|
53
|
+
export declare const controlWakeAcceptedEventId: (requestId: string) => string;
|
|
54
|
+
/** Deterministic, non-model-derived event id for a control wake denial. */
|
|
55
|
+
export declare const controlWakeDeniedEventId: (requestId: string) => string;
|
|
56
|
+
/**
|
|
57
|
+
* Deterministic, non-model-derived event id for a Moltnet-delivered wake
|
|
58
|
+
* acceptance. Distinct from `controlWakeAcceptedEventId` (own `.delivery.`
|
|
59
|
+
* segment) so the two paths never collide even if a future caller reused a
|
|
60
|
+
* `requestId` across both endpoints for the same target.
|
|
61
|
+
*/
|
|
62
|
+
export declare const deliveryWakeAcceptedEventId: (requestId: string) => string;
|
|
63
|
+
export interface EmitControlWakeAcceptedInput {
|
|
64
|
+
causeEventIds?: string[];
|
|
65
|
+
operatorName: string;
|
|
66
|
+
requestId: string;
|
|
67
|
+
runId?: string;
|
|
68
|
+
runtimeHomePath: string;
|
|
69
|
+
targetAgentId: string;
|
|
70
|
+
wakeKind: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Stamps `control.wake.accepted` once root's operator-control endpoint has
|
|
74
|
+
* verified the bearer token and is about to wake `targetAgentId`.
|
|
75
|
+
* `principal_id` is always `operator:<operatorName>` (the authenticated
|
|
76
|
+
* operator behind the verified token), never derived from the request body.
|
|
77
|
+
* `run_id` defaults to `resolveRunId()` like every other daimon emitter.
|
|
78
|
+
*/
|
|
79
|
+
export declare const emitControlWakeAccepted: (input: EmitControlWakeAcceptedInput) => Promise<CausalEvent<ControlWakeAcceptedPayload>>;
|
|
80
|
+
export interface EmitDeliveryWakeAcceptedInput {
|
|
81
|
+
causeEventIds?: string[];
|
|
82
|
+
requestId: string;
|
|
83
|
+
runId?: string;
|
|
84
|
+
runtimeHomePath: string;
|
|
85
|
+
targetAgentId: string;
|
|
86
|
+
wakeKind: string;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Stamps `control.wake.accepted` once root's Moltnet loopback delivery
|
|
90
|
+
* endpoint (`/agents/:slug/wake`) is about to wake `targetAgentId` on behalf
|
|
91
|
+
* of an inter-agent message. `principal_id` is always the fixed
|
|
92
|
+
* `DELIVERY_PRINCIPAL_ID` (`system:moltnet`) — the delivering authority —
|
|
93
|
+
* never derived from a caller-supplied `from`/agent field, and never
|
|
94
|
+
* `operator:<name>` (that principal is reserved for the operator-only
|
|
95
|
+
* control endpoint; see `emitControlWakeAccepted`). `run_id` defaults to
|
|
96
|
+
* `resolveRunId()` like every other daimon emitter.
|
|
97
|
+
*/
|
|
98
|
+
export declare const emitDeliveryWakeAccepted: (input: EmitDeliveryWakeAcceptedInput) => Promise<CausalEvent<DeliveryWakeAcceptedPayload>>;
|
|
99
|
+
export interface EmitControlWakeDeniedInput {
|
|
100
|
+
causeEventIds?: string[];
|
|
101
|
+
operatorName: string;
|
|
102
|
+
reason: string;
|
|
103
|
+
requestId: string;
|
|
104
|
+
runId?: string;
|
|
105
|
+
runtimeHomePath: string;
|
|
106
|
+
targetAgentId: string;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Stamps `control.wake.denied` when root's operator-control endpoint rejects
|
|
110
|
+
* a wake request over a missing or invalid bearer token. Deny paths must
|
|
111
|
+
* never drop silently (see `specs/CAUSAL.md` enforcement point #3 / T5):
|
|
112
|
+
* every 401 the endpoint returns should carry exactly one of these. As with
|
|
113
|
+
* `emitControlWakeAccepted`, `principal_id` is always the authenticated
|
|
114
|
+
* operator identity, never a value read from the request.
|
|
115
|
+
*/
|
|
116
|
+
export declare const emitControlWakeDenied: (input: EmitControlWakeDeniedInput) => Promise<CausalEvent<ControlWakeDeniedPayload>>;
|
|
117
|
+
//# sourceMappingURL=controlCausal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controlCausal.d.ts","sourceRoot":"","sources":["../../src/observability/controlCausal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEpF;;;;;;;;;;;;;;GAcG;AAEH,eAAO,MAAM,0BAA0B,EAAG,uBAAgC,CAAC;AAC3E,eAAO,MAAM,wBAAwB,EAAG,qBAA8B,CAAC;AAEvE,2CAA2C;AAC3C,MAAM,WAAW,0BAA2B,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACzE,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,yCAAyC;AACzC,MAAM,WAAW,wBAAyB,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACvE,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,2BAA4B,SAAQ,0BAA0B;IAC7E,YAAY,EAAE,SAAS,CAAC;CACzB;AAED;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAAI,cAAc,MAAM,KAAG,MAAoC,CAAC;AAEhG;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,EAAG,gBAAyB,CAAC;AAE/D,+EAA+E;AAC/E,eAAO,MAAM,0BAA0B,GAAI,WAAW,MAAM,KAAG,MAClB,CAAC;AAE9C,2EAA2E;AAC3E,eAAO,MAAM,wBAAwB,GAAI,WAAW,MAAM,KAAG,MAAmD,CAAC;AAEjH;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,GAAI,WAAW,MAAM,KAAG,MAClB,CAAC;AAE/C,MAAM,WAAW,4BAA4B;IAC3C,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,GAClC,OAAO,4BAA4B,KAClC,OAAO,CAAC,WAAW,CAAC,0BAA0B,CAAC,CAa9C,CAAC;AAEL,MAAM,WAAW,6BAA6B;IAC5C,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,wBAAwB,GACnC,OAAO,6BAA6B,KACnC,OAAO,CAAC,WAAW,CAAC,2BAA2B,CAAC,CAc/C,CAAC;AAEL,MAAM,WAAW,0BAA0B;IACzC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,GAChC,OAAO,0BAA0B,KAChC,OAAO,CAAC,WAAW,CAAC,wBAAwB,CAAC,CAa5C,CAAC"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { emitCausalEvent, resolveRunId } from "./causalEvents.js";
|
|
2
|
+
/**
|
|
3
|
+
* Control-wake causal emitters for root's two wake-acceptance surfaces
|
|
4
|
+
* (`src/runtime/pi/appControlSource.ts`, root repo, out of scope here):
|
|
5
|
+
* the operator-only control endpoint and the Moltnet loopback delivery
|
|
6
|
+
* endpoint (`/agents/:slug/wake`). Both stamp `control.wake.accepted` —
|
|
7
|
+
* enforcement point #3 in `specs/CAUSAL.md` §"Enforcement points" — but with
|
|
8
|
+
* different `principal_id` authorities, per the B62 fix (Option B): the
|
|
9
|
+
* operator endpoint stamps `operator:<name>` (`emitControlWakeAccepted`),
|
|
10
|
+
* the delivery endpoint stamps the fixed `system:moltnet` authority
|
|
11
|
+
* (`emitDeliveryWakeAccepted`), never a caller-supplied `from`/agent field.
|
|
12
|
+
* `control.wake.denied` (`emitControlWakeDenied`) stays operator-only: the
|
|
13
|
+
* delivery endpoint has no bearer-token deny path to stamp. Root's generated
|
|
14
|
+
* app calls these directly; this file has no knowledge of HTTP, tokens, or
|
|
15
|
+
* the request shape, only the causal envelope contract.
|
|
16
|
+
*/
|
|
17
|
+
export const CONTROL_WAKE_ACCEPTED_TYPE = "control.wake.accepted";
|
|
18
|
+
export const CONTROL_WAKE_DENIED_TYPE = "control.wake.denied";
|
|
19
|
+
/**
|
|
20
|
+
* Principal grammar per `specs/CAUSAL.md` §3 (`^(agent|operator|system):.+`):
|
|
21
|
+
* an authenticated operator identity, e.g. `operator:control`. Callers must
|
|
22
|
+
* pass the identity behind the verified bearer token, never a value read
|
|
23
|
+
* from the request body or model output.
|
|
24
|
+
*/
|
|
25
|
+
export const operatorPrincipalId = (operatorName) => `operator:${operatorName}`;
|
|
26
|
+
/**
|
|
27
|
+
* Fixed principal for wakes accepted through the Moltnet loopback delivery
|
|
28
|
+
* endpoint (`/agents/:slug/wake`): the delivering authority itself, never an
|
|
29
|
+
* identity derived from a caller-supplied `from`/agent field on the request.
|
|
30
|
+
*/
|
|
31
|
+
export const DELIVERY_PRINCIPAL_ID = "system:moltnet";
|
|
32
|
+
/** Deterministic, non-model-derived event id for a control wake acceptance. */
|
|
33
|
+
export const controlWakeAcceptedEventId = (requestId) => `daimon:${requestId}:control.wake.accepted`;
|
|
34
|
+
/** Deterministic, non-model-derived event id for a control wake denial. */
|
|
35
|
+
export const controlWakeDeniedEventId = (requestId) => `daimon:${requestId}:control.wake.denied`;
|
|
36
|
+
/**
|
|
37
|
+
* Deterministic, non-model-derived event id for a Moltnet-delivered wake
|
|
38
|
+
* acceptance. Distinct from `controlWakeAcceptedEventId` (own `.delivery.`
|
|
39
|
+
* segment) so the two paths never collide even if a future caller reused a
|
|
40
|
+
* `requestId` across both endpoints for the same target.
|
|
41
|
+
*/
|
|
42
|
+
export const deliveryWakeAcceptedEventId = (requestId) => `daimon:${requestId}:delivery.wake.accepted`;
|
|
43
|
+
/**
|
|
44
|
+
* Stamps `control.wake.accepted` once root's operator-control endpoint has
|
|
45
|
+
* verified the bearer token and is about to wake `targetAgentId`.
|
|
46
|
+
* `principal_id` is always `operator:<operatorName>` (the authenticated
|
|
47
|
+
* operator behind the verified token), never derived from the request body.
|
|
48
|
+
* `run_id` defaults to `resolveRunId()` like every other daimon emitter.
|
|
49
|
+
*/
|
|
50
|
+
export const emitControlWakeAccepted = (input) => emitCausalEvent({
|
|
51
|
+
agentId: input.targetAgentId,
|
|
52
|
+
causeEventIds: input.causeEventIds ?? [],
|
|
53
|
+
eventId: controlWakeAcceptedEventId(input.requestId),
|
|
54
|
+
payload: {
|
|
55
|
+
target_agent_id: input.targetAgentId,
|
|
56
|
+
wake_kind: input.wakeKind
|
|
57
|
+
},
|
|
58
|
+
principalId: operatorPrincipalId(input.operatorName),
|
|
59
|
+
runId: input.runId ?? resolveRunId(),
|
|
60
|
+
runtimeHomePath: input.runtimeHomePath,
|
|
61
|
+
type: CONTROL_WAKE_ACCEPTED_TYPE
|
|
62
|
+
});
|
|
63
|
+
/**
|
|
64
|
+
* Stamps `control.wake.accepted` once root's Moltnet loopback delivery
|
|
65
|
+
* endpoint (`/agents/:slug/wake`) is about to wake `targetAgentId` on behalf
|
|
66
|
+
* of an inter-agent message. `principal_id` is always the fixed
|
|
67
|
+
* `DELIVERY_PRINCIPAL_ID` (`system:moltnet`) — the delivering authority —
|
|
68
|
+
* never derived from a caller-supplied `from`/agent field, and never
|
|
69
|
+
* `operator:<name>` (that principal is reserved for the operator-only
|
|
70
|
+
* control endpoint; see `emitControlWakeAccepted`). `run_id` defaults to
|
|
71
|
+
* `resolveRunId()` like every other daimon emitter.
|
|
72
|
+
*/
|
|
73
|
+
export const emitDeliveryWakeAccepted = (input) => emitCausalEvent({
|
|
74
|
+
agentId: input.targetAgentId,
|
|
75
|
+
causeEventIds: input.causeEventIds ?? [],
|
|
76
|
+
eventId: deliveryWakeAcceptedEventId(input.requestId),
|
|
77
|
+
payload: {
|
|
78
|
+
delivered_by: "moltnet",
|
|
79
|
+
target_agent_id: input.targetAgentId,
|
|
80
|
+
wake_kind: input.wakeKind
|
|
81
|
+
},
|
|
82
|
+
principalId: DELIVERY_PRINCIPAL_ID,
|
|
83
|
+
runId: input.runId ?? resolveRunId(),
|
|
84
|
+
runtimeHomePath: input.runtimeHomePath,
|
|
85
|
+
type: CONTROL_WAKE_ACCEPTED_TYPE
|
|
86
|
+
});
|
|
87
|
+
/**
|
|
88
|
+
* Stamps `control.wake.denied` when root's operator-control endpoint rejects
|
|
89
|
+
* a wake request over a missing or invalid bearer token. Deny paths must
|
|
90
|
+
* never drop silently (see `specs/CAUSAL.md` enforcement point #3 / T5):
|
|
91
|
+
* every 401 the endpoint returns should carry exactly one of these. As with
|
|
92
|
+
* `emitControlWakeAccepted`, `principal_id` is always the authenticated
|
|
93
|
+
* operator identity, never a value read from the request.
|
|
94
|
+
*/
|
|
95
|
+
export const emitControlWakeDenied = (input) => emitCausalEvent({
|
|
96
|
+
agentId: input.targetAgentId,
|
|
97
|
+
causeEventIds: input.causeEventIds ?? [],
|
|
98
|
+
eventId: controlWakeDeniedEventId(input.requestId),
|
|
99
|
+
payload: {
|
|
100
|
+
reason: input.reason,
|
|
101
|
+
target_agent_id: input.targetAgentId
|
|
102
|
+
},
|
|
103
|
+
principalId: operatorPrincipalId(input.operatorName),
|
|
104
|
+
runId: input.runId ?? resolveRunId(),
|
|
105
|
+
runtimeHomePath: input.runtimeHomePath,
|
|
106
|
+
type: CONTROL_WAKE_DENIED_TYPE
|
|
107
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type CausalEvent } from "./causalEvents.js";
|
|
2
|
+
export interface CausalFixtureResult {
|
|
3
|
+
events: [CausalEvent, CausalEvent];
|
|
4
|
+
jsonlPath: string;
|
|
5
|
+
}
|
|
6
|
+
export interface RunCausalFixtureOptions {
|
|
7
|
+
/** Overrides the scratch runtime home; defaults to a fixed `.runtime/` path keyed on `spoof`. */
|
|
8
|
+
runtimeHomePath?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Adversarial mode: the fixture's "wake text" (standing in for untrusted
|
|
11
|
+
* model/request input) embeds a forged identity claim, but the emitted
|
|
12
|
+
* `principal_id` on both events must remain the authenticated
|
|
13
|
+
* `agent:<agentId>` regardless — never the claimed identity. Mirrors the
|
|
14
|
+
* spoof invariant already covered live in `piHarnessCausal.test.ts`
|
|
15
|
+
* ("model output cannot set principal_id...").
|
|
16
|
+
*/
|
|
17
|
+
spoof?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Runs the fixture and returns the two stamped events plus the jsonl path,
|
|
21
|
+
* without touching `process.stdout`/`process.exitCode` — the CLI entry
|
|
22
|
+
* point below wraps this for `npm run emit-causal-fixture`.
|
|
23
|
+
*/
|
|
24
|
+
export declare const runCausalFixture: (options?: RunCausalFixtureOptions) => Promise<CausalFixtureResult>;
|
|
25
|
+
//# sourceMappingURL=emitCausalFixture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emitCausalFixture.d.ts","sourceRoot":"","sources":["../../src/observability/emitCausalFixture.ts"],"names":[],"mappings":"AAIA,OAAO,EAA4E,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AA0B/H,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IACnC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,uBAAuB;IACtC,iGAAiG;IACjG,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAID;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAU,UAAS,uBAA4B,KAAG,OAAO,CAAC,mBAAmB,CA0DzG,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { mkdir, rm } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { emitTurnInputSubmitted, emitTurnOutputCompleted, resolveRunId, sha256Hex } from "./causalEvents.js";
|
|
5
|
+
/**
|
|
6
|
+
* Standalone fixture emitter, run via `npm run emit-causal-fixture`
|
|
7
|
+
* (or `npm run emit-causal-fixture:spoof` for the adversarial mode below).
|
|
8
|
+
*
|
|
9
|
+
* Stamps one synthetic `turn.input.submitted` -> `turn.output.completed`
|
|
10
|
+
* chain into a scratch runtime home under `.runtime/causal-fixture[-spoof]/`,
|
|
11
|
+
* using the same `causalEvents.ts` functions `turnCausal.ts` uses for real
|
|
12
|
+
* turns. This is a fixture, not a live engine run: no Pi session, no mneme
|
|
13
|
+
* recall.
|
|
14
|
+
*
|
|
15
|
+
* `principal_id` here is stamped as `agent:<agentId>` directly (rather than
|
|
16
|
+
* importing `turnCausal.ts`'s `agentPrincipalId` helper) because this file
|
|
17
|
+
* lives under `src/observability/`, which stays engine-neutral and must not
|
|
18
|
+
* import `src/pi/` implementation details (see this folder's `AGENTS.md`).
|
|
19
|
+
* The value is the same grammar (`specs/CAUSAL.md` §3) either way.
|
|
20
|
+
*
|
|
21
|
+
* Exact invocation contract for a future cross-repo conformance harness
|
|
22
|
+
* (B92/B62, out of scope here) is not defined yet, so this script picks the
|
|
23
|
+
* simplest reasonable convention: write the fixture under a fixed relative
|
|
24
|
+
* path and print its absolute location as the last line of stdout.
|
|
25
|
+
*/
|
|
26
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
27
|
+
const daimonRoot = path.resolve(__dirname, "../..");
|
|
28
|
+
const SPOOFED_AGENT_ID = "attacker-agent";
|
|
29
|
+
/**
|
|
30
|
+
* Runs the fixture and returns the two stamped events plus the jsonl path,
|
|
31
|
+
* without touching `process.stdout`/`process.exitCode` — the CLI entry
|
|
32
|
+
* point below wraps this for `npm run emit-causal-fixture`.
|
|
33
|
+
*/
|
|
34
|
+
export const runCausalFixture = async (options = {}) => {
|
|
35
|
+
const spoof = options.spoof ?? false;
|
|
36
|
+
const runtimeHomePath = options.runtimeHomePath ?? path.join(daimonRoot, ".runtime", spoof ? "causal-fixture-spoof" : "causal-fixture");
|
|
37
|
+
await rm(runtimeHomePath, { recursive: true, force: true });
|
|
38
|
+
await mkdir(runtimeHomePath, { recursive: true });
|
|
39
|
+
const agentId = "fixture-agent";
|
|
40
|
+
const principalId = `agent:${agentId}`;
|
|
41
|
+
const turnId = "fixture-turn-1";
|
|
42
|
+
const runId = resolveRunId();
|
|
43
|
+
const inputText = spoof
|
|
44
|
+
? `Fixture wake text for the causal conformance harness. ` +
|
|
45
|
+
`SPOOF CLAIM (must be ignored): ${JSON.stringify({ from: SPOOFED_AGENT_ID, principal_id: `agent:${SPOOFED_AGENT_ID}` })}`
|
|
46
|
+
: "Fixture wake text for the causal conformance harness.";
|
|
47
|
+
const promptText = "Fixture prompt text.";
|
|
48
|
+
const outputText = spoof
|
|
49
|
+
? `Fixture reply text. SPOOF CLAIM (must be ignored): ${JSON.stringify({ principal_id: `agent:${SPOOFED_AGENT_ID}` })}`
|
|
50
|
+
: "Fixture reply text.";
|
|
51
|
+
const inputSubmitted = await emitTurnInputSubmitted({
|
|
52
|
+
agentId,
|
|
53
|
+
causeEventIds: [turnId],
|
|
54
|
+
inputContentSha256: sha256Hex(inputText),
|
|
55
|
+
inputMessageIds: [turnId],
|
|
56
|
+
principalId,
|
|
57
|
+
promptSha256: sha256Hex(promptText),
|
|
58
|
+
runId,
|
|
59
|
+
runtimeHomePath,
|
|
60
|
+
turnId
|
|
61
|
+
});
|
|
62
|
+
const outputCompleted = await emitTurnOutputCompleted({
|
|
63
|
+
agentId,
|
|
64
|
+
causeEventIds: [inputSubmitted.event_id],
|
|
65
|
+
outputSha256: sha256Hex(outputText),
|
|
66
|
+
principalId,
|
|
67
|
+
runId,
|
|
68
|
+
runtimeHomePath,
|
|
69
|
+
turnId
|
|
70
|
+
});
|
|
71
|
+
// The whole point of spoof mode: the forged claim above must never reach
|
|
72
|
+
// the stamped envelope. Fail loudly here rather than let a future refactor
|
|
73
|
+
// silently regress this invariant.
|
|
74
|
+
for (const event of [inputSubmitted, outputCompleted]) {
|
|
75
|
+
if (event.principal_id !== principalId) {
|
|
76
|
+
throw new Error(`causal fixture invariant violated: principal_id was "${event.principal_id}", expected "${principalId}"`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
events: [inputSubmitted, outputCompleted],
|
|
81
|
+
jsonlPath: path.join(runtimeHomePath, "telemetry", "causal.jsonl")
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
const isMainModule = () => {
|
|
85
|
+
const invoked = process.argv[1] ? path.resolve(process.argv[1]) : undefined;
|
|
86
|
+
return invoked !== undefined && invoked === path.resolve(fileURLToPath(import.meta.url));
|
|
87
|
+
};
|
|
88
|
+
if (isMainModule()) {
|
|
89
|
+
const spoof = process.argv.includes("--spoof");
|
|
90
|
+
runCausalFixture({ spoof })
|
|
91
|
+
.then((result) => {
|
|
92
|
+
console.log(result.jsonlPath);
|
|
93
|
+
})
|
|
94
|
+
.catch((error) => {
|
|
95
|
+
console.error(error instanceof Error ? error.stack ?? error.message : String(error));
|
|
96
|
+
process.exitCode = 1;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/observability/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { MemoryRecallAudit } from "@noopolis/mneme";
|
|
2
|
+
export interface WakeBenchRow {
|
|
3
|
+
agent: string;
|
|
4
|
+
engine: string;
|
|
5
|
+
event: string;
|
|
6
|
+
totalMs: number;
|
|
7
|
+
memoryPrepareMs: number;
|
|
8
|
+
engineMs: number;
|
|
9
|
+
memoryRecordMs: number;
|
|
10
|
+
promptChars: number;
|
|
11
|
+
outputChars: number;
|
|
12
|
+
recallCandidates: number;
|
|
13
|
+
recallSelected: number;
|
|
14
|
+
}
|
|
15
|
+
export interface TurnObservation extends WakeBenchRow {
|
|
16
|
+
mentions: string[];
|
|
17
|
+
outputText: string;
|
|
18
|
+
recall: Array<{
|
|
19
|
+
decision: string;
|
|
20
|
+
eventId: string;
|
|
21
|
+
representation: string;
|
|
22
|
+
scope: string;
|
|
23
|
+
}>;
|
|
24
|
+
signalsMentioned: string[];
|
|
25
|
+
signalsInWakeText: string[];
|
|
26
|
+
}
|
|
27
|
+
export interface SignalObservation {
|
|
28
|
+
agent: string;
|
|
29
|
+
engine: string;
|
|
30
|
+
signal: string;
|
|
31
|
+
}
|
|
32
|
+
export interface ConsultationObservation {
|
|
33
|
+
event: string;
|
|
34
|
+
from: string;
|
|
35
|
+
observed: boolean;
|
|
36
|
+
outputText: string;
|
|
37
|
+
to: string;
|
|
38
|
+
}
|
|
39
|
+
export interface BehaviorAssertion {
|
|
40
|
+
detail: string;
|
|
41
|
+
event: string;
|
|
42
|
+
kind: "consultation" | "no-leak" | "recall";
|
|
43
|
+
passed: boolean;
|
|
44
|
+
}
|
|
45
|
+
export interface OrgObserverOptions {
|
|
46
|
+
orgId: string;
|
|
47
|
+
runId: string;
|
|
48
|
+
}
|
|
49
|
+
export declare const mentionsFrom: (text: string) => string[];
|
|
50
|
+
export declare class OrgObserver {
|
|
51
|
+
readonly options: OrgObserverOptions;
|
|
52
|
+
readonly assertions: BehaviorAssertion[];
|
|
53
|
+
readonly consultations: ConsultationObservation[];
|
|
54
|
+
readonly signals: SignalObservation[];
|
|
55
|
+
readonly turns: TurnObservation[];
|
|
56
|
+
constructor(options: OrgObserverOptions);
|
|
57
|
+
knownSignals(): string[];
|
|
58
|
+
recordAssertion(assertion: BehaviorAssertion): void;
|
|
59
|
+
recordSignal(input: SignalObservation): void;
|
|
60
|
+
recordTurn(input: {
|
|
61
|
+
agent: string;
|
|
62
|
+
engine: string;
|
|
63
|
+
engineMs: number;
|
|
64
|
+
event: string;
|
|
65
|
+
eventText: string;
|
|
66
|
+
memoryPrepareMs: number;
|
|
67
|
+
memoryRecordMs: number;
|
|
68
|
+
outputChars: number;
|
|
69
|
+
outputText: string;
|
|
70
|
+
promptChars: number;
|
|
71
|
+
recall: MemoryRecallAudit;
|
|
72
|
+
totalMs: number;
|
|
73
|
+
}): void;
|
|
74
|
+
recordConsultation(input: {
|
|
75
|
+
event: string;
|
|
76
|
+
from: string;
|
|
77
|
+
outputText: string;
|
|
78
|
+
to: string;
|
|
79
|
+
}): ConsultationObservation;
|
|
80
|
+
benchRows(): WakeBenchRow[];
|
|
81
|
+
summary(): Record<string, {
|
|
82
|
+
count: number;
|
|
83
|
+
engineMs: number;
|
|
84
|
+
promptChars: number;
|
|
85
|
+
totalMs: number;
|
|
86
|
+
}>;
|
|
87
|
+
behaviorSummary(): {
|
|
88
|
+
assertionPassRate: number;
|
|
89
|
+
consultationObservedRate: number;
|
|
90
|
+
signalsGenerated: number;
|
|
91
|
+
totalRecallSelected: number;
|
|
92
|
+
totalTurns: number;
|
|
93
|
+
};
|
|
94
|
+
tracePayload(): object;
|
|
95
|
+
write(runtimeRoot: string): Promise<void>;
|
|
96
|
+
toMarkdown(): string;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=orgObserver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orgObserver.d.ts","sourceRoot":"","sources":["../../src/observability/orgObserver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const maxExcerptChars = 280;
|
|
4
|
+
const excerpt = (text) => text.length <= maxExcerptChars ? text : `${text.slice(0, maxExcerptChars).trim()}...`;
|
|
5
|
+
export const mentionsFrom = (text) => [...new Set([...text.matchAll(/@([a-z0-9-]+)/gi)].map((match) => match[1].toLowerCase()))];
|
|
6
|
+
export class OrgObserver {
|
|
7
|
+
options;
|
|
8
|
+
assertions = [];
|
|
9
|
+
consultations = [];
|
|
10
|
+
signals = [];
|
|
11
|
+
turns = [];
|
|
12
|
+
constructor(options) {
|
|
13
|
+
this.options = options;
|
|
14
|
+
}
|
|
15
|
+
knownSignals() {
|
|
16
|
+
return this.signals.map((signal) => signal.signal);
|
|
17
|
+
}
|
|
18
|
+
recordAssertion(assertion) {
|
|
19
|
+
this.assertions.push(assertion);
|
|
20
|
+
}
|
|
21
|
+
recordSignal(input) {
|
|
22
|
+
this.signals.push(input);
|
|
23
|
+
}
|
|
24
|
+
recordTurn(input) {
|
|
25
|
+
this.turns.push({
|
|
26
|
+
agent: input.agent,
|
|
27
|
+
engine: input.engine,
|
|
28
|
+
event: input.event,
|
|
29
|
+
totalMs: input.totalMs,
|
|
30
|
+
memoryPrepareMs: input.memoryPrepareMs,
|
|
31
|
+
engineMs: input.engineMs,
|
|
32
|
+
memoryRecordMs: input.memoryRecordMs,
|
|
33
|
+
promptChars: input.promptChars,
|
|
34
|
+
outputChars: input.outputChars,
|
|
35
|
+
recallCandidates: input.recall.totalCandidates,
|
|
36
|
+
recallSelected: input.recall.selectedEventIds.length,
|
|
37
|
+
mentions: mentionsFrom(input.outputText),
|
|
38
|
+
outputText: input.outputText,
|
|
39
|
+
recall: (input.recall.selected ?? []).map((selected) => ({
|
|
40
|
+
decision: selected.decision,
|
|
41
|
+
eventId: selected.eventId,
|
|
42
|
+
representation: excerpt(selected.representation),
|
|
43
|
+
scope: selected.scope
|
|
44
|
+
})),
|
|
45
|
+
signalsMentioned: this.knownSignals().filter((signal) => input.outputText.includes(signal)),
|
|
46
|
+
signalsInWakeText: this.knownSignals().filter((signal) => input.eventText.includes(signal))
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
recordConsultation(input) {
|
|
50
|
+
const observed = mentionsFrom(input.outputText).includes(input.to);
|
|
51
|
+
const edge = { ...input, observed };
|
|
52
|
+
this.consultations.push(edge);
|
|
53
|
+
this.recordAssertion({
|
|
54
|
+
detail: `${input.from} should mention @${input.to}`,
|
|
55
|
+
event: input.event,
|
|
56
|
+
kind: "consultation",
|
|
57
|
+
passed: observed
|
|
58
|
+
});
|
|
59
|
+
return edge;
|
|
60
|
+
}
|
|
61
|
+
benchRows() {
|
|
62
|
+
return this.turns.map((turn) => ({
|
|
63
|
+
agent: turn.agent,
|
|
64
|
+
engine: turn.engine,
|
|
65
|
+
event: turn.event,
|
|
66
|
+
totalMs: turn.totalMs,
|
|
67
|
+
memoryPrepareMs: turn.memoryPrepareMs,
|
|
68
|
+
engineMs: turn.engineMs,
|
|
69
|
+
memoryRecordMs: turn.memoryRecordMs,
|
|
70
|
+
promptChars: turn.promptChars,
|
|
71
|
+
outputChars: turn.outputChars,
|
|
72
|
+
recallCandidates: turn.recallCandidates,
|
|
73
|
+
recallSelected: turn.recallSelected
|
|
74
|
+
}));
|
|
75
|
+
}
|
|
76
|
+
summary() {
|
|
77
|
+
return this.turns.reduce((memo, turn) => {
|
|
78
|
+
const value = memo[turn.engine] ?? { count: 0, engineMs: 0, promptChars: 0, totalMs: 0 };
|
|
79
|
+
value.count += 1;
|
|
80
|
+
value.engineMs += turn.engineMs;
|
|
81
|
+
value.promptChars += turn.promptChars;
|
|
82
|
+
value.totalMs += turn.totalMs;
|
|
83
|
+
memo[turn.engine] = value;
|
|
84
|
+
return memo;
|
|
85
|
+
}, {});
|
|
86
|
+
}
|
|
87
|
+
behaviorSummary() {
|
|
88
|
+
const passedAssertions = this.assertions.filter((assertion) => assertion.passed).length;
|
|
89
|
+
const observedConsultations = this.consultations.filter((edge) => edge.observed).length;
|
|
90
|
+
const rate = (part, total) => total === 0 ? 1 : part / total;
|
|
91
|
+
return {
|
|
92
|
+
assertionPassRate: rate(passedAssertions, this.assertions.length),
|
|
93
|
+
consultationObservedRate: rate(observedConsultations, this.consultations.length),
|
|
94
|
+
signalsGenerated: this.signals.length,
|
|
95
|
+
totalRecallSelected: this.turns.reduce((total, turn) => total + turn.recallSelected, 0),
|
|
96
|
+
totalTurns: this.turns.length
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
tracePayload() {
|
|
100
|
+
return {
|
|
101
|
+
assertions: this.assertions,
|
|
102
|
+
behavior: this.behaviorSummary(),
|
|
103
|
+
consultations: this.consultations,
|
|
104
|
+
org_id: this.options.orgId,
|
|
105
|
+
run_id: this.options.runId,
|
|
106
|
+
schema: "daimon.org.telemetry/v1",
|
|
107
|
+
signals: this.signals,
|
|
108
|
+
summary: this.summary(),
|
|
109
|
+
turns: this.turns
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
async write(runtimeRoot) {
|
|
113
|
+
const telemetryDir = path.join(runtimeRoot, "telemetry");
|
|
114
|
+
await mkdir(telemetryDir, { recursive: true });
|
|
115
|
+
const summaryRecord = {
|
|
116
|
+
assertions: this.assertions,
|
|
117
|
+
behavior: this.behaviorSummary(),
|
|
118
|
+
kind: "org.turn_summary",
|
|
119
|
+
org_id: this.options.orgId,
|
|
120
|
+
run_id: this.options.runId,
|
|
121
|
+
schema: "daimon.org.telemetry/v1",
|
|
122
|
+
summary: this.summary()
|
|
123
|
+
};
|
|
124
|
+
const turnRecords = this.turns.map((turn) => ({
|
|
125
|
+
kind: "org.turn",
|
|
126
|
+
org_id: this.options.orgId,
|
|
127
|
+
run_id: this.options.runId,
|
|
128
|
+
schema: "daimon.org.telemetry/v1",
|
|
129
|
+
turn
|
|
130
|
+
}));
|
|
131
|
+
const ndjson = [...turnRecords, summaryRecord]
|
|
132
|
+
.map((record) => JSON.stringify(record))
|
|
133
|
+
.join("\n");
|
|
134
|
+
await writeFile(path.join(runtimeRoot, "trace.json"), `${JSON.stringify(this.tracePayload(), null, 2)}\n`);
|
|
135
|
+
await writeFile(path.join(runtimeRoot, "trace.md"), this.toMarkdown());
|
|
136
|
+
await writeFile(path.join(runtimeRoot, "bench.json"), `${JSON.stringify({ rows: this.benchRows(), summary: this.summary() }, null, 2)}\n`);
|
|
137
|
+
await writeFile(path.join(telemetryDir, "events.ndjson"), `${ndjson}\n`);
|
|
138
|
+
await writeFile(path.join(telemetryDir, "summary.json"), `${JSON.stringify(summaryRecord, null, 2)}\n`);
|
|
139
|
+
}
|
|
140
|
+
toMarkdown() {
|
|
141
|
+
const lines = [
|
|
142
|
+
"# Mixed Engine Org Trace",
|
|
143
|
+
"",
|
|
144
|
+
`- org: ${this.options.orgId}`,
|
|
145
|
+
`- run: ${this.options.runId}`,
|
|
146
|
+
"",
|
|
147
|
+
"## Behavior",
|
|
148
|
+
"",
|
|
149
|
+
`- assertion_pass_rate: ${this.behaviorSummary().assertionPassRate}`,
|
|
150
|
+
`- consultation_observed_rate: ${this.behaviorSummary().consultationObservedRate}`,
|
|
151
|
+
`- total_recall_selected: ${this.behaviorSummary().totalRecallSelected}`,
|
|
152
|
+
"",
|
|
153
|
+
"## Signals",
|
|
154
|
+
"",
|
|
155
|
+
"| Agent | Engine | Signal |",
|
|
156
|
+
"| --- | --- | --- |",
|
|
157
|
+
...this.signals.map((signal) => `| ${signal.agent} | ${signal.engine} | ${signal.signal} |`),
|
|
158
|
+
"",
|
|
159
|
+
"## Consultations",
|
|
160
|
+
"",
|
|
161
|
+
"| Event | Edge | Observed |",
|
|
162
|
+
"| --- | --- | --- |",
|
|
163
|
+
...this.consultations.map((edge) => `| ${edge.event} | ${edge.from} -> ${edge.to} | ${edge.observed ? "yes" : "no"} |`),
|
|
164
|
+
"",
|
|
165
|
+
"## Turns",
|
|
166
|
+
""
|
|
167
|
+
];
|
|
168
|
+
for (const turn of this.turns) {
|
|
169
|
+
lines.push(`### ${turn.event}`, "", `- agent: ${turn.agent}`, `- engine: ${turn.engine}`, `- total_ms: ${turn.totalMs}`, `- engine_ms: ${turn.engineMs}`, `- memory_prepare_ms: ${turn.memoryPrepareMs}`, `- memory_record_ms: ${turn.memoryRecordMs}`, `- prompt_chars: ${turn.promptChars}`, `- recall: ${turn.recallSelected}/${turn.recallCandidates}`, `- mentions: ${turn.mentions.join(", ") || "(none)"}`, `- signals_mentioned: ${turn.signalsMentioned.join(", ") || "(none)"}`, "", "Output:", "", `> ${turn.outputText}`, "");
|
|
170
|
+
if (turn.recall.length > 0) {
|
|
171
|
+
lines.push("Recalled memory:", "");
|
|
172
|
+
for (const recall of turn.recall) {
|
|
173
|
+
lines.push(`- ${recall.eventId} [${recall.decision}] ${recall.representation}`);
|
|
174
|
+
}
|
|
175
|
+
lines.push("");
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
lines.push("## Assertions", "", "| Event | Kind | Passed | Detail |", "| --- | --- | --- | --- |", ...this.assertions.map((assertion) => `| ${assertion.event} | ${assertion.kind} | ${assertion.passed ? "yes" : "no"} | ${assertion.detail} |`), "");
|
|
179
|
+
return lines.join("\n");
|
|
180
|
+
}
|
|
181
|
+
}
|