principles-disciple 1.71.0 → 1.73.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.
- package/openclaw.plugin.json +10 -5
- package/package.json +17 -19
- package/scripts/acceptance-test.mjs +16 -73
- package/scripts/sync-plugin.mjs +382 -77
- package/src/commands/archive-impl.ts +2 -1
- package/src/commands/capabilities.ts +2 -2
- package/src/commands/context.ts +2 -2
- package/src/commands/disable-impl.ts +2 -1
- package/src/commands/evolution-status.ts +16 -16
- package/src/commands/export.ts +12 -67
- package/src/commands/pain.ts +91 -1
- package/src/commands/principle-rollback.ts +2 -1
- package/src/commands/promote-impl.ts +7 -43
- package/src/commands/rollback-impl.ts +2 -1
- package/src/commands/rollback.ts +2 -1
- package/src/commands/samples.ts +2 -1
- package/src/commands/thinking-os.ts +2 -1
- package/src/config/errors.ts +18 -2
- package/src/constants/diagnostician.ts +2 -2
- package/src/constants/tools.ts +2 -1
- package/src/core/__tests__/focus-history.test.ts +210 -0
- package/src/core/config.ts +1 -1
- package/src/core/confirm-first-gate.ts +255 -0
- package/src/core/correction-cue-learner.ts +2 -136
- package/src/core/correction-types.ts +16 -88
- package/src/core/dictionary.ts +19 -20
- package/src/core/empathy-keyword-matcher.ts +17 -289
- package/src/core/empathy-types.ts +18 -229
- package/src/core/event-log.ts +38 -132
- package/src/core/evolution-reducer.ts +21 -2
- package/src/core/evolution-types.ts +76 -464
- package/src/core/file-store.ts +80 -0
- package/src/core/focus-history.ts +228 -955
- package/src/core/local-worker-routing.ts +34 -314
- package/src/core/merge-gate-audit.ts +0 -195
- package/src/core/pain-diagnostic-gate.ts +154 -0
- package/src/core/pain-signal.ts +21 -138
- package/src/core/pain.ts +15 -88
- package/src/core/pd-task-reconciler.ts +26 -115
- package/src/core/pd-task-service.ts +9 -9
- package/src/core/pd-task-types.ts +23 -127
- package/src/core/principle-compiler/__tests__/compiler-replay-gate.test.ts +174 -0
- package/src/core/principle-compiler/code-validator.ts +15 -42
- package/src/core/principle-compiler/compiler.ts +100 -15
- package/src/core/principle-compiler/index.ts +5 -2
- package/src/core/principle-compiler/template-generator.ts +4 -104
- package/src/core/principle-injection.ts +10 -202
- package/src/core/principle-internalization/filesystem-lifecycle-datasource.ts +42 -0
- package/src/core/principle-internalization/lifecycle-read-model.ts +39 -242
- package/src/core/principle-internalization/principle-lifecycle-service.ts +12 -10
- package/src/core/principle-tree-ledger-adapter.ts +145 -0
- package/src/core/principle-tree-ledger.ts +8 -6
- package/src/core/reflection/reflection-context.ts +14 -109
- package/src/core/replay-engine.ts +8 -500
- package/src/core/rule-host-helpers.ts +5 -35
- package/src/core/rule-host-types.ts +10 -82
- package/src/core/rule-host.ts +6 -63
- package/src/core/runtime-v2-prompt-activation-reader.ts +231 -0
- package/src/core/session-tracker.ts +87 -101
- package/src/core/shadow-observation-registry.ts +19 -48
- package/src/core/trajectory.ts +3 -1
- package/src/core/workflow-funnel-loader.ts +62 -68
- package/src/core/workspace-context.ts +46 -0
- package/src/core/workspace-dir-service.ts +1 -1
- package/src/core/workspace-dir-validation.ts +18 -9
- package/src/hooks/AGENTS.md +1 -1
- package/src/hooks/gate-block-helper.ts +46 -44
- package/src/hooks/gate.ts +207 -7
- package/src/hooks/lifecycle.ts +30 -32
- package/src/hooks/llm.ts +60 -32
- package/src/hooks/pain.ts +297 -103
- package/src/hooks/prompt.ts +469 -339
- package/src/hooks/subagent.ts +2 -29
- package/src/i18n/commands.ts +2 -10
- package/src/index.ts +95 -85
- package/src/openclaw-sdk.ts +311 -0
- package/src/service/central-database.ts +8 -4
- package/src/service/evolution-queue-migration.ts +2 -1
- package/src/service/evolution-worker.ts +163 -1786
- package/src/service/internalization-trigger-adapter.ts +302 -0
- package/src/service/keyword-optimization-service.ts +4 -4
- package/src/service/monitoring-query-service.ts +1 -215
- package/src/service/queue-io.ts +60 -331
- package/src/service/runtime-summary-service.ts +115 -18
- package/src/service/subagent-workflow/index.ts +0 -41
- package/src/service/subagent-workflow/types.ts +9 -120
- package/src/service/subagent-workflow/workflow-store.ts +2 -119
- package/src/service/workflow-watchdog.ts +0 -43
- package/src/types/event-payload.ts +16 -74
- package/src/types/event-types.ts +39 -547
- package/src/types/hygiene-types.ts +7 -30
- package/src/types/principle-tree-schema.ts +20 -222
- package/src/types/queue.ts +15 -70
- package/src/types/runtime-summary.ts +5 -49
- package/src/utils/io.ts +10 -0
- package/src/utils/retry.ts +1 -1
- package/src/utils/shadow-fingerprint.ts +2 -2
- package/src/utils/workspace-resolver.ts +50 -0
- package/templates/langs/en/core/AGENTS.md +2 -2
- package/templates/langs/en/core/BOOT.md +1 -1
- package/templates/langs/en/core/HEARTBEAT.md +2 -2
- package/templates/langs/en/skills/ai-sprint-orchestration/references/agent-registry.json +1 -72
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +6 -6
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +6 -6
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +2 -12
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +2 -12
- package/templates/langs/en/skills/ai-sprint-orchestration/runtime/.gitignore +2 -2
- package/templates/langs/en/skills/ai-sprint-orchestration/scripts/run.mjs +51 -15
- package/templates/langs/en/skills/evolve-task/SKILL.md +1 -1
- package/templates/langs/en/skills/pd-cli-operator/SKILL.md +67 -0
- package/templates/langs/en/skills/pd-diagnostician/SKILL.md +1 -1
- package/templates/langs/en/skills/pd-mentor/SKILL.md +1 -1
- package/templates/langs/en/skills/pd-pain-signal/SKILL.md +17 -39
- package/templates/langs/en/skills/pd-runtime-v2/SKILL.md +61 -0
- package/templates/langs/zh/core/AGENTS.md +2 -2
- package/templates/langs/zh/core/BOOT.md +1 -1
- package/templates/langs/zh/core/HEARTBEAT.md +2 -2
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/agent-registry.json +1 -72
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +6 -6
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +6 -6
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/nocturnal-trinity-quality-enhancement.json +8 -8
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +2 -12
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +2 -12
- package/templates/langs/zh/skills/ai-sprint-orchestration/runtime/.gitignore +2 -2
- package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/run.mjs +51 -15
- package/templates/langs/zh/skills/ai-sprint-orchestration/test/run.test.mjs +21 -5
- package/templates/langs/zh/skills/evolve-task/SKILL.md +2 -2
- package/templates/langs/zh/skills/pd-cli-operator/SKILL.md +67 -0
- package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +1 -1
- package/templates/langs/zh/skills/pd-mentor/SKILL.md +1 -1
- package/templates/langs/zh/skills/pd-pain-signal/SKILL.md +17 -38
- package/templates/langs/zh/skills/pd-runtime-v2/SKILL.md +61 -0
- package/tests/build-artifacts.test.ts +1 -3
- package/tests/commands/evolution-status.test.ts +0 -118
- package/tests/core/bootstrap-rules.test.ts +1 -1
- package/tests/core/config.test.ts +1 -1
- package/tests/core/event-log.test.ts +35 -0
- package/tests/core/evolution-engine.test.ts +610 -0
- package/tests/core/file-store.test.ts +102 -0
- package/tests/core/focus-history.test.ts +203 -11
- package/tests/core/merge-gate-audit.test.ts +2 -169
- package/tests/core/model-deployment-registry.test.ts +7 -1
- package/tests/core/model-training-registry.test.ts +19 -0
- package/tests/core/observability.test.ts +0 -1
- package/tests/core/pain-diagnostic-gate.test.ts +498 -0
- package/tests/core/pain.test.ts +0 -1
- package/tests/core/principle-internalization/deprecated-readiness.test.ts +2 -2
- package/tests/core/principle-internalization/lifecycle-metrics.test.ts +2 -2
- package/tests/core/principle-internalization/{internalization-routing-policy.test.ts → lifecycle-routing-policy.test.ts} +6 -6
- package/tests/core/principle-internalization/lineage-source-retired.test.ts +56 -0
- package/tests/core/principle-internalization/principle-lifecycle-service.test.ts +1 -23
- package/tests/core/principle-tree-ledger-adapter.test.ts +253 -0
- package/tests/core/reflection-context.test.ts +0 -14
- package/tests/core/replay-engine.test.ts +127 -215
- package/tests/core/rule-host-helpers.test.ts +2 -2
- package/tests/core/rule-implementation-runtime.test.ts +0 -27
- package/tests/core/workflow-funnel-loader.test.ts +162 -0
- package/tests/core/workspace-dir-validation.test.ts +8 -1
- package/tests/core-anti-growth.test.ts +192 -0
- package/tests/hook-workspace-nextaction-contract.test.ts +42 -0
- package/tests/hooks/confirm-first-gate.test.ts +333 -0
- package/tests/hooks/gate-auto-correct-shadow.test.ts +310 -0
- package/tests/hooks/gate-auto-correct.test.ts +665 -0
- package/tests/hooks/gate-rule-host-pipeline.test.ts +2 -1
- package/tests/hooks/pain.test.ts +269 -12
- package/tests/hooks/prompt-characterization.test.ts +500 -0
- package/tests/hooks/prompt-size-guard.test.ts +329 -0
- package/tests/hooks/runtime-v2-prompt-activation.test.ts +869 -0
- package/tests/index.test.ts +94 -1
- package/tests/integration/auto-entry-gate.test.ts +248 -0
- package/tests/integration/internalization-trigger-guard.test.ts +69 -0
- package/tests/integration/m8-legacy-paths.test.ts +63 -0
- package/tests/integration/runtime-v2-pain-guard.test.ts +125 -0
- package/tests/plugin-config-resolution-cutover.test.ts +359 -0
- package/tests/runtime-v2-discovery-guard.test.ts +154 -0
- package/tests/service/central-database.test.ts +457 -0
- package/tests/service/evolution-worker.correction-observer.test.ts +173 -0
- package/tests/service/evolution-worker.timeout.test.ts +11 -129
- package/tests/service/internalization-trigger-adapter.test.ts +251 -0
- package/tests/service/monitoring-query-service.test.ts +1 -47
- package/tests/service/queue-io.test.ts +1 -62
- package/tests/service/runtime-summary-service.test.ts +184 -3
- package/tests/service/workflow-watchdog.test.ts +0 -91
- package/tests/utils/file-lock.test.ts +5 -3
- package/tests/utils/session-key.test.ts +52 -0
- package/tests/utils/subagent-probe.test.ts +48 -1
- package/vitest.config.ts +4 -11
- package/.planning/codebase/ARCHITECTURE.md +0 -157
- package/.planning/codebase/CONCERNS.md +0 -145
- package/.planning/codebase/CONVENTIONS.md +0 -148
- package/.planning/codebase/INTEGRATIONS.md +0 -81
- package/.planning/codebase/STACK.md +0 -87
- package/.planning/codebase/STRUCTURE.md +0 -193
- package/.planning/codebase/TESTING.md +0 -243
- package/.planning/phases/01-basic-visualization/01-GAP-CLOSURE-VERIFICATION.md +0 -113
- package/docs/COMMAND_REFERENCE.md +0 -76
- package/docs/COMMAND_REFERENCE_EN.md +0 -79
- package/scripts/build-web.mjs +0 -46
- package/scripts/diagnose-nocturnal.mjs +0 -537
- package/scripts/seed-nocturnal-scenarios.mjs +0 -384
- package/src/commands/nocturnal-review.ts +0 -322
- package/src/commands/nocturnal-rollout.ts +0 -790
- package/src/commands/nocturnal-train.ts +0 -986
- package/src/commands/pd-reflect.ts +0 -88
- package/src/core/adaptive-thresholds.ts +0 -478
- package/src/core/diagnostician-task-store.ts +0 -192
- package/src/core/nocturnal-arbiter.ts +0 -715
- package/src/core/nocturnal-artifact-lineage.ts +0 -116
- package/src/core/nocturnal-artificer.ts +0 -257
- package/src/core/nocturnal-candidate-scoring.ts +0 -530
- package/src/core/nocturnal-compliance.ts +0 -1146
- package/src/core/nocturnal-dataset.ts +0 -763
- package/src/core/nocturnal-executability.ts +0 -428
- package/src/core/nocturnal-export.ts +0 -499
- package/src/core/nocturnal-paths.ts +0 -240
- package/src/core/nocturnal-reasoning-deriver.ts +0 -343
- package/src/core/nocturnal-rule-implementation-validator.ts +0 -246
- package/src/core/nocturnal-snapshot-contract.ts +0 -99
- package/src/core/nocturnal-trajectory-extractor.ts +0 -512
- package/src/core/nocturnal-trinity-types.ts +0 -218
- package/src/core/nocturnal-trinity.ts +0 -2680
- package/src/core/principle-internalization/deprecated-readiness.ts +0 -93
- package/src/core/principle-internalization/internalization-routing-policy.ts +0 -208
- package/src/core/principle-internalization/lifecycle-metrics.ts +0 -152
- package/src/http/principles-console-route.ts +0 -709
- package/src/service/central-health-service.ts +0 -49
- package/src/service/central-overview-service.ts +0 -138
- package/src/service/control-ui-query-service.ts +0 -900
- package/src/service/cooldown-strategy.ts +0 -97
- package/src/service/evolution-pain-context.ts +0 -79
- package/src/service/evolution-query-service.ts +0 -407
- package/src/service/health-query-service.ts +0 -1038
- package/src/service/nocturnal-config.ts +0 -214
- package/src/service/nocturnal-runtime.ts +0 -734
- package/src/service/nocturnal-service.ts +0 -1605
- package/src/service/nocturnal-target-selector.ts +0 -545
- package/src/service/sleep-cycle.ts +0 -157
- package/src/service/startup-reconciler.ts +0 -112
- package/src/service/subagent-workflow/correction-observer-types.ts +0 -82
- package/src/service/subagent-workflow/correction-observer-workflow-manager.ts +0 -250
- package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +0 -1
- package/src/service/subagent-workflow/dynamic-timeout.ts +0 -30
- package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +0 -268
- package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +0 -795
- package/src/service/subagent-workflow/runtime-direct-driver.ts +0 -268
- package/src/service/subagent-workflow/workflow-manager-base.ts +0 -580
- package/src/tools/write-pain-flag.ts +0 -215
- package/tests/commands/nocturnal-review.test.ts +0 -448
- package/tests/commands/nocturnal-train.test.ts +0 -97
- package/tests/commands/pd-reflect.test.ts +0 -49
- package/tests/core/adaptive-thresholds.test.ts +0 -261
- package/tests/core/nocturnal-arbiter.test.ts +0 -559
- package/tests/core/nocturnal-artifact-lineage.test.ts +0 -53
- package/tests/core/nocturnal-artificer.test.ts +0 -241
- package/tests/core/nocturnal-candidate-scoring.test.ts +0 -532
- package/tests/core/nocturnal-compliance-p-principles.test.ts +0 -133
- package/tests/core/nocturnal-compliance.test.ts +0 -646
- package/tests/core/nocturnal-dataset.test.ts +0 -892
- package/tests/core/nocturnal-e2e.test.ts +0 -234
- package/tests/core/nocturnal-executability.test.ts +0 -357
- package/tests/core/nocturnal-export.test.ts +0 -517
- package/tests/core/nocturnal-reasoning-deriver.test.ts +0 -372
- package/tests/core/nocturnal-reviewed-subset-comparison.test.ts +0 -428
- package/tests/core/nocturnal-rule-implementation-validator.test.ts +0 -127
- package/tests/core/nocturnal-snapshot-contract.test.ts +0 -121
- package/tests/core/nocturnal-trajectory-extractor.test.ts +0 -634
- package/tests/core/nocturnal-trinity.test.ts +0 -2053
- package/tests/core/pain-auto-repair.test.ts +0 -96
- package/tests/core/pain-integration.test.ts +0 -510
- package/tests/fixtures/nocturnal-reviewed-subset.json +0 -183
- package/tests/http/principles-console-route.test.ts +0 -162
- package/tests/integration/chaos-resilience.test.ts +0 -348
- package/tests/integration/empathy-workflow-integration.test.ts +0 -626
- package/tests/integration/pain-diagnostician-loop.e2e.test.ts +0 -380
- package/tests/service/control-ui-query-service.test.ts +0 -121
- package/tests/service/cooldown-strategy.test.ts +0 -164
- package/tests/service/data-endpoints-regression.test.ts +0 -834
- package/tests/service/empathy-observer-workflow-manager.test.ts +0 -175
- package/tests/service/evolution-worker.nocturnal.test.ts +0 -601
- package/tests/service/nocturnal-runtime-hardening.test.ts +0 -118
- package/tests/service/nocturnal-runtime.test.ts +0 -473
- package/tests/service/nocturnal-service-code-candidate.test.ts +0 -330
- package/tests/service/nocturnal-target-selector.test.ts +0 -615
- package/tests/service/startup-reconciler.test.ts +0 -148
- package/tests/tools/write-pain-flag.test.ts +0 -358
- package/ui/src/App.tsx +0 -45
- package/ui/src/api.ts +0 -220
- package/ui/src/charts.tsx +0 -955
- package/ui/src/components/ErrorState.tsx +0 -6
- package/ui/src/components/Loading.tsx +0 -13
- package/ui/src/components/ProtectedRoute.tsx +0 -12
- package/ui/src/components/Shell.tsx +0 -91
- package/ui/src/components/WorkspaceConfig.tsx +0 -178
- package/ui/src/components/index.ts +0 -5
- package/ui/src/context/auth.tsx +0 -80
- package/ui/src/context/theme.tsx +0 -66
- package/ui/src/hooks/useAutoRefresh.ts +0 -39
- package/ui/src/i18n/ui.ts +0 -473
- package/ui/src/main.tsx +0 -16
- package/ui/src/pages/EvolutionPage.tsx +0 -333
- package/ui/src/pages/FeedbackPage.tsx +0 -138
- package/ui/src/pages/GateMonitorPage.tsx +0 -136
- package/ui/src/pages/LoginPage.tsx +0 -89
- package/ui/src/pages/OverviewPage.tsx +0 -599
- package/ui/src/pages/SamplesPage.tsx +0 -174
- package/ui/src/pages/ThinkingModelsPage.tsx +0 -702
- package/ui/src/styles.css +0 -2020
- package/ui/src/types.ts +0 -384
- package/ui/src/utils/format.ts +0 -15
|
@@ -1,499 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Nocturnal ORPO Export — Approved Dataset to Decision-Point JSONL
|
|
3
|
-
* =================================================================
|
|
4
|
-
*
|
|
5
|
-
* PURPOSE: Export approved nocturnal samples as ORPO-formatted decision-point
|
|
6
|
-
* training JSONL, strictly separated from legacy correction export.
|
|
7
|
-
*
|
|
8
|
-
* ARCHITECTURE:
|
|
9
|
-
* - Export output: .state/exports/orpo/{exportId}.jsonl
|
|
10
|
-
* - Export manifest: .state/exports/orpo/{exportId}-manifest.json
|
|
11
|
-
* - Legacy corrections: untouched, separate path
|
|
12
|
-
*
|
|
13
|
-
* ORPO FORMAT (each line):
|
|
14
|
-
* {
|
|
15
|
-
* sampleFingerprint: string,
|
|
16
|
-
* artifactId: string,
|
|
17
|
-
* sessionId: string,
|
|
18
|
-
* principleId: string,
|
|
19
|
-
* targetModelFamily: string,
|
|
20
|
-
* prompt: string, // badDecision (the wrong choice)
|
|
21
|
-
* chosen: string, // betterDecision (the right choice)
|
|
22
|
-
* rejected: string, // badDecision (for ORPO)
|
|
23
|
-
* rationale: string,
|
|
24
|
-
* datasetMetadata: {
|
|
25
|
-
* sampleFingerprint: string,
|
|
26
|
-
* artifactPath: string,
|
|
27
|
-
* createdAt: string,
|
|
28
|
-
* exportedAt: string,
|
|
29
|
-
* exportId: string,
|
|
30
|
-
* datasetFingerprint: string
|
|
31
|
-
* }
|
|
32
|
-
* }
|
|
33
|
-
*
|
|
34
|
-
* EXPORT GATING (fail-closed):
|
|
35
|
-
* - reviewStatus === 'approved_for_training'
|
|
36
|
-
* - targetModelFamily matches requested target (or any if not specified)
|
|
37
|
-
* - Lineage fields complete (sampleFingerprint, artifactId, sessionId, principleId)
|
|
38
|
-
* - Source artifact file exists and is approved
|
|
39
|
-
*
|
|
40
|
-
* DESIGN CONSTRAINTS:
|
|
41
|
-
* - No trainer invocation
|
|
42
|
-
* - No automatic training
|
|
43
|
-
* - No checkpoint deploy
|
|
44
|
-
* - Export is read-only from dataset perspective
|
|
45
|
-
*/
|
|
46
|
-
|
|
47
|
-
import * as fs from 'fs';
|
|
48
|
-
import * as path from 'path';
|
|
49
|
-
import * as crypto from 'crypto';
|
|
50
|
-
import { atomicWriteFileSync } from '../utils/io.js';
|
|
51
|
-
import {
|
|
52
|
-
listDatasetRecords,
|
|
53
|
-
readDatasetArtifact,
|
|
54
|
-
type NocturnalDatasetRecord,
|
|
55
|
-
} from './nocturnal-dataset.js';
|
|
56
|
-
import {
|
|
57
|
-
listArtifactLineageRecords,
|
|
58
|
-
type ArtifactLineageRecord,
|
|
59
|
-
} from './nocturnal-artifact-lineage.js';
|
|
60
|
-
import { NocturnalPathResolver } from './nocturnal-paths.js';
|
|
61
|
-
|
|
62
|
-
// ---------------------------------------------------------------------------
|
|
63
|
-
// Types
|
|
64
|
-
// ---------------------------------------------------------------------------
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* A single ORPO training sample in JSONL format.
|
|
68
|
-
*/
|
|
69
|
-
export interface ORPOSample {
|
|
70
|
-
sampleFingerprint: string;
|
|
71
|
-
artifactId: string;
|
|
72
|
-
sessionId: string;
|
|
73
|
-
principleId: string;
|
|
74
|
-
targetModelFamily: string;
|
|
75
|
-
/** The suboptimal decision (what the agent did wrong) */
|
|
76
|
-
prompt: string;
|
|
77
|
-
/** The correct decision (what should have been done) */
|
|
78
|
-
chosen: string;
|
|
79
|
-
/** The suboptimal decision (same as prompt, for ORPO structure) */
|
|
80
|
-
rejected: string;
|
|
81
|
-
rationale: string;
|
|
82
|
-
datasetMetadata: {
|
|
83
|
-
sampleFingerprint: string;
|
|
84
|
-
artifactPath: string;
|
|
85
|
-
createdAt: string;
|
|
86
|
-
exportedAt: string;
|
|
87
|
-
exportId: string;
|
|
88
|
-
datasetFingerprint: string;
|
|
89
|
-
evidenceSummary: ORPOEvidenceSummary;
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export type EvidenceState = 'observed' | 'not_observed' | 'unknown';
|
|
94
|
-
|
|
95
|
-
export interface ORPOEvidenceSummary {
|
|
96
|
-
lineageStatus: 'observed' | 'unknown';
|
|
97
|
-
painSignals: {
|
|
98
|
-
status: EvidenceState;
|
|
99
|
-
count: number | null;
|
|
100
|
-
ids: string[];
|
|
101
|
-
};
|
|
102
|
-
gateBlocks: {
|
|
103
|
-
status: EvidenceState;
|
|
104
|
-
count: number | null;
|
|
105
|
-
ids: string[];
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Export manifest containing metadata about the entire export.
|
|
111
|
-
*/
|
|
112
|
-
export interface ORPOExportManifest {
|
|
113
|
-
exportId: string;
|
|
114
|
-
createdAt: string;
|
|
115
|
-
sampleCount: number;
|
|
116
|
-
targetModelFamily: string;
|
|
117
|
-
/** SHA-256 of all sample fingerprints, sorted — for reproducibility */
|
|
118
|
-
datasetFingerprint: string;
|
|
119
|
-
exportPath: string;
|
|
120
|
-
manifestPath: string;
|
|
121
|
-
samples: {
|
|
122
|
-
sampleFingerprint: string;
|
|
123
|
-
artifactId: string;
|
|
124
|
-
sessionId: string;
|
|
125
|
-
principleId: string;
|
|
126
|
-
}[];
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Result of an export operation.
|
|
131
|
-
*/
|
|
132
|
-
export interface ExportResult {
|
|
133
|
-
success: boolean;
|
|
134
|
-
manifest?: ORPOExportManifest;
|
|
135
|
-
error?: string;
|
|
136
|
-
emptyReason?: 'no_approved_samples' | 'family_mismatch' | 'all_samples_missing_artifacts';
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// ---------------------------------------------------------------------------
|
|
140
|
-
// Dataset Fingerprint (for reproducibility)
|
|
141
|
-
// ---------------------------------------------------------------------------
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Compute a deterministic dataset fingerprint from a sorted list of sample fingerprints.
|
|
145
|
-
* This allows reproducible exports — same dataset always produces same fingerprint.
|
|
146
|
-
*/
|
|
147
|
-
function computeDatasetFingerprint(sampleFingerprints: string[]): string {
|
|
148
|
-
const sorted = [...sampleFingerprints].sort();
|
|
149
|
-
const combined = sorted.join('|');
|
|
150
|
-
return crypto.createHash('sha256').update(combined, 'utf8').digest('hex');
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// ---------------------------------------------------------------------------
|
|
154
|
-
// Individual Sample Serialization
|
|
155
|
-
// ---------------------------------------------------------------------------
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Serialize a single dataset record + artifact to ORPO JSONL line.
|
|
159
|
-
* Caller guarantees record.targetModelFamily is non-null.
|
|
160
|
-
*/
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
function serializeORPOSample(
|
|
164
|
-
record: NocturnalDatasetRecord,
|
|
165
|
-
artifact: ReturnType<typeof readDatasetArtifact>,
|
|
166
|
-
evidenceSummary: ORPOEvidenceSummary,
|
|
167
|
-
exportId: string,
|
|
168
|
-
datasetFingerprint: string
|
|
169
|
-
): ORPOSample {
|
|
170
|
-
const now = new Date().toISOString();
|
|
171
|
-
|
|
172
|
-
const rejected = buildEvidenceBoundedRejected(artifact, evidenceSummary);
|
|
173
|
-
|
|
174
|
-
return {
|
|
175
|
-
sampleFingerprint: record.sampleFingerprint,
|
|
176
|
-
artifactId: record.artifactId,
|
|
177
|
-
sessionId: record.sessionId,
|
|
178
|
-
principleId: record.principleId,
|
|
179
|
-
targetModelFamily: record.targetModelFamily as string, // validated non-null by caller
|
|
180
|
-
// Export only evidence-bounded narratives. Free-form artifact text can overstate what was observed.
|
|
181
|
-
prompt: rejected,
|
|
182
|
-
chosen: artifact.betterDecision,
|
|
183
|
-
rejected,
|
|
184
|
-
|
|
185
|
-
rationale: buildEvidenceBoundedRationale(evidenceSummary),
|
|
186
|
-
datasetMetadata: {
|
|
187
|
-
sampleFingerprint: record.sampleFingerprint,
|
|
188
|
-
artifactPath: record.artifactPath,
|
|
189
|
-
createdAt: record.createdAt,
|
|
190
|
-
exportedAt: now,
|
|
191
|
-
exportId,
|
|
192
|
-
datasetFingerprint,
|
|
193
|
-
evidenceSummary,
|
|
194
|
-
},
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function buildEvidenceSummary(
|
|
199
|
-
lineageRecord: ArtifactLineageRecord | null
|
|
200
|
-
): ORPOEvidenceSummary {
|
|
201
|
-
if (!lineageRecord) {
|
|
202
|
-
return {
|
|
203
|
-
lineageStatus: 'unknown',
|
|
204
|
-
painSignals: { status: 'unknown', count: null, ids: [] },
|
|
205
|
-
gateBlocks: { status: 'unknown', count: null, ids: [] },
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// Defensive: old lineage files on disk may lack these fields
|
|
210
|
-
const painIds = lineageRecord.sourcePainIds ?? [];
|
|
211
|
-
const gateBlockIds = lineageRecord.sourceGateBlockIds ?? [];
|
|
212
|
-
const painCount = painIds.length;
|
|
213
|
-
const gateCount = gateBlockIds.length;
|
|
214
|
-
|
|
215
|
-
return {
|
|
216
|
-
lineageStatus: 'observed',
|
|
217
|
-
painSignals: {
|
|
218
|
-
status: painCount > 0 ? 'observed' : 'not_observed',
|
|
219
|
-
count: painCount,
|
|
220
|
-
ids: [...painIds],
|
|
221
|
-
},
|
|
222
|
-
gateBlocks: {
|
|
223
|
-
status: gateCount > 0 ? 'observed' : 'not_observed',
|
|
224
|
-
count: gateCount,
|
|
225
|
-
ids: [...gateBlockIds],
|
|
226
|
-
},
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
function buildEvidenceBoundedRejected(
|
|
231
|
-
artifact: ReturnType<typeof readDatasetArtifact>,
|
|
232
|
-
evidenceSummary: ORPOEvidenceSummary
|
|
233
|
-
): string {
|
|
234
|
-
if (evidenceSummary.lineageStatus === 'unknown') {
|
|
235
|
-
return 'Take the next action without verified source evidence.';
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const clauses: string[] = [];
|
|
239
|
-
if (evidenceSummary.painSignals.status === 'observed' && evidenceSummary.painSignals.count) {
|
|
240
|
-
clauses.push(`continue despite ${evidenceSummary.painSignals.count} observed pain signals`);
|
|
241
|
-
}
|
|
242
|
-
if (evidenceSummary.gateBlocks.status === 'observed' && evidenceSummary.gateBlocks.count) {
|
|
243
|
-
clauses.push(`ignore ${evidenceSummary.gateBlocks.count} observed gate blocks`);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
if (clauses.length === 0) {
|
|
247
|
-
return 'Proceed without first verifying the relevant state from the source session.';
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
const prefix = artifact.badDecision.trim().length > 0
|
|
251
|
-
? 'Proceed with the rejected action and '
|
|
252
|
-
: 'Take the rejected action and ';
|
|
253
|
-
return `${prefix}${clauses.join(' and ')}.`;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
function buildEvidenceBoundedRationale(evidenceSummary: ORPOEvidenceSummary): string {
|
|
257
|
-
if (evidenceSummary.lineageStatus === 'unknown') {
|
|
258
|
-
return 'Source evidence is unknown. Export uses a neutral rationale instead of narrating unverified failures or violations.';
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
const painCount = evidenceSummary.painSignals.count ?? 0;
|
|
262
|
-
const gateCount = evidenceSummary.gateBlocks.count ?? 0;
|
|
263
|
-
if (painCount === 0 && gateCount === 0) {
|
|
264
|
-
return 'Source lineage is present but records no pain signals or gate blocks. Export keeps the corrective preference while avoiding invented failure narratives.';
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
return `Observed source evidence: ${painCount} pain signals and ${gateCount} gate blocks. Prefer the bounded corrective action over repeating the rejected choice.`;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// ---------------------------------------------------------------------------
|
|
271
|
-
// Core Export Function
|
|
272
|
-
// ---------------------------------------------------------------------------
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Export approved nocturnal samples as ORPO decision-point JSONL.
|
|
276
|
-
*
|
|
277
|
-
* @param workspaceDir - Workspace directory
|
|
278
|
-
* @param targetModelFamily - Specific model family to export, or undefined for all
|
|
279
|
-
* @param options - Additional export options
|
|
280
|
-
* @returns ExportResult
|
|
281
|
-
*/
|
|
282
|
-
export function exportORPOSamples(
|
|
283
|
-
workspaceDir: string,
|
|
284
|
-
targetModelFamily?: string | null,
|
|
285
|
-
_options: Record<string, never> = {}
|
|
286
|
-
): ExportResult {
|
|
287
|
-
const exportId = crypto.randomUUID();
|
|
288
|
-
const now = new Date().toISOString();
|
|
289
|
-
const lineageRecords = listArtifactLineageRecords(workspaceDir, 'behavioral-sample');
|
|
290
|
-
|
|
291
|
-
// Step 1: Collect eligible records
|
|
292
|
-
// Use listDatasetRecords directly to have full control over the family filter
|
|
293
|
-
// (listExportReadyRecords uses ?? which maps null→undefined, losing the null distinction)
|
|
294
|
-
const allApprovedRecords = listDatasetRecords(workspaceDir, {
|
|
295
|
-
reviewStatus: 'approved_for_training',
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
let eligibleRecords: typeof allApprovedRecords;
|
|
301
|
-
|
|
302
|
-
if (targetModelFamily !== undefined && targetModelFamily !== null) {
|
|
303
|
-
// Specific family: check if ANY records (regardless of status) have this family
|
|
304
|
-
const allRecords = listDatasetRecords(workspaceDir);
|
|
305
|
-
const hasAnyWithFamily = allRecords.some((r) => r.targetModelFamily === targetModelFamily);
|
|
306
|
-
if (!hasAnyWithFamily) {
|
|
307
|
-
// Family doesn't exist in any record
|
|
308
|
-
return {
|
|
309
|
-
success: false,
|
|
310
|
-
error: 'No samples found for the requested target model family',
|
|
311
|
-
emptyReason: 'family_mismatch',
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
// Family exists but none are approved
|
|
315
|
-
eligibleRecords = allApprovedRecords.filter((r) => r.targetModelFamily === targetModelFamily);
|
|
316
|
-
} else {
|
|
317
|
-
// All families
|
|
318
|
-
eligibleRecords = allApprovedRecords;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
// Step 2: Validate we have records
|
|
322
|
-
if (eligibleRecords.length === 0) {
|
|
323
|
-
return {
|
|
324
|
-
success: false,
|
|
325
|
-
error: 'No approved samples found for export',
|
|
326
|
-
emptyReason: 'no_approved_samples',
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// Step 3: Verify lineage completeness and read artifacts
|
|
331
|
-
const orpoSamples: ORPOSample[] = [];
|
|
332
|
-
const failedFingerprints: string[] = [];
|
|
333
|
-
|
|
334
|
-
for (const record of eligibleRecords) {
|
|
335
|
-
// Enforce targetModelFamily binding — samples without a family cannot enter training
|
|
336
|
-
if (record.targetModelFamily === null) {
|
|
337
|
-
failedFingerprints.push(record.sampleFingerprint);
|
|
338
|
-
continue;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// Verify lineage completeness
|
|
342
|
-
if (!record.sampleFingerprint || !record.artifactId || !record.sessionId || !record.principleId) {
|
|
343
|
-
failedFingerprints.push(record.sampleFingerprint);
|
|
344
|
-
continue;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// Read artifact (throws on error — distinguishes read failure from missing artifact)
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
let artifact;
|
|
351
|
-
try {
|
|
352
|
-
artifact = readDatasetArtifact(workspaceDir, record.sampleFingerprint);
|
|
353
|
-
} catch {
|
|
354
|
-
failedFingerprints.push(record.sampleFingerprint);
|
|
355
|
-
continue;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
const lineageRecord =
|
|
359
|
-
lineageRecords.find((candidate) => candidate.artifactId === record.artifactId) ?? null;
|
|
360
|
-
const evidenceSummary = buildEvidenceSummary(lineageRecord);
|
|
361
|
-
|
|
362
|
-
// Serialize
|
|
363
|
-
orpoSamples.push(serializeORPOSample(record, artifact, evidenceSummary, exportId, ''));
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// Step 4: Fail if all samples failed validation
|
|
367
|
-
if (orpoSamples.length === 0) {
|
|
368
|
-
return {
|
|
369
|
-
success: false,
|
|
370
|
-
error: `All ${eligibleRecords.length} eligible samples failed validation (missing artifacts or lineage)`,
|
|
371
|
-
emptyReason: 'all_samples_missing_artifacts',
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// Step 5: Compute dataset fingerprint for manifest
|
|
376
|
-
const datasetFingerprint = computeDatasetFingerprint(
|
|
377
|
-
orpoSamples.map((s) => s.sampleFingerprint)
|
|
378
|
-
);
|
|
379
|
-
|
|
380
|
-
// Step 6: Fill in dataset fingerprint in all samples
|
|
381
|
-
for (const sample of orpoSamples) {
|
|
382
|
-
sample.datasetMetadata.datasetFingerprint = datasetFingerprint;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
// Step 7: Write JSONL file
|
|
386
|
-
const exportsDir = NocturnalPathResolver.exportsDir(workspaceDir);
|
|
387
|
-
const jsonlPath = path.join(exportsDir, `${exportId}.jsonl`);
|
|
388
|
-
const lines = orpoSamples.map((s) => JSON.stringify(s)).join('\n') + '\n';
|
|
389
|
-
atomicWriteFileSync(jsonlPath, lines);
|
|
390
|
-
|
|
391
|
-
// Step 8: Write manifest
|
|
392
|
-
const manifest: ORPOExportManifest = {
|
|
393
|
-
exportId,
|
|
394
|
-
createdAt: now,
|
|
395
|
-
sampleCount: orpoSamples.length,
|
|
396
|
-
targetModelFamily: targetModelFamily ?? 'all',
|
|
397
|
-
datasetFingerprint,
|
|
398
|
-
exportPath: jsonlPath,
|
|
399
|
-
manifestPath: path.join(exportsDir, `${exportId}-manifest.json`),
|
|
400
|
-
samples: orpoSamples.map((s) => ({
|
|
401
|
-
sampleFingerprint: s.sampleFingerprint,
|
|
402
|
-
artifactId: s.artifactId,
|
|
403
|
-
sessionId: s.sessionId,
|
|
404
|
-
principleId: s.principleId,
|
|
405
|
-
})),
|
|
406
|
-
};
|
|
407
|
-
|
|
408
|
-
atomicWriteFileSync(manifest.manifestPath, JSON.stringify(manifest, null, 2));
|
|
409
|
-
|
|
410
|
-
return {
|
|
411
|
-
success: true,
|
|
412
|
-
manifest,
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
/**
|
|
417
|
-
* Verify an existing export by re-computing its dataset fingerprint.
|
|
418
|
-
* Returns true if the export is intact and reproducible.
|
|
419
|
-
*/
|
|
420
|
-
export function verifyExportIntegrity(
|
|
421
|
-
workspaceDir: string,
|
|
422
|
-
exportId: string
|
|
423
|
-
): { valid: boolean; computedFingerprint: string; manifestFingerprint: string } | null {
|
|
424
|
-
const exportsDir = NocturnalPathResolver.exportsDir(workspaceDir);
|
|
425
|
-
const manifestPath = path.join(exportsDir, `${exportId}-manifest.json`);
|
|
426
|
-
|
|
427
|
-
if (!fs.existsSync(manifestPath)) {
|
|
428
|
-
return null;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
try {
|
|
432
|
-
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8')) as ORPOExportManifest;
|
|
433
|
-
const computedFingerprint = computeDatasetFingerprint(
|
|
434
|
-
manifest.samples.map((s) => s.sampleFingerprint)
|
|
435
|
-
);
|
|
436
|
-
|
|
437
|
-
return {
|
|
438
|
-
valid: computedFingerprint === manifest.datasetFingerprint,
|
|
439
|
-
computedFingerprint,
|
|
440
|
-
manifestFingerprint: manifest.datasetFingerprint,
|
|
441
|
-
};
|
|
442
|
-
} catch {
|
|
443
|
-
return null;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
/**
|
|
448
|
-
* List all exports in the exports directory.
|
|
449
|
-
*/
|
|
450
|
-
export function listExports(workspaceDir: string): ORPOExportManifest[] {
|
|
451
|
-
const exportsDir = NocturnalPathResolver.exportsDir(workspaceDir);
|
|
452
|
-
if (!fs.existsSync(exportsDir)) {
|
|
453
|
-
return [];
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
try {
|
|
457
|
-
const files = fs.readdirSync(exportsDir);
|
|
458
|
-
const manifests: ORPOExportManifest[] = [];
|
|
459
|
-
|
|
460
|
-
for (const file of files) {
|
|
461
|
-
if (!file.endsWith('-manifest.json')) continue;
|
|
462
|
-
try {
|
|
463
|
-
const manifest = JSON.parse(
|
|
464
|
-
fs.readFileSync(path.join(exportsDir, file), 'utf-8')
|
|
465
|
-
) as ORPOExportManifest;
|
|
466
|
-
manifests.push(manifest);
|
|
467
|
-
} catch {
|
|
468
|
-
// Skip malformed manifest
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
return manifests.sort(
|
|
473
|
-
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
|
|
474
|
-
);
|
|
475
|
-
} catch {
|
|
476
|
-
return [];
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
/**
|
|
481
|
-
* Read an export manifest by ID.
|
|
482
|
-
*/
|
|
483
|
-
export function getExportManifest(
|
|
484
|
-
workspaceDir: string,
|
|
485
|
-
exportId: string
|
|
486
|
-
): ORPOExportManifest | null {
|
|
487
|
-
const exportsDir = NocturnalPathResolver.exportsDir(workspaceDir);
|
|
488
|
-
const manifestPath = path.join(exportsDir, `${exportId}-manifest.json`);
|
|
489
|
-
|
|
490
|
-
if (!fs.existsSync(manifestPath)) {
|
|
491
|
-
return null;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
try {
|
|
495
|
-
return JSON.parse(fs.readFileSync(manifestPath, 'utf-8')) as ORPOExportManifest;
|
|
496
|
-
} catch {
|
|
497
|
-
return null;
|
|
498
|
-
}
|
|
499
|
-
}
|