create-principles-disciple 1.132.2 → 1.132.4
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/codex-adapter/dist/codec/index.d.ts +6 -0
- package/codex-adapter/dist/codec/index.js +5 -0
- package/codex-adapter/dist/codec/input-decoder.d.ts +13 -0
- package/codex-adapter/dist/codec/input-decoder.js +103 -0
- package/codex-adapter/dist/codec/output-encoder.d.ts +32 -0
- package/codex-adapter/dist/codec/output-encoder.js +51 -0
- package/codex-adapter/dist/host-adapter.d.ts +30 -0
- package/codex-adapter/dist/host-adapter.js +28 -0
- package/codex-adapter/dist/index.d.ts +25 -0
- package/codex-adapter/dist/index.js +19 -0
- package/codex-adapter/dist/ingestion/admission.d.ts +54 -0
- package/codex-adapter/dist/ingestion/admission.js +137 -0
- package/codex-adapter/dist/ingestion/catch-up.d.ts +41 -0
- package/codex-adapter/dist/ingestion/catch-up.js +94 -0
- package/codex-adapter/dist/ingestion/codex-home.d.ts +19 -0
- package/codex-adapter/dist/ingestion/codex-home.js +43 -0
- package/codex-adapter/dist/ingestion/codex-version.d.ts +30 -0
- package/codex-adapter/dist/ingestion/codex-version.js +53 -0
- package/codex-adapter/dist/ingestion/ingestion-fields.d.ts +17 -0
- package/codex-adapter/dist/ingestion/ingestion-fields.js +28 -0
- package/codex-adapter/dist/ingestion/ingestion.d.ts +71 -0
- package/codex-adapter/dist/ingestion/ingestion.js +248 -0
- package/codex-adapter/dist/ingestion/transcript-decoder.d.ts +72 -0
- package/codex-adapter/dist/ingestion/transcript-decoder.js +485 -0
- package/codex-adapter/dist/ingestion/transcript-locate.d.ts +13 -0
- package/codex-adapter/dist/ingestion/transcript-locate.js +75 -0
- package/codex-adapter/dist/ingestion/transcript-path.d.ts +25 -0
- package/codex-adapter/dist/ingestion/transcript-path.js +87 -0
- package/codex-adapter/dist/pd-hook.d.ts +9 -0
- package/codex-adapter/dist/pd-hook.js +159 -0
- package/codex-adapter/dist/tool-semantics.d.ts +20 -0
- package/codex-adapter/dist/tool-semantics.js +27 -0
- package/codex-adapter/dist/worker/workspace-worker.d.ts +59 -0
- package/codex-adapter/dist/worker/workspace-worker.js +270 -0
- package/codex-adapter/package.json +52 -0
- package/console/dist/server/routes/update.js +187 -19
- package/console/dist/server/update/mutation-controller.d.ts +18 -0
- package/console/dist/server/update/mutation-controller.js +29 -0
- package/console/dist/server/utils/installed-layout.d.ts +1 -0
- package/console/dist/server/utils/installed-layout.js +2 -0
- package/console/package.json +1 -0
- package/core/dist/runtime-v2/__tests__/architecture-regression.test.js +15 -0
- package/core/dist/runtime-v2/__tests__/architecture-regression.test.js.map +1 -1
- package/core/dist/runtime-v2/adapter/__tests__/pd-stream-simple.test.d.ts +2 -0
- package/core/dist/runtime-v2/adapter/__tests__/pd-stream-simple.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/adapter/__tests__/pd-stream-simple.test.js +71 -0
- package/core/dist/runtime-v2/adapter/__tests__/pd-stream-simple.test.js.map +1 -0
- package/core/dist/runtime-v2/adapter/__tests__/pi-ai-http-transport.test.d.ts +2 -0
- package/core/dist/runtime-v2/adapter/__tests__/pi-ai-http-transport.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/adapter/__tests__/pi-ai-http-transport.test.js +219 -0
- package/core/dist/runtime-v2/adapter/__tests__/pi-ai-http-transport.test.js.map +1 -0
- package/core/dist/runtime-v2/adapter/artificer-l2-adapter.d.ts.map +1 -1
- package/core/dist/runtime-v2/adapter/artificer-l2-adapter.js +2 -3
- package/core/dist/runtime-v2/adapter/artificer-l2-adapter.js.map +1 -1
- package/core/dist/runtime-v2/adapter/l2-agent-loop-adapter.d.ts +12 -1
- package/core/dist/runtime-v2/adapter/l2-agent-loop-adapter.d.ts.map +1 -1
- package/core/dist/runtime-v2/adapter/l2-agent-loop-adapter.js +15 -2
- package/core/dist/runtime-v2/adapter/l2-agent-loop-adapter.js.map +1 -1
- package/core/dist/runtime-v2/adapter/pi-ai-http-transport.d.ts +50 -0
- package/core/dist/runtime-v2/adapter/pi-ai-http-transport.d.ts.map +1 -0
- package/core/dist/runtime-v2/adapter/pi-ai-http-transport.js +93 -0
- package/core/dist/runtime-v2/adapter/pi-ai-http-transport.js.map +1 -0
- package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.d.ts.map +1 -1
- package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.js +5 -0
- package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.js.map +1 -1
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision.test.js +152 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision.test.js.map +1 -1
- package/core/dist/runtime-v2/internalization/owner-review.d.ts +7 -1
- package/core/dist/runtime-v2/internalization/owner-review.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/owner-review.js +22 -3
- package/core/dist/runtime-v2/internalization/owner-review.js.map +1 -1
- package/core/package.json +3 -2
- package/dist/installer.d.ts +32 -0
- package/dist/installer.d.ts.map +1 -1
- package/dist/installer.js +198 -2
- package/dist/installer.js.map +1 -1
- package/dist/installers/codex-host-installer.d.ts.map +1 -1
- package/dist/installers/codex-host-installer.js +5 -3
- package/dist/installers/codex-host-installer.js.map +1 -1
- package/dist/update/release-manager-authority.d.ts +69 -0
- package/dist/update/release-manager-authority.d.ts.map +1 -0
- package/dist/update/release-manager-authority.js +125 -0
- package/dist/update/release-manager-authority.js.map +1 -0
- package/dist/utils/workspace-divergence.d.ts +36 -0
- package/dist/utils/workspace-divergence.d.ts.map +1 -0
- package/dist/utils/workspace-divergence.js +87 -0
- package/dist/utils/workspace-divergence.js.map +1 -0
- package/install-layout/dist/index.d.ts +5 -0
- package/install-layout/dist/index.js +1 -0
- package/install-layout/package.json +1 -1
- package/package.json +4 -2
- package/plugin/dist/bundle.js +870 -734
- package/plugin/dist/governance-audit.js +381 -247
- package/plugin/dist/openclaw-sdk.d.ts +3 -0
- package/plugin/dist/rulehost-evidence.js +420 -286
- package/plugin/dist/service/subagent-workflow/types.d.ts +2 -2
- package/plugin/dist/utils/retry.d.ts +1 -1
- package/plugin/dist/utils/workspace-resolver.d.ts +35 -4
- package/plugin/openclaw.plugin.json +1 -1
- package/plugin/package.json +1 -1
- package/release-manager/dist/i18n.d.ts +6 -0
- package/release-manager/dist/i18n.d.ts.map +1 -0
- package/release-manager/dist/i18n.js +234 -0
- package/release-manager/dist/i18n.js.map +1 -0
- package/release-manager/dist/index.d.ts +7 -0
- package/release-manager/dist/index.d.ts.map +1 -0
- package/release-manager/dist/index.js +474 -0
- package/release-manager/dist/index.js.map +1 -0
- package/release-manager/dist/installer.d.ts +217 -0
- package/release-manager/dist/installer.d.ts.map +1 -0
- package/release-manager/dist/installer.js +2451 -0
- package/release-manager/dist/installer.js.map +1 -0
- package/release-manager/dist/installers/codex-host-installer.d.ts +29 -0
- package/release-manager/dist/installers/codex-host-installer.d.ts.map +1 -0
- package/release-manager/dist/installers/codex-host-installer.js +496 -0
- package/release-manager/dist/installers/codex-host-installer.js.map +1 -0
- package/release-manager/dist/installers/index.d.ts +40 -0
- package/release-manager/dist/installers/index.d.ts.map +1 -0
- package/release-manager/dist/installers/index.js +35 -0
- package/release-manager/dist/installers/index.js.map +1 -0
- package/release-manager/dist/installers/openclaw-host-installer.d.ts +18 -0
- package/release-manager/dist/installers/openclaw-host-installer.d.ts.map +1 -0
- package/release-manager/dist/installers/openclaw-host-installer.js +414 -0
- package/release-manager/dist/installers/openclaw-host-installer.js.map +1 -0
- package/release-manager/dist/mvp-config.d.ts +197 -0
- package/release-manager/dist/mvp-config.d.ts.map +1 -0
- package/release-manager/dist/mvp-config.js +509 -0
- package/release-manager/dist/mvp-config.js.map +1 -0
- package/release-manager/dist/prompts.d.ts +30 -0
- package/release-manager/dist/prompts.d.ts.map +1 -0
- package/release-manager/dist/prompts.js +231 -0
- package/release-manager/dist/prompts.js.map +1 -0
- package/release-manager/dist/skill-language.d.ts +32 -0
- package/release-manager/dist/skill-language.d.ts.map +1 -0
- package/release-manager/dist/skill-language.js +107 -0
- package/release-manager/dist/skill-language.js.map +1 -0
- package/release-manager/dist/uninstaller.d.ts +55 -0
- package/release-manager/dist/uninstaller.d.ts.map +1 -0
- package/release-manager/dist/uninstaller.js +409 -0
- package/release-manager/dist/uninstaller.js.map +1 -0
- package/release-manager/dist/update/atomic-file.d.ts +35 -0
- package/release-manager/dist/update/atomic-file.d.ts.map +1 -0
- package/release-manager/dist/update/atomic-file.js +120 -0
- package/release-manager/dist/update/atomic-file.js.map +1 -0
- package/release-manager/dist/update/bootstrap-protocol.d.ts +55 -0
- package/release-manager/dist/update/bootstrap-protocol.d.ts.map +1 -0
- package/release-manager/dist/update/bootstrap-protocol.js +132 -0
- package/release-manager/dist/update/bootstrap-protocol.js.map +1 -0
- package/release-manager/dist/update/channel-metadata.d.ts +28 -0
- package/release-manager/dist/update/channel-metadata.d.ts.map +1 -0
- package/release-manager/dist/update/channel-metadata.js +90 -0
- package/release-manager/dist/update/channel-metadata.js.map +1 -0
- package/release-manager/dist/update/channel-promotion.d.ts +39 -0
- package/release-manager/dist/update/channel-promotion.d.ts.map +1 -0
- package/release-manager/dist/update/channel-promotion.js +55 -0
- package/release-manager/dist/update/channel-promotion.js.map +1 -0
- package/release-manager/dist/update/data-compatibility.d.ts +35 -0
- package/release-manager/dist/update/data-compatibility.d.ts.map +1 -0
- package/release-manager/dist/update/data-compatibility.js +45 -0
- package/release-manager/dist/update/data-compatibility.js.map +1 -0
- package/release-manager/dist/update/install-layout.d.ts +59 -0
- package/release-manager/dist/update/install-layout.d.ts.map +1 -0
- package/release-manager/dist/update/install-layout.js +127 -0
- package/release-manager/dist/update/install-layout.js.map +1 -0
- package/release-manager/dist/update/legacy-migration.d.ts +56 -0
- package/release-manager/dist/update/legacy-migration.d.ts.map +1 -0
- package/release-manager/dist/update/legacy-migration.js +254 -0
- package/release-manager/dist/update/legacy-migration.js.map +1 -0
- package/release-manager/dist/update/product-identity.d.ts +50 -0
- package/release-manager/dist/update/product-identity.d.ts.map +1 -0
- package/release-manager/dist/update/product-identity.js +63 -0
- package/release-manager/dist/update/product-identity.js.map +1 -0
- package/release-manager/dist/update/release-asset-manifest.d.ts +31 -0
- package/release-manager/dist/update/release-asset-manifest.d.ts.map +1 -0
- package/release-manager/dist/update/release-asset-manifest.js +161 -0
- package/release-manager/dist/update/release-asset-manifest.js.map +1 -0
- package/release-manager/dist/update/release-identity.d.ts +33 -0
- package/release-manager/dist/update/release-identity.d.ts.map +1 -0
- package/release-manager/dist/update/release-identity.js +108 -0
- package/release-manager/dist/update/release-identity.js.map +1 -0
- package/release-manager/dist/update/release-manager-authority.d.ts +69 -0
- package/release-manager/dist/update/release-manager-authority.d.ts.map +1 -0
- package/release-manager/dist/update/release-manager-authority.js +125 -0
- package/release-manager/dist/update/release-manager-authority.js.map +1 -0
- package/release-manager/dist/update/release-manager.d.ts +96 -0
- package/release-manager/dist/update/release-manager.d.ts.map +1 -0
- package/release-manager/dist/update/release-manager.js +253 -0
- package/release-manager/dist/update/release-manager.js.map +1 -0
- package/release-manager/dist/update/release-metadata.d.ts +57 -0
- package/release-manager/dist/update/release-metadata.d.ts.map +1 -0
- package/release-manager/dist/update/release-metadata.js +200 -0
- package/release-manager/dist/update/release-metadata.js.map +1 -0
- package/release-manager/dist/update/release-policy.d.ts +51 -0
- package/release-manager/dist/update/release-policy.d.ts.map +1 -0
- package/release-manager/dist/update/release-policy.js +103 -0
- package/release-manager/dist/update/release-policy.js.map +1 -0
- package/release-manager/dist/update/rollback-policy.d.ts +56 -0
- package/release-manager/dist/update/rollback-policy.d.ts.map +1 -0
- package/release-manager/dist/update/rollback-policy.js +61 -0
- package/release-manager/dist/update/rollback-policy.js.map +1 -0
- package/release-manager/dist/update/transaction-journal.d.ts +131 -0
- package/release-manager/dist/update/transaction-journal.d.ts.map +1 -0
- package/release-manager/dist/update/transaction-journal.js +346 -0
- package/release-manager/dist/update/transaction-journal.js.map +1 -0
- package/release-manager/dist/update/trust-metadata.d.ts +55 -0
- package/release-manager/dist/update/trust-metadata.d.ts.map +1 -0
- package/release-manager/dist/update/trust-metadata.js +170 -0
- package/release-manager/dist/update/trust-metadata.js.map +1 -0
- package/release-manager/dist/update/update-history.d.ts +46 -0
- package/release-manager/dist/update/update-history.d.ts.map +1 -0
- package/release-manager/dist/update/update-history.js +123 -0
- package/release-manager/dist/update/update-history.js.map +1 -0
- package/release-manager/dist/utils/config-file-io.d.ts +26 -0
- package/release-manager/dist/utils/config-file-io.d.ts.map +1 -0
- package/release-manager/dist/utils/config-file-io.js +128 -0
- package/release-manager/dist/utils/config-file-io.js.map +1 -0
- package/release-manager/dist/utils/env.d.ts +60 -0
- package/release-manager/dist/utils/env.d.ts.map +1 -0
- package/release-manager/dist/utils/env.js +308 -0
- package/release-manager/dist/utils/env.js.map +1 -0
- package/release-manager/dist/utils/logger.d.ts +15 -0
- package/release-manager/dist/utils/logger.d.ts.map +1 -0
- package/release-manager/dist/utils/logger.js +30 -0
- package/release-manager/dist/utils/logger.js.map +1 -0
- package/release-manager/dist/utils/workspace-divergence.d.ts +36 -0
- package/release-manager/dist/utils/workspace-divergence.d.ts.map +1 -0
- package/release-manager/dist/utils/workspace-divergence.js +87 -0
- package/release-manager/dist/utils/workspace-divergence.js.map +1 -0
- package/release-manager/package.json +79 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type CodexHomeResolution = {
|
|
2
|
+
ok: true;
|
|
3
|
+
home: string;
|
|
4
|
+
} | {
|
|
5
|
+
ok: false;
|
|
6
|
+
reason: string;
|
|
7
|
+
nextAction: string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Canonicalize a Windows path to its final long form. Node's JS realpath
|
|
11
|
+
* preserves 8.3 short-name segments (ADMINI~1), while hook payloads carry
|
|
12
|
+
* the long form — containment comparisons must use one canonical form, so
|
|
13
|
+
* prefer the native realpath (GetFinalPathNameByHandle) and fall back to
|
|
14
|
+
* the JS implementation where native is unavailable.
|
|
15
|
+
*/
|
|
16
|
+
export declare function canonicalizePath(target: string): string;
|
|
17
|
+
export declare function resolveCodexHome(env?: {
|
|
18
|
+
CODEX_HOME?: string | undefined;
|
|
19
|
+
}): CodexHomeResolution;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex home resolution (G1 probe report §7, source-pinned from
|
|
3
|
+
* codex-rs/utils/home-dir/src/lib.rs find_codex_home).
|
|
4
|
+
*
|
|
5
|
+
* Rules: CODEX_HOME, when set and non-empty, MUST already exist and be a
|
|
6
|
+
* directory and is canonicalized; a missing path or a file path is an error.
|
|
7
|
+
* When unset, the home comes from the OS home directory plus `.codex`, and
|
|
8
|
+
* existence is NOT verified. There is one OS-generic implementation.
|
|
9
|
+
*/
|
|
10
|
+
import fs from 'node:fs';
|
|
11
|
+
import os from 'node:os';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
/**
|
|
14
|
+
* Canonicalize a Windows path to its final long form. Node's JS realpath
|
|
15
|
+
* preserves 8.3 short-name segments (ADMINI~1), while hook payloads carry
|
|
16
|
+
* the long form — containment comparisons must use one canonical form, so
|
|
17
|
+
* prefer the native realpath (GetFinalPathNameByHandle) and fall back to
|
|
18
|
+
* the JS implementation where native is unavailable.
|
|
19
|
+
*/
|
|
20
|
+
export function canonicalizePath(target) {
|
|
21
|
+
try {
|
|
22
|
+
return fs.realpathSync.native(target);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return fs.realpathSync(target);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export function resolveCodexHome(env = process.env) {
|
|
29
|
+
const configured = env.CODEX_HOME;
|
|
30
|
+
if (configured !== undefined && configured.trim() !== '') {
|
|
31
|
+
try {
|
|
32
|
+
const stats = fs.statSync(configured);
|
|
33
|
+
if (!stats.isDirectory()) {
|
|
34
|
+
return { ok: false, reason: 'codex_home_not_directory', nextAction: 'CODEX_HOME must point at an existing directory (Codex fatal-error contract).' };
|
|
35
|
+
}
|
|
36
|
+
return { ok: true, home: canonicalizePath(configured) };
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return { ok: false, reason: 'codex_home_unavailable', nextAction: 'CODEX_HOME is set but does not exist; unset it or create the directory.' };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return { ok: true, home: path.join(os.homedir(), '.codex') };
|
|
43
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported Codex ingestion version contract (Codex Governance Closure
|
|
3
|
+
* Slice A; ADR-0020 §11.2 + G1 probe report §9).
|
|
4
|
+
*
|
|
5
|
+
* The G1 contract baseline is: minimum supported Codex 0.148.0, verified
|
|
6
|
+
* on-device at 0.150.1. The transcript itself carries `session_meta.
|
|
7
|
+
* payload.cli_version`, which is the version signal available inside a hook
|
|
8
|
+
* subprocess (hook payloads carry no version field — G1 fixture contract).
|
|
9
|
+
*
|
|
10
|
+
* Older-than-minimum and newer-than-verified versions both degrade
|
|
11
|
+
* explicitly; an unknown version must never silently guess record fields.
|
|
12
|
+
* Adopting a newer Codex version requires re-running the contract probe and
|
|
13
|
+
* refreshing the fixtures (G1 report §9 drift-detection boundary).
|
|
14
|
+
*/
|
|
15
|
+
export declare const CODEX_INGESTION_MIN_VERSION = "0.148.0";
|
|
16
|
+
export declare const CODEX_INGESTION_VERIFIED_VERSION = "0.150.1";
|
|
17
|
+
export type CodexVersionClassification = {
|
|
18
|
+
status: 'supported';
|
|
19
|
+
} | {
|
|
20
|
+
status: 'unsupported_below';
|
|
21
|
+
reason: 'unsupported_codex_version';
|
|
22
|
+
} | {
|
|
23
|
+
status: 'unverified_above';
|
|
24
|
+
reason: 'codex_version_unverified';
|
|
25
|
+
} | {
|
|
26
|
+
status: 'unknown';
|
|
27
|
+
reason: 'codex_version_unverified';
|
|
28
|
+
};
|
|
29
|
+
export declare const CODEX_VERSION_NEXT_ACTION = "rerun the Codex contract probe / update supported fixtures (docs/architecture/CODEX_G1_CONTRACT_PROBE_REPORT.md \u00A79)";
|
|
30
|
+
export declare function classifyCodexVersion(cliVersion: string | null | undefined): CodexVersionClassification;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported Codex ingestion version contract (Codex Governance Closure
|
|
3
|
+
* Slice A; ADR-0020 §11.2 + G1 probe report §9).
|
|
4
|
+
*
|
|
5
|
+
* The G1 contract baseline is: minimum supported Codex 0.148.0, verified
|
|
6
|
+
* on-device at 0.150.1. The transcript itself carries `session_meta.
|
|
7
|
+
* payload.cli_version`, which is the version signal available inside a hook
|
|
8
|
+
* subprocess (hook payloads carry no version field — G1 fixture contract).
|
|
9
|
+
*
|
|
10
|
+
* Older-than-minimum and newer-than-verified versions both degrade
|
|
11
|
+
* explicitly; an unknown version must never silently guess record fields.
|
|
12
|
+
* Adopting a newer Codex version requires re-running the contract probe and
|
|
13
|
+
* refreshing the fixtures (G1 report §9 drift-detection boundary).
|
|
14
|
+
*/
|
|
15
|
+
export const CODEX_INGESTION_MIN_VERSION = '0.148.0';
|
|
16
|
+
export const CODEX_INGESTION_VERIFIED_VERSION = '0.150.1';
|
|
17
|
+
export const CODEX_VERSION_NEXT_ACTION = 'rerun the Codex contract probe / update supported fixtures (docs/architecture/CODEX_G1_CONTRACT_PROBE_REPORT.md §9)';
|
|
18
|
+
function isDigits(value) {
|
|
19
|
+
return value.length > 0 && /^[0-9]+$/.test(value);
|
|
20
|
+
}
|
|
21
|
+
function parseSemver(value) {
|
|
22
|
+
const parts = value.trim().split('.');
|
|
23
|
+
if (parts.length < 3)
|
|
24
|
+
return null;
|
|
25
|
+
const [major, minor, patch] = parts;
|
|
26
|
+
if (major === undefined || minor === undefined || patch === undefined)
|
|
27
|
+
return null;
|
|
28
|
+
if (!isDigits(major) || !isDigits(minor) || !isDigits(patch))
|
|
29
|
+
return null;
|
|
30
|
+
return { major: Number(major), minor: Number(minor), patch: Number(patch) };
|
|
31
|
+
}
|
|
32
|
+
function compareSemver(a, b) {
|
|
33
|
+
if (a.major !== b.major)
|
|
34
|
+
return a.major - b.major;
|
|
35
|
+
if (a.minor !== b.minor)
|
|
36
|
+
return a.minor - b.minor;
|
|
37
|
+
return a.patch - b.patch;
|
|
38
|
+
}
|
|
39
|
+
export function classifyCodexVersion(cliVersion) {
|
|
40
|
+
if (typeof cliVersion !== 'string' || cliVersion.trim().length === 0) {
|
|
41
|
+
return { status: 'unknown', reason: 'codex_version_unverified' };
|
|
42
|
+
}
|
|
43
|
+
const version = parseSemver(cliVersion);
|
|
44
|
+
const min = parseSemver(CODEX_INGESTION_MIN_VERSION);
|
|
45
|
+
const verified = parseSemver(CODEX_INGESTION_VERIFIED_VERSION);
|
|
46
|
+
if (!version || !min || !verified)
|
|
47
|
+
return { status: 'unknown', reason: 'codex_version_unverified' };
|
|
48
|
+
if (compareSemver(version, min) < 0)
|
|
49
|
+
return { status: 'unsupported_below', reason: 'unsupported_codex_version' };
|
|
50
|
+
if (compareSemver(version, verified) > 0)
|
|
51
|
+
return { status: 'unverified_above', reason: 'codex_version_unverified' };
|
|
52
|
+
return { status: 'supported' };
|
|
53
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex hook payload field extraction (Slice A) — shared by the live
|
|
3
|
+
* observation ingestor and the Slice B admission orchestrator.
|
|
4
|
+
*/
|
|
5
|
+
export interface PayloadFields {
|
|
6
|
+
transcriptPath: string | null;
|
|
7
|
+
sessionId: string;
|
|
8
|
+
turnId: string | null;
|
|
9
|
+
prompt: string | null;
|
|
10
|
+
toolUseId: string | null;
|
|
11
|
+
toolName: string | null;
|
|
12
|
+
toolInput: unknown;
|
|
13
|
+
toolResponse: unknown;
|
|
14
|
+
}
|
|
15
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
16
|
+
export declare function own(value: Record<string, unknown>, key: string): unknown;
|
|
17
|
+
export declare function extractFields(raw: unknown): PayloadFields | null;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function isRecord(value) {
|
|
2
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
export function own(value, key) {
|
|
5
|
+
return Object.hasOwn(value, key) ? Object.getOwnPropertyDescriptor(value, key)?.value : undefined;
|
|
6
|
+
}
|
|
7
|
+
export function extractFields(raw) {
|
|
8
|
+
if (!isRecord(raw))
|
|
9
|
+
return null;
|
|
10
|
+
const transcriptPath = own(raw, 'transcript_path');
|
|
11
|
+
if (transcriptPath !== null && typeof transcriptPath !== 'string')
|
|
12
|
+
return null;
|
|
13
|
+
const sessionId = own(raw, 'session_id');
|
|
14
|
+
const turnId = own(raw, 'turn_id');
|
|
15
|
+
const prompt = own(raw, 'prompt');
|
|
16
|
+
const toolUseId = own(raw, 'tool_use_id');
|
|
17
|
+
const toolName = own(raw, 'tool_name');
|
|
18
|
+
return {
|
|
19
|
+
transcriptPath: transcriptPath ?? null,
|
|
20
|
+
sessionId: typeof sessionId === 'string' ? sessionId : '',
|
|
21
|
+
turnId: typeof turnId === 'string' ? turnId : null,
|
|
22
|
+
prompt: typeof prompt === 'string' ? prompt : null,
|
|
23
|
+
toolUseId: typeof toolUseId === 'string' ? toolUseId : null,
|
|
24
|
+
toolName: typeof toolName === 'string' ? toolName : null,
|
|
25
|
+
toolInput: own(raw, 'tool_input'),
|
|
26
|
+
toolResponse: own(raw, 'tool_response'),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex conversation ingestion orchestrator (Codex Governance Closure
|
|
3
|
+
* Slice A; SPEC rev 2 §7/§9/§17).
|
|
4
|
+
*
|
|
5
|
+
* Called by pd-hook ONLY after both `host.codex` and
|
|
6
|
+
* `codex_conversation_ingestion` are enabled — the flag check happens
|
|
7
|
+
* before any transcript filesystem I/O, so flag-off means this module is
|
|
8
|
+
* never entered and the transcript boundary receives zero calls (SPEC §10
|
|
9
|
+
* hard privacy invariant, proven by a port spy test).
|
|
10
|
+
*
|
|
11
|
+
* Responsibilities: extract validated ingestion fields from the raw hook
|
|
12
|
+
* payload (Codex protocol facts live in the adapter, not host-runtime),
|
|
13
|
+
* authorize the transcript path, run bounded incremental decoding from the
|
|
14
|
+
* durable checkpoint, apply the supported-version guard, and hand the
|
|
15
|
+
* projected observations to the host-neutral host-runtime seam.
|
|
16
|
+
*
|
|
17
|
+
* Stop (`turn_complete`) is the turn-complete ingestion trigger (G1 §2);
|
|
18
|
+
* live `UserPromptSubmit`/`PostToolUse` contribute live observations that
|
|
19
|
+
* the transcript replay later converges with (SPEC §10 source precedence).
|
|
20
|
+
*/
|
|
21
|
+
import type { HostEventKind } from '@principles/core/host';
|
|
22
|
+
import { type GovernanceSignalCandidate } from '@principles/host-runtime';
|
|
23
|
+
import { type TranscriptPort } from './transcript-decoder.js';
|
|
24
|
+
export interface CodexIngestionOptions {
|
|
25
|
+
readonly workspaceDir: string;
|
|
26
|
+
readonly env?: {
|
|
27
|
+
CODEX_HOME?: string | undefined;
|
|
28
|
+
};
|
|
29
|
+
readonly now?: Date;
|
|
30
|
+
readonly port?: TranscriptPort;
|
|
31
|
+
}
|
|
32
|
+
export type CodexIngestionOutcome = {
|
|
33
|
+
status: 'ok';
|
|
34
|
+
inserted: number;
|
|
35
|
+
enriched: number;
|
|
36
|
+
duplicates: number;
|
|
37
|
+
warnings: readonly string[];
|
|
38
|
+
lagBytes: number;
|
|
39
|
+
/** Slice B: normalized admission candidates for the committed observations (host-neutral; consumed by pd-hook's admission pass). */
|
|
40
|
+
readonly admissionCandidates: readonly GovernanceSignalCandidate[];
|
|
41
|
+
readonly rolloutIdentity: string;
|
|
42
|
+
} | {
|
|
43
|
+
status: 'degraded';
|
|
44
|
+
reason: string;
|
|
45
|
+
nextAction: string;
|
|
46
|
+
warnings: readonly string[];
|
|
47
|
+
};
|
|
48
|
+
/** Test seam: inject/inspect the transcript filesystem boundary (zero-read proofs). */
|
|
49
|
+
export declare function setCodexTranscriptPortForTest(port: TranscriptPort | null): void;
|
|
50
|
+
export declare function ingestCodexConversation(rawPayload: unknown, kind: HostEventKind, options: CodexIngestionOptions): CodexIngestionOutcome;
|
|
51
|
+
export interface CodexTranscriptFromPathArgs {
|
|
52
|
+
readonly workspaceDir: string;
|
|
53
|
+
/** Absolute transcript path (catch-up resolves it by exact rollout identity before calling). */
|
|
54
|
+
readonly transcriptPath: string;
|
|
55
|
+
/** Fallback root session when neither the transcript nor the checkpoint carries one. */
|
|
56
|
+
readonly fallbackRootSessionId: string;
|
|
57
|
+
readonly env?: {
|
|
58
|
+
CODEX_HOME?: string | undefined;
|
|
59
|
+
};
|
|
60
|
+
readonly now?: Date;
|
|
61
|
+
readonly port?: TranscriptPort;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* PRI-624 (Slice C): bounded incremental ingest of one known rollout from its
|
|
65
|
+
* durable checkpoint, driven by an explicit transcript path instead of a live
|
|
66
|
+
* hook payload. The path is re-authorized through the same
|
|
67
|
+
* `validateCodexTranscriptPath` containment + post-open identity contract the
|
|
68
|
+
* hook path uses (SPEC §9) — callers MUST only pass paths resolved from
|
|
69
|
+
* previously-authenticated rollouts (checkpoints), never discovered sessions.
|
|
70
|
+
*/
|
|
71
|
+
export declare function ingestCodexTranscriptFromPath(args: CodexTranscriptFromPathArgs): CodexIngestionOutcome;
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { ingestGovernanceObservations, readGovernanceCheckpoint, } from '@principles/host-runtime';
|
|
2
|
+
import { resolveCodexHome } from './codex-home.js';
|
|
3
|
+
import { validateCodexTranscriptPath } from './transcript-path.js';
|
|
4
|
+
import { classifyCodexVersion, CODEX_VERSION_NEXT_ACTION } from './codex-version.js';
|
|
5
|
+
import { decodeTranscriptWindow, createNodeTranscriptPort, CODEX_INGESTION_MAX_BATCH_BYTES, TranscriptReplacedError } from './transcript-decoder.js';
|
|
6
|
+
import { extractFields } from './ingestion-fields.js';
|
|
7
|
+
import { buildLiveCorrectionCandidate, buildLiveToolCandidate, buildTranscriptCandidates } from './admission.js';
|
|
8
|
+
let activePort = null;
|
|
9
|
+
/** Test seam: inject/inspect the transcript filesystem boundary (zero-read proofs). */
|
|
10
|
+
export function setCodexTranscriptPortForTest(port) {
|
|
11
|
+
activePort = port;
|
|
12
|
+
}
|
|
13
|
+
function activeTranscriptPort(options) {
|
|
14
|
+
return options.port ?? activePort ?? createNodeTranscriptPort();
|
|
15
|
+
}
|
|
16
|
+
function ingestLiveObservation({ fields, rolloutIdentity, workspaceDir, now }) {
|
|
17
|
+
const nowIso = now.toISOString();
|
|
18
|
+
let observation = null;
|
|
19
|
+
if (fields.turnId !== null && fields.prompt !== null) {
|
|
20
|
+
observation = {
|
|
21
|
+
hostKind: 'codex',
|
|
22
|
+
rolloutIdentity,
|
|
23
|
+
rootSessionId: fields.sessionId,
|
|
24
|
+
hostTurnId: fields.turnId,
|
|
25
|
+
kind: 'user_turn',
|
|
26
|
+
logicalObservationKey: `codex|${rolloutIdentity}|${fields.turnId}|user`,
|
|
27
|
+
visibleText: fields.prompt,
|
|
28
|
+
source: 'live_hook',
|
|
29
|
+
completeness: 'complete',
|
|
30
|
+
observedAt: nowIso,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
else if (fields.turnId !== null && fields.toolUseId !== null) {
|
|
34
|
+
observation = {
|
|
35
|
+
hostKind: 'codex',
|
|
36
|
+
rolloutIdentity,
|
|
37
|
+
rootSessionId: fields.sessionId,
|
|
38
|
+
hostTurnId: fields.turnId,
|
|
39
|
+
kind: 'tool_call',
|
|
40
|
+
logicalObservationKey: `codex|${rolloutIdentity}|${fields.toolUseId}`,
|
|
41
|
+
toolUseId: fields.toolUseId,
|
|
42
|
+
toolFacts: {
|
|
43
|
+
toolName: fields.toolName,
|
|
44
|
+
params: fields.toolInput ?? null,
|
|
45
|
+
result: fields.toolResponse ?? null,
|
|
46
|
+
},
|
|
47
|
+
source: 'live_hook',
|
|
48
|
+
completeness: 'complete',
|
|
49
|
+
observedAt: nowIso,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (observation === null) {
|
|
53
|
+
return { status: 'degraded', reason: 'transcript_path_invalid', nextAction: 'the live event lacks turn/prompt/tool identity; no observation was written.', warnings: [] };
|
|
54
|
+
}
|
|
55
|
+
const result = ingestGovernanceObservations({
|
|
56
|
+
workspaceDir,
|
|
57
|
+
observations: [observation],
|
|
58
|
+
now,
|
|
59
|
+
});
|
|
60
|
+
if (!result.ok)
|
|
61
|
+
return { status: 'degraded', reason: result.reason ?? 'governance_write_failed', nextAction: result.nextAction ?? 'inspect the workspace trajectory database.', warnings: result.warnings };
|
|
62
|
+
// Slice B: hand the live observation to the admission pass as a normalized
|
|
63
|
+
// candidate (SPEC §12) — the caller (pd-hook) runs detection/admission.
|
|
64
|
+
const candidate = observation.kind === 'user_turn'
|
|
65
|
+
? buildLiveCorrectionCandidate({ fields, rolloutIdentity })
|
|
66
|
+
: buildLiveToolCandidate({ fields, rolloutIdentity });
|
|
67
|
+
return {
|
|
68
|
+
status: 'ok', inserted: result.inserted, enriched: result.enriched, duplicates: result.duplicates, warnings: result.warnings, lagBytes: 0,
|
|
69
|
+
admissionCandidates: candidate !== null ? [candidate] : [],
|
|
70
|
+
rolloutIdentity,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function ingestTranscriptDelta({ fallbackRootSessionId, canonicalPath, identity, rolloutIdentity, workspaceDir, now, port }) {
|
|
74
|
+
const checkpoint = readGovernanceCheckpoint({ workspaceDir, hostKind: 'codex', rolloutIdentity });
|
|
75
|
+
if (checkpoint !== null && !('byteOffset' in checkpoint) && 'ok' in checkpoint && checkpoint.ok === false) {
|
|
76
|
+
return { status: 'degraded', reason: checkpoint.reason, nextAction: checkpoint.nextAction, warnings: [] };
|
|
77
|
+
}
|
|
78
|
+
const existing = checkpoint !== null && 'byteOffset' in checkpoint ? checkpoint : null;
|
|
79
|
+
const offset = existing !== null ? existing.byteOffset : 0;
|
|
80
|
+
let window;
|
|
81
|
+
try {
|
|
82
|
+
// Post-open revalidation (SPEC §9): the port must prove the opened
|
|
83
|
+
// object still carries the identity the validator approved.
|
|
84
|
+
window = port.read({ canonicalPath, offset, maxBytes: CODEX_INGESTION_MAX_BATCH_BYTES, expectedIdentity: identity });
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
if (error instanceof TranscriptReplacedError) {
|
|
88
|
+
return { status: 'degraded', reason: 'transcript_replaced', nextAction: 'the transcript changed identity after validation (replacement or symlink swap); refusing to read — the next Stop revalidates the current file.', warnings: [] };
|
|
89
|
+
}
|
|
90
|
+
const detail = error instanceof Error ? error.message.slice(0, 200) : String(error).slice(0, 200);
|
|
91
|
+
return { status: 'degraded', reason: `transcript_read_failed:${detail}`, nextAction: 'verify the transcript file still exists and is a regular file inside the Codex sessions root.', warnings: [] };
|
|
92
|
+
}
|
|
93
|
+
if (offset > window.fileSize) {
|
|
94
|
+
// The file shrank below the committed cursor: replaced or truncated —
|
|
95
|
+
// never guess; hold the checkpoint and degrade explicitly.
|
|
96
|
+
return { status: 'degraded', reason: 'checkpoint_inconsistent', nextAction: 'the transcript is shorter than the committed checkpoint; run the audited recovery/quarantine path once available or re-ingest from a fresh rollout.', warnings: [] };
|
|
97
|
+
}
|
|
98
|
+
const byteBoundReached = offset + window.bytes.length < window.fileSize;
|
|
99
|
+
const decoded = decodeTranscriptWindow({
|
|
100
|
+
bytes: window.bytes,
|
|
101
|
+
fileOffset: offset,
|
|
102
|
+
byteBoundReached,
|
|
103
|
+
rolloutIdentity,
|
|
104
|
+
fallbackRootSessionId: existing !== null ? existing.rootSessionId : fallbackRootSessionId,
|
|
105
|
+
nowIso: now.toISOString(),
|
|
106
|
+
});
|
|
107
|
+
// Supported-version guard (SPEC §9): the version signal lives in the
|
|
108
|
+
// transcript session_meta (or the committed checkpoint after the first
|
|
109
|
+
// batch). Anything below the verified floor or above the verified ceiling
|
|
110
|
+
// degrades explicitly — never guess record fields of an unknown contract.
|
|
111
|
+
const cliVersion = decoded.rolloutMeta.cliVersion ?? existing?.cliVersion ?? null;
|
|
112
|
+
const version = classifyCodexVersion(cliVersion);
|
|
113
|
+
if (version.status !== 'supported') {
|
|
114
|
+
return {
|
|
115
|
+
status: 'degraded',
|
|
116
|
+
reason: `${version.reason}:${cliVersion ?? 'unknown'}`,
|
|
117
|
+
nextAction: CODEX_VERSION_NEXT_ACTION,
|
|
118
|
+
warnings: decoded.warnings,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const degradations = [];
|
|
122
|
+
if (decoded.stop.kind === 'malformed') {
|
|
123
|
+
degradations.push({ reason: 'transcript_record_malformed', ...(decoded.stop.ordinal !== null ? { ordinal: decoded.stop.ordinal } : {}), nextAction: 'the record is stable-invalid; run the audited quarantine command once available (Slice D). Later records remain as lag.' });
|
|
124
|
+
}
|
|
125
|
+
else if (decoded.stop.kind === 'oversized_record') {
|
|
126
|
+
degradations.push({ reason: 'transcript_record_too_large', nextAction: 'a single transcript record exceeds the bounded-read window; inspect the rollout file.' });
|
|
127
|
+
}
|
|
128
|
+
const rootSessionId = decoded.rolloutMeta.rootSessionId ?? existing?.rootSessionId ?? fallbackRootSessionId;
|
|
129
|
+
const result = ingestGovernanceObservations({
|
|
130
|
+
workspaceDir,
|
|
131
|
+
rollout: {
|
|
132
|
+
hostKind: 'codex',
|
|
133
|
+
rolloutIdentity,
|
|
134
|
+
rootSessionId,
|
|
135
|
+
...(decoded.rolloutMeta.parentRolloutIdentity !== null ? { parentRolloutIdentity: decoded.rolloutMeta.parentRolloutIdentity } : {}),
|
|
136
|
+
...(decoded.rolloutMeta.agentIdentity !== null ? { agentIdentity: decoded.rolloutMeta.agentIdentity } : {}),
|
|
137
|
+
...(decoded.rolloutMeta.agentDepth !== null ? { agentDepth: decoded.rolloutMeta.agentDepth } : {}),
|
|
138
|
+
},
|
|
139
|
+
observations: decoded.observations,
|
|
140
|
+
checkpoint: {
|
|
141
|
+
hostKind: 'codex',
|
|
142
|
+
rolloutIdentity,
|
|
143
|
+
byteOffset: decoded.nextByteOffset,
|
|
144
|
+
lastOrdinal: decoded.lastOrdinal >= 0 ? decoded.lastOrdinal : existing?.lastOrdinal ?? 0,
|
|
145
|
+
cliVersion: cliVersion ?? undefined,
|
|
146
|
+
rootSessionId,
|
|
147
|
+
incompleteTail: decoded.stop.kind === 'incomplete_tail',
|
|
148
|
+
},
|
|
149
|
+
...(degradations.length > 0 ? { degradations } : {}),
|
|
150
|
+
...(decoded.compactionTimestamp !== null ? { compactionTimestamp: decoded.compactionTimestamp } : {}),
|
|
151
|
+
...(decoded.rollbackTurns.length > 0 ? { rollbackTurns: decoded.rollbackTurns } : {}),
|
|
152
|
+
now,
|
|
153
|
+
});
|
|
154
|
+
const warnings = [...decoded.warnings, ...result.warnings];
|
|
155
|
+
if (!result.ok) {
|
|
156
|
+
return { status: 'degraded', reason: result.reason ?? 'governance_write_failed', nextAction: result.nextAction ?? 'inspect the workspace trajectory database.', warnings };
|
|
157
|
+
}
|
|
158
|
+
if (decoded.stop.kind === 'incomplete_tail') {
|
|
159
|
+
warnings.push('transcript_incomplete_tail');
|
|
160
|
+
}
|
|
161
|
+
if (decoded.stop.kind === 'malformed') {
|
|
162
|
+
// The store already committed the records before the malformed line and
|
|
163
|
+
// held the checkpoint at it; surface the stable failure loudly (SPEC §14.2).
|
|
164
|
+
return {
|
|
165
|
+
status: 'degraded',
|
|
166
|
+
reason: 'transcript_record_malformed',
|
|
167
|
+
nextAction: 'the record is stable-invalid; run the audited quarantine command once available (Slice D). Later records remain as lag.',
|
|
168
|
+
warnings,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
if (decoded.stop.kind === 'oversized_record') {
|
|
172
|
+
return {
|
|
173
|
+
status: 'degraded',
|
|
174
|
+
reason: 'transcript_record_too_large',
|
|
175
|
+
nextAction: 'a single transcript record exceeds the bounded-read window; inspect the rollout file.',
|
|
176
|
+
warnings,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
status: 'ok',
|
|
181
|
+
inserted: result.inserted,
|
|
182
|
+
enriched: result.enriched,
|
|
183
|
+
duplicates: result.duplicates,
|
|
184
|
+
warnings,
|
|
185
|
+
lagBytes: Math.max(0, window.fileSize - decoded.nextByteOffset),
|
|
186
|
+
// Slice B: transcript-only user turns / tool calls that were never seen
|
|
187
|
+
// live get their admission evaluation here (SPEC §10/§12) — already
|
|
188
|
+
// admitted logical keys no-op on the marker.
|
|
189
|
+
admissionCandidates: buildTranscriptCandidates(decoded.observations),
|
|
190
|
+
rolloutIdentity,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
export function ingestCodexConversation(rawPayload, kind, options) {
|
|
194
|
+
const fields = extractFields(rawPayload);
|
|
195
|
+
if (fields === null || fields.sessionId.length === 0) {
|
|
196
|
+
return { status: 'degraded', reason: 'transcript_path_invalid', nextAction: 'the hook payload lacks the required Codex identity fields.', warnings: [] };
|
|
197
|
+
}
|
|
198
|
+
if (fields.transcriptPath === null) {
|
|
199
|
+
// SPEC §9: never scan for another file; keep a neutral degraded result.
|
|
200
|
+
return { status: 'degraded', reason: 'transcript_unavailable', nextAction: 'Codex supplied no transcript for this event; nothing is read or fabricated.', warnings: [] };
|
|
201
|
+
}
|
|
202
|
+
const home = resolveCodexHome(options.env);
|
|
203
|
+
if (!home.ok)
|
|
204
|
+
return { status: 'degraded', reason: home.reason, nextAction: home.nextAction, warnings: [] };
|
|
205
|
+
const validated = validateCodexTranscriptPath(fields.transcriptPath, home.home);
|
|
206
|
+
if (!validated.ok)
|
|
207
|
+
return { status: 'degraded', reason: validated.reason, nextAction: validated.nextAction, warnings: [] };
|
|
208
|
+
const now = options.now ?? new Date();
|
|
209
|
+
if (kind === 'before_prompt_build' || kind === 'after_tool_call') {
|
|
210
|
+
return ingestLiveObservation({ fields, rolloutIdentity: validated.rolloutIdentity, workspaceDir: options.workspaceDir, now });
|
|
211
|
+
}
|
|
212
|
+
return ingestTranscriptDelta({
|
|
213
|
+
fallbackRootSessionId: fields.sessionId,
|
|
214
|
+
canonicalPath: validated.canonicalPath,
|
|
215
|
+
identity: validated.identity,
|
|
216
|
+
rolloutIdentity: validated.rolloutIdentity,
|
|
217
|
+
workspaceDir: options.workspaceDir,
|
|
218
|
+
env: options.env ?? {},
|
|
219
|
+
now,
|
|
220
|
+
port: activeTranscriptPort(options),
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* PRI-624 (Slice C): bounded incremental ingest of one known rollout from its
|
|
225
|
+
* durable checkpoint, driven by an explicit transcript path instead of a live
|
|
226
|
+
* hook payload. The path is re-authorized through the same
|
|
227
|
+
* `validateCodexTranscriptPath` containment + post-open identity contract the
|
|
228
|
+
* hook path uses (SPEC §9) — callers MUST only pass paths resolved from
|
|
229
|
+
* previously-authenticated rollouts (checkpoints), never discovered sessions.
|
|
230
|
+
*/
|
|
231
|
+
export function ingestCodexTranscriptFromPath(args) {
|
|
232
|
+
const home = resolveCodexHome(args.env);
|
|
233
|
+
if (!home.ok)
|
|
234
|
+
return { status: 'degraded', reason: home.reason, nextAction: home.nextAction, warnings: [] };
|
|
235
|
+
const validated = validateCodexTranscriptPath(args.transcriptPath, home.home);
|
|
236
|
+
if (!validated.ok)
|
|
237
|
+
return { status: 'degraded', reason: validated.reason, nextAction: validated.nextAction, warnings: [] };
|
|
238
|
+
return ingestTranscriptDelta({
|
|
239
|
+
fallbackRootSessionId: args.fallbackRootSessionId,
|
|
240
|
+
canonicalPath: validated.canonicalPath,
|
|
241
|
+
identity: validated.identity,
|
|
242
|
+
rolloutIdentity: validated.rolloutIdentity,
|
|
243
|
+
workspaceDir: args.workspaceDir,
|
|
244
|
+
env: args.env ?? {},
|
|
245
|
+
now: args.now ?? new Date(),
|
|
246
|
+
port: args.port ?? activeTranscriptPort({ workspaceDir: args.workspaceDir }),
|
|
247
|
+
});
|
|
248
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { GovernanceObservationInput } from '@principles/host-runtime';
|
|
2
|
+
export declare const CODEX_INGESTION_MAX_BATCH_BYTES: number;
|
|
3
|
+
export declare const CODEX_INGESTION_MAX_BATCH_RECORDS = 256;
|
|
4
|
+
export interface TranscriptReadResult {
|
|
5
|
+
/** Raw bytes read from [offset, offset+maxBytes) — never a decoded string, so multi-byte UTF-8 never corrupts offset math. */
|
|
6
|
+
readonly bytes: Buffer;
|
|
7
|
+
readonly fileSize: number;
|
|
8
|
+
}
|
|
9
|
+
/** Identity the validator captured pre-open; the port must re-prove it post-open (SPEC §9 TOCTOU). */
|
|
10
|
+
export interface TranscriptExpectedIdentity {
|
|
11
|
+
readonly dev: number;
|
|
12
|
+
readonly ino: number;
|
|
13
|
+
readonly size: number;
|
|
14
|
+
readonly mtimeMs: number;
|
|
15
|
+
}
|
|
16
|
+
/** Arguments for one bounded transcript window read. */
|
|
17
|
+
export interface TranscriptReadRequest {
|
|
18
|
+
readonly canonicalPath: string;
|
|
19
|
+
readonly offset: number;
|
|
20
|
+
readonly maxBytes: number;
|
|
21
|
+
/** Pre-open identity from the validator; the port re-proves it post-open. */
|
|
22
|
+
readonly expectedIdentity?: TranscriptExpectedIdentity;
|
|
23
|
+
}
|
|
24
|
+
/** Filesystem boundary for the transcript read — injectable so tests can spy. */
|
|
25
|
+
export interface TranscriptPort {
|
|
26
|
+
read(request: TranscriptReadRequest): TranscriptReadResult;
|
|
27
|
+
}
|
|
28
|
+
/** Thrown by the port when the opened object is not the validated one (replacement/TOCTOU). */
|
|
29
|
+
export declare class TranscriptReplacedError extends Error {
|
|
30
|
+
constructor();
|
|
31
|
+
}
|
|
32
|
+
export declare function createNodeTranscriptPort(): TranscriptPort;
|
|
33
|
+
export type TranscriptDecodeStop = {
|
|
34
|
+
kind: 'eof';
|
|
35
|
+
} | {
|
|
36
|
+
kind: 'incomplete_tail';
|
|
37
|
+
} | {
|
|
38
|
+
kind: 'malformed';
|
|
39
|
+
ordinal: number | null;
|
|
40
|
+
} | {
|
|
41
|
+
kind: 'byte_bound';
|
|
42
|
+
} | {
|
|
43
|
+
kind: 'oversized_record';
|
|
44
|
+
};
|
|
45
|
+
export interface DecodedDelta {
|
|
46
|
+
readonly observations: readonly GovernanceObservationInput[];
|
|
47
|
+
readonly rolloutMeta: {
|
|
48
|
+
rootSessionId: string | null;
|
|
49
|
+
cliVersion: string | null;
|
|
50
|
+
parentRolloutIdentity: string | null;
|
|
51
|
+
agentIdentity: string | null;
|
|
52
|
+
agentDepth: number | null;
|
|
53
|
+
};
|
|
54
|
+
readonly compactionTimestamp: string | null;
|
|
55
|
+
readonly rollbackTurns: readonly number[];
|
|
56
|
+
readonly stop: TranscriptDecodeStop;
|
|
57
|
+
/** Checkpoint byte offset to commit: start of the first unconsumed byte. */
|
|
58
|
+
readonly nextByteOffset: number;
|
|
59
|
+
/** Highest ordinal consumed (for the checkpoint's last_ordinal). */
|
|
60
|
+
readonly lastOrdinal: number;
|
|
61
|
+
readonly warnings: readonly string[];
|
|
62
|
+
}
|
|
63
|
+
export interface DecodeTranscriptWindowInput {
|
|
64
|
+
readonly bytes: Buffer;
|
|
65
|
+
readonly fileOffset: number;
|
|
66
|
+
/** True when bytes.length did not reach EOF — the window was cut by the bounded read. */
|
|
67
|
+
readonly byteBoundReached: boolean;
|
|
68
|
+
readonly rolloutIdentity: string;
|
|
69
|
+
readonly fallbackRootSessionId: string | null;
|
|
70
|
+
readonly nowIso: string;
|
|
71
|
+
}
|
|
72
|
+
export declare function decodeTranscriptWindow(input: DecodeTranscriptWindowInput): DecodedDelta;
|