create-principles-disciple 1.145.43 → 1.146.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/_release/product-identity.json +2 -2
  2. package/console/dist/server/routes/examiner-executor.d.ts +8 -2
  3. package/console/dist/server/routes/examiner-executor.js +10 -4
  4. package/console/dist/server/routes/examiner.d.ts +23 -17
  5. package/console/dist/server/routes/examiner.js +30 -27
  6. package/console/dist/ui/App.js +1 -1
  7. package/console/dist/ui/components/layout/app-sidebar.d.ts +3 -4
  8. package/console/dist/ui/components/layout/app-sidebar.js +5 -7
  9. package/console/dist/ui/i18n/en.json +1 -1
  10. package/console/dist/ui/i18n/zh-CN.json +1 -1
  11. package/console/dist/ui/utils/runtime-mode.d.ts +2 -1
  12. package/console/dist/ui/utils/runtime-mode.js +2 -1
  13. package/console/dist/web/assets/app.js +23 -23
  14. package/console/lab/lib/challenge-actor.mjs +394 -0
  15. package/console/lab/lib/challenge-arms.mjs +102 -0
  16. package/console/lab/lib/challenge-behavior-report.mjs +2484 -0
  17. package/console/lab/lib/challenge-compile.mjs +211 -0
  18. package/console/lab/lib/challenge-freeze.mjs +215 -0
  19. package/console/lab/lib/challenge-isolation-probe.mjs +550 -0
  20. package/console/lab/lib/challenge-ledger.mjs +206 -0
  21. package/console/lab/lib/challenge-openclaw-host.mjs +630 -0
  22. package/console/lab/lib/challenge-runner.mjs +868 -0
  23. package/console/lab/lib/challenge-stage-r-gate.mjs +458 -0
  24. package/console/lab/lib/challenge-statistics.mjs +591 -0
  25. package/console/lab/lib/claim-evidence-env.mjs +652 -0
  26. package/console/lab/lib/claim-evidence-scorer.mjs +784 -0
  27. package/console/lab/lib/examiner-injection-evidence.mjs +367 -0
  28. package/console/lab/lib/examiner-study.mjs +1203 -0
  29. package/console/lab/lib/examiner-v07-cases.mjs +187 -0
  30. package/console/lab/lib/examiner-v2-cases.mjs +61 -0
  31. package/console/lab/lib/fixture.mjs +382 -0
  32. package/console/lab/lib/g2.mjs +146 -0
  33. package/console/lab/lib/host-actor.mjs +987 -0
  34. package/console/lab/lib/sidecar.mjs +273 -0
  35. package/package.json +1 -1
  36. package/release-manager/package.json +1 -1
@@ -0,0 +1,146 @@
1
+ // PRI-971 — G2 preparation layer: same-channel delivery, memory manifest,
2
+ // model stub and cross-arm contamination checks.
3
+ //
4
+ // Everything here is WIRING-ONLY preparation for the real-agent pilot. It
5
+ // proves the plumbing (delivery, isolation, freezing, receipt accounting)
6
+ // with a deterministic stub; it deliberately cannot and does not run any real
7
+ // model. Real-agent execution stays NOT_RUN until the Owner decisions listed
8
+ // in fixtures/pilot-run-config.json are made.
9
+ //
10
+ // Same-channel contract (SPEC §4.2/§9.1 Pilot-1/2): whatever the arm content
11
+ // is (real generated principle, strong summary, raw experience, nothing), it
12
+ // reaches the actor ONLY through deliverPayload() — one code path, one
13
+ // position, one budget. The C-A contrast can then never be accidentally
14
+ // confounded by different delivery mechanics.
15
+
16
+ import { createHash } from 'node:crypto';
17
+ import { readFileSync } from 'node:fs';
18
+
19
+ export const DELIVERY_CHANNEL = 'research-same-channel-v1';
20
+ export const MEMORY_MANIFEST_SCHEMA = 'memory-manifest.v0.1';
21
+
22
+ function sha256(text) {
23
+ return createHash('sha256').update(text, 'utf8').digest('hex');
24
+ }
25
+
26
+ function sha256File(file) {
27
+ return sha256(readFileSync(file, 'utf8'));
28
+ }
29
+
30
+ /**
31
+ * The single delivery path for arm content entering any actor session.
32
+ * `content` is the exact bytes to deliver (frozen candidate policy text,
33
+ * summary text, raw experience text — or null for the no-content arm).
34
+ * Returns an immutable receipt describing what the actor WILL see.
35
+ */
36
+ export function deliverPayload({ armId, content, position = 'after-task-text', budgetChars = 2000 }) {
37
+ const payloadText = content === null || content === undefined ? '' : String(content);
38
+ if (payloadText.length > budgetChars) {
39
+ throw new Error(`deliverPayload: arm ${armId} payload exceeds the channel budget (${payloadText.length} > ${budgetChars} chars) — truncate policy belongs to the study design, not to delivery`);
40
+ }
41
+ return {
42
+ channel: DELIVERY_CHANNEL,
43
+ armId,
44
+ payloadText,
45
+ payloadHash: sha256(payloadText),
46
+ deliveredBytes: Buffer.byteLength(payloadText, 'utf8'),
47
+ position,
48
+ budgetChars,
49
+ deliveredAt: null, // stamped by the runner in real runs; stays null in stubs
50
+ };
51
+ }
52
+
53
+ /** What the actor session input looks like: task text + payload in position. */
54
+ export function composeActorPrompt({ taskText, delivery }) {
55
+ if (delivery.position === 'after-task-text') {
56
+ return `${taskText}\n\n--- applied context ---\n${delivery.payloadText}`;
57
+ }
58
+ throw new Error(`composeActorPrompt: unsupported position ${delivery.position}`);
59
+ }
60
+
61
+ /**
62
+ * Memory manifest for REAL agent profiles (G2): fingerprint every external
63
+ * rule/memory file the profile could see, so a run can prove which external
64
+ * content existed. G1 simulated arms use the empty embedded variant.
65
+ */
66
+ export function buildMemoryManifest({ files }) {
67
+ const entries = files.map((f) => ({
68
+ path: f.path,
69
+ role: f.role,
70
+ sha256: sha256File(f.path),
71
+ bytes: null, // filled by the caller if needed; hash is the identity
72
+ }));
73
+ return { schemaVersion: MEMORY_MANIFEST_SCHEMA, entries, note: 'fingerprint of externally visible rule/memory files at run start' };
74
+ }
75
+
76
+ /**
77
+ * Deterministic model stub — the Actor test interface.
78
+ *
79
+ * `scriptedPlans` maps armId -> plan (actions array, like the calibration
80
+ * battery). The stub faithfully RECORDS everything it received through the
81
+ * channel and replays the scripted plan; it never sees the environment or
82
+ * the gold. This is what proves the wiring end-to-end without any model.
83
+ */
84
+ export function createModelStub({ scriptedPlans }) {
85
+ const receipts = [];
86
+ return {
87
+ async runActor({ armId, delivery, taskText }) {
88
+ if (delivery.channel !== DELIVERY_CHANNEL) {
89
+ throw new Error(`model stub: refusing non-${DELIVERY_CHANNEL} delivery`);
90
+ }
91
+ const plan = scriptedPlans[armId];
92
+ if (!plan) throw new Error(`model stub: no scripted plan for arm ${armId}`);
93
+ receipts.push({
94
+ armId,
95
+ payloadHash: delivery.payloadHash,
96
+ promptHash: sha256(composeActorPrompt({ taskText, delivery })),
97
+ });
98
+ return { actions: plan.actions.map((a) => ({ ...a })), narrative: plan.narrative ?? '' };
99
+ },
100
+ receipts() {
101
+ return receipts.slice();
102
+ },
103
+ };
104
+ }
105
+
106
+ /**
107
+ * Cross-arm contamination check: every stub receipt must carry its OWN arm's
108
+ * payload hash, and no foreign payload may appear in any receipt's prompt.
109
+ * `deliveries` maps armId -> delivery receipt produced by deliverPayload.
110
+ */
111
+ export function checkNoCrossContamination({ receipts, deliveries }) {
112
+ const problems = [];
113
+ for (const r of receipts) {
114
+ const own = deliveries[r.armId];
115
+ if (!own) {
116
+ problems.push(`receipt for unknown arm ${r.armId}`);
117
+ continue;
118
+ }
119
+ if (r.payloadHash !== own.payloadHash) {
120
+ problems.push(`arm ${r.armId} received payload ${r.payloadHash.slice(0, 8)}… instead of its own ${own.payloadHash.slice(0, 8)}… (cross-arm contamination)`);
121
+ }
122
+ }
123
+ return problems;
124
+ }
125
+
126
+ /**
127
+ * Stricter prompt-level contamination check: recompute each receipt's expected
128
+ * prompt hash from ITS OWN delivery and task text; a mismatch means the stub
129
+ * (or a future real runner) composed the prompt with foreign content.
130
+ */
131
+ export function verifyPromptIsolation({ receipts, deliveries, taskTextByArm }) {
132
+ const problems = [];
133
+ for (const r of receipts) {
134
+ const own = deliveries[r.armId];
135
+ const taskText = taskTextByArm[r.armId];
136
+ if (!own || !taskText) {
137
+ problems.push(`missing delivery or task text for arm ${r.armId}`);
138
+ continue;
139
+ }
140
+ const expected = sha256(composeActorPrompt({ taskText, delivery: own }));
141
+ if (r.promptHash !== expected) {
142
+ problems.push(`arm ${r.armId} prompt hash mismatch — session input was not composed from this arm's own delivery (contamination or wiring bug)`);
143
+ }
144
+ }
145
+ return problems;
146
+ }