mtech-ai-local-runner 0.1.28 → 0.1.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,43 @@
1
+ ## 0.1.30: governed local alert investigation evidence
2
+
3
+ The Gateway candidate offers explicit Local Codex Runner or Local Claude Runner
4
+ selection for read-only Sentry investigation. It binds the chosen adapter/model
5
+ and employee device to the exact approved launch; it never falls back to another
6
+ adapter. Each adapter retains authenticated host-owned bounded read observations
7
+ through completion and cleanup. These counts are employee-runner evidence, not
8
+ independent root-cause verification.
9
+
10
+ This release does not remove Claude's current unsupported host-admission gate.
11
+ An installed or authenticated CLI alone is not execution permission. Gateway
12
+ deployment, feature activation and actual execution approvals remain separate.
13
+
14
+ ## 0.1.29: reviewed daemon Codex admission
15
+
16
+ This candidate connects the installed, hash-pinned root policy observer to the
17
+ existing employee-privileged Codex CLI adapter. The observer accepts only its
18
+ fixed employee UID and runs only a fixed read-only machine-policy command; it
19
+ never runs a model or reads credentials. A fresh per-job inventory and local
20
+ forced-policy checks are required. Managed, partial, stale and unknown evidence
21
+ block execution. This snapshot is not continuous machine-policy monitoring.
22
+
23
+ The package alone does not install or start the privileged service. Deployment
24
+ and employee-daemon upgrade remain separately approved operations. The daemon
25
+ advertises Codex only when the installed observer and supported binary/account
26
+ checks pass; the current account settings and Claude path are retained. Existing
27
+ manual releases remain separately registered. Already approved foreground Codex
28
+ acceptance is not daemon acceptance.
29
+
30
+ Terminal completion requests are persisted before delivery and replayed with
31
+ the original generation, lease and result after an unknown acknowledgement;
32
+ this never reruns a model. Admitted task duration/count remain unbounded. The
33
+ preparation, lease, cancellation, path and authenticated-MCP checks remain.
34
+
35
+ Starting the upgraded employee daemon can refresh Gateway OAuth credentials.
36
+ Future actual Codex execution may refresh its original shared account tokens.
37
+ Neither effect is part of no-model local verification. See the repository
38
+ `docs/ai-ops-gateway/codex-daemon-admission-validation.md` for exact evidence and
39
+ remaining acceptance gates.
40
+
1
41
  # MTEC AI Local Runner
2
42
 
3
43
  Install and configure the employee-bound macOS daemon:
@@ -8,7 +48,15 @@ npx mtech-ai-local-runner setup
8
48
 
9
49
  The package stores safe runner configuration locally and keeps OAuth refresh credentials in macOS Keychain when available.
10
50
 
11
- Public npm installation requires no npm account or publishing OTP. Setup requires Gateway OAuth login, and local Claude Code must already be authenticated (`claude auth status`).
51
+ Public npm installation requires no npm account or publishing OTP. Setup requires
52
+ the employee's own Gateway OAuth login and a supported, authenticated selected
53
+ CLI. Codex uses the employee's ChatGPT account. Claude requires its own login
54
+ and a separately verified host admission; it is currently not admitted.
55
+
56
+ For the current unattended Codex pilot, an administrator must separately install
57
+ the reviewed, employee-UID-bound policy observer on that Mac. The npm setup
58
+ command does not install that privileged service. A fresh employee computer is
59
+ not ready merely because npm installation succeeds.
12
60
 
13
61
  ## 0.1.28: completion receipt wire compatibility
14
62
 
@@ -8,6 +8,7 @@ import { completionEvent } from "./completion-report.js";
8
8
  import { requireJobSecurity } from "../../safety/job-security-policy.js";
9
9
  import { HOOK_POLICY_ENV_VAR, buildHookPolicy, encodeHookPolicy } from "../../safety/hook-policy-builder.js";
10
10
  import { formatHookCommand } from "../../safety/hook-command.js";
11
+ import { localEndpointReadObservations } from '../../safety/local-tool-server.js';
11
12
  const ALLOWED_ENV_KEYS = ["PATH", "HOME", "USER", "LOGNAME", "LANG", "LC_ALL", "TMPDIR", "NODE_ENV", "CLAUDE_CONFIG_DIR"];
12
13
  const ALLOWED_SUPERVISOR_COMMANDS = new Set(["continue", "answer_question", "request_revision", "request_checkpoint", "submit_for_review"]);
13
14
  const LOCAL_NAMES = ["local_read", "local_search", "local_edit", "local_test"].map(name => `mcp__ai_local__${name}`);
@@ -44,7 +45,16 @@ export class ClaudeCodeAdapter {
44
45
  cancelled = false;
45
46
  started = false;
46
47
  receipt;
48
+ toolEndpoint;
49
+ readSnapshot;
47
50
  securityReceipt() { return this.receipt ? structuredClone(this.receipt) : undefined; }
51
+ localReadObservations() {
52
+ const endpoint = this.toolEndpoint;
53
+ const observed = endpoint ? localEndpointReadObservations(endpoint.url, endpoint.token) : undefined;
54
+ if (observed)
55
+ this.readSnapshot = observed;
56
+ return this.readSnapshot ? structuredClone(this.readSnapshot) : undefined;
57
+ }
48
58
  constructor(options) {
49
59
  this.options = options;
50
60
  }
@@ -97,6 +107,7 @@ export class ClaudeCodeAdapter {
97
107
  if (this.cancelled)
98
108
  throw new Error("adapter startup cancelled");
99
109
  const endpoint = await this.tools.start();
110
+ this.toolEndpoint = endpoint;
100
111
  if (this.cancelled)
101
112
  throw new Error("adapter startup cancelled");
102
113
  const mcpConfigPath = this.options.writeLocalMcpConfig(endpoint);
@@ -222,6 +233,8 @@ export class ClaudeCodeAdapter {
222
233
  await this.cleanup();
223
234
  }
224
235
  async cleanup() {
236
+ this.localReadObservations();
237
+ this.toolEndpoint = undefined;
225
238
  const tools = this.tools;
226
239
  const handle = this.handle;
227
240
  this.tools = undefined;
@@ -19,6 +19,7 @@ export class CodexAdapter {
19
19
  started = false;
20
20
  receipt;
21
21
  terminalAudit;
22
+ localReadObservations() { return this.usingOfficialExec ? this.ownedExec?.localReadObservations() : undefined; }
22
23
  securityReceipt() { return this.terminalAudit ? structuredClone(this.terminalAudit) : this.usingOfficialExec ? this.ownedExec?.receipt() : this.receipt ? structuredClone(this.receipt) : undefined; }
23
24
  cancelled = false;
24
25
  usingOfficialExec = false;
@@ -1,6 +1,6 @@
1
1
  import { performance } from 'node:perf_hooks';
2
2
  import { PreparationClock, CODEX_PREPARATION_BUDGET_MS } from '../../safety/preparation-timing.js';
3
- import { localEndpointObservations } from '../../safety/local-tool-server.js';
3
+ import { localEndpointObservations, localEndpointReadObservations } from '../../safety/local-tool-server.js';
4
4
  import { processStartupObservations, freezeProcessLaunchObservation } from '../../process/node-process-group-host.js';
5
5
  import { executionError } from '../../safety/execution-failure.js';
6
6
  import { UNMANAGED_EXEC_MODE } from './unmanaged-scope.js';
@@ -24,6 +24,7 @@ export class CodexExecLaunchHost {
24
24
  handle;
25
25
  inspection;
26
26
  exitAudit;
27
+ readAudit;
27
28
  inspectionFingerprint;
28
29
  handshake;
29
30
  receiptValue;
@@ -105,7 +106,7 @@ export class CodexExecLaunchHost {
105
106
  this.inspectionFingerprint = hash(this.inspection.snapshot);
106
107
  this.current();
107
108
  const invocation = requireOfficialExecPlan(this.plan);
108
- const manual = ctx.approvedTaskPackage?.security_requirements?.mdm_verification === 'manual_prelaunch';
109
+ const manual = ctx.approvedTaskPackage?.security_requirements?.mdm_verification !== undefined;
109
110
  if (manual && typeof this.inspection.beforeSpawn !== 'function')
110
111
  throw fail();
111
112
  assertPreparationBudget();
@@ -233,9 +234,22 @@ export class CodexExecLaunchHost {
233
234
  catch {
234
235
  return undefined;
235
236
  } }
237
+ localReadObservations() {
238
+ if (!this.stopped) {
239
+ try {
240
+ const token = requireOfficialExecPlan(this.plan).env.AI_GATEWAY_JOB_MCP_TOKEN;
241
+ const current = token ? localEndpointReadObservations(this.plan.endpoint, token) : undefined;
242
+ if (current)
243
+ this.readAudit = current;
244
+ }
245
+ catch { /* Absent observation is never interpreted as successful reads. */ }
246
+ }
247
+ return this.readAudit ? structuredClone(this.readAudit) : undefined;
248
+ }
236
249
  cancel() {
237
250
  if (this.closePromise)
238
251
  return this.closePromise;
252
+ this.localReadObservations();
239
253
  this.stopped = true;
240
254
  this.receiptValue = undefined;
241
255
  if (!this.ownsAttempt) {
@@ -1,3 +1,4 @@
1
+ import { PRIVILEGED_MDM_MODE, PRIVILEGED_MDM_RISK, validPolicyObserverPin } from './prelaunch-policy.js';
1
2
  import { MANUAL_MDM_RISK } from './manual-policy-scope.js';
2
3
  import { UNMANAGED_EXEC_MODE, UNMANAGED_SCOPE, requirePolicyScope } from './unmanaged-scope.js';
3
4
  import { isDeepStrictEqual } from 'node:util';
@@ -6,13 +7,13 @@ export const EXEC_RELEASE_GATES = Object.freeze(['binary_release', 'official_per
6
7
  export const EXEC_INPUT_GATES = Object.freeze(['exact_inputs', 'launch_binding', 'repository_scope', 'private_paths_environment', 'owned_lifecycle']);
7
8
  export const EXEC_RELEASE_RISKS = Object.freeze(['vendor_native_policy_enforcement', 'managed_policy_not_directly_observed_per_job', 'first_inference_order_not_observed_per_job', 'job_mcp_token_possession_not_os_identity', 'account_startup_may_refresh_shared_credentials']);
8
9
  export const releaseGates = (mode) => mode === UNMANAGED_EXEC_MODE ? EXEC_RELEASE_GATES.map(g => g === 'managed_policy_enforcement' ? 'managed_scope_exclusion' : g) : EXEC_RELEASE_GATES;
9
- export const releaseRisks = (mode, mdmVerification) => mode === UNMANAGED_EXEC_MODE ? [...EXEC_RELEASE_RISKS, 'concurrent_policy_change_not_atomic', 'local_account_metadata_not_remote_entitlement', ...(mdmVerification === 'manual_prelaunch' ? [MANUAL_MDM_RISK] : [])] : EXEC_RELEASE_RISKS;
10
+ export const releaseRisks = (mode, mdmVerification) => mode === UNMANAGED_EXEC_MODE ? [...EXEC_RELEASE_RISKS, 'concurrent_policy_change_not_atomic', 'local_account_metadata_not_remote_entitlement', ...(mdmVerification === 'manual_prelaunch' ? [MANUAL_MDM_RISK] : mdmVerification === PRIVILEGED_MDM_MODE ? [PRIVILEGED_MDM_RISK] : [])] : EXEC_RELEASE_RISKS;
10
11
  const record = (v) => !!v && typeof v === 'object' && !Array.isArray(v);
11
12
  const exact = (v, keys) => isDeepStrictEqual(Object.keys(v).sort(), keys.sort());
12
13
  const checks = (v, gates, basis) => Array.isArray(v) && v.length === gates.length && gates.every(g => v.filter(c => record(c) && exact(c, ['gate', 'status', 'basis']) && c.gate === g && c.status === 'verified' && c.basis === basis).length === 1);
13
14
  export function requireReleaseDescriptor(r) {
14
15
  const v3 = r.security_mode === UNMANAGED_EXEC_MODE;
15
- if (!['release_manifest_sha256', 'binary_sha256', 'fixtures_sha256', 'commands_sha256', 'tools_sha256', 'fingerprint'].every(k => typeof r[k] === 'string' && /^[a-f0-9]{64}$/.test(r[k])) || !Array.isArray(r.model_ids) || !r.model_ids.length || new Set(r.model_ids).size !== r.model_ids.length || r.model_ids.some(m => typeof m !== 'string' || !m || m.length > 255 || /[\r\n\0]/.test(m)) || !exact(r, ['version', 'security_mode', 'transport', 'status', 'adapter_id', 'adapter_version', 'mandatory_gates', 'accepted_residual_risks', 'evidence_class', 'release_manifest_sha256', 'binary_sha256', 'fixtures_sha256', 'commands_sha256', 'tools_sha256', 'model_ids', 'fingerprint', ...(v3 ? ['policy_scope', 'managed_enforcement', ...(r.mdm_verification === undefined ? [] : ['mdm_verification'])] : [])]) || r.version !== (v3 ? 'cli-exec-security-requirements-v3' : 'cli-exec-security-requirements-v2') || !isReleaseExecMode(r.security_mode) || v3 && (r.policy_scope !== UNMANAGED_SCOPE || r.managed_enforcement !== 'not_run' || r.mdm_verification !== undefined && r.mdm_verification !== 'manual_prelaunch') || r.evidence_class !== 'reviewed_release_and_current_inputs' || !isDeepStrictEqual(r.mandatory_gates, [...releaseGates(r.security_mode), ...EXEC_INPUT_GATES, 'authenticated_mcp_discovery']) || !isDeepStrictEqual(r.accepted_residual_risks, releaseRisks(r.security_mode, r.mdm_verification)))
16
+ if (!['release_manifest_sha256', 'binary_sha256', 'fixtures_sha256', 'commands_sha256', 'tools_sha256', 'fingerprint'].every(k => typeof r[k] === 'string' && /^[a-f0-9]{64}$/.test(r[k])) || !Array.isArray(r.model_ids) || !r.model_ids.length || new Set(r.model_ids).size !== r.model_ids.length || r.model_ids.some(m => typeof m !== 'string' || !m || m.length > 255 || /[\r\n\0]/.test(m)) || !exact(r, ['version', 'security_mode', 'transport', 'status', 'adapter_id', 'adapter_version', 'mandatory_gates', 'accepted_residual_risks', 'evidence_class', 'release_manifest_sha256', 'binary_sha256', 'fixtures_sha256', 'commands_sha256', 'tools_sha256', 'model_ids', 'fingerprint', ...(v3 ? ['policy_scope', 'managed_enforcement', ...(r.mdm_verification === undefined ? [] : ['mdm_verification']), ...(r.mdm_verification === PRIVILEGED_MDM_MODE ? ['policy_observer'] : [])] : [])]) || r.version !== (v3 ? 'cli-exec-security-requirements-v3' : 'cli-exec-security-requirements-v2') || !isReleaseExecMode(r.security_mode) || v3 && (r.policy_scope !== UNMANAGED_SCOPE || r.managed_enforcement !== 'not_run' || r.mdm_verification !== undefined && r.mdm_verification !== 'manual_prelaunch' && r.mdm_verification !== PRIVILEGED_MDM_MODE) || r.mdm_verification === PRIVILEGED_MDM_MODE && !validPolicyObserverPin(r.policy_observer) || r.evidence_class !== 'reviewed_release_and_current_inputs' || !isDeepStrictEqual(r.mandatory_gates, [...releaseGates(r.security_mode), ...EXEC_INPUT_GATES, 'authenticated_mcp_discovery']) || !isDeepStrictEqual(r.accepted_residual_risks, releaseRisks(r.security_mode, r.mdm_verification)))
16
17
  throw Error('EXEC_RELEASE_DESCRIPTOR_REJECTED');
17
18
  }
18
19
  /** Diagnostic validator only; live currentness and trusted dependency origin
@@ -28,7 +29,7 @@ export function assessReleaseExec(value, plan) {
28
29
  return { ready: false, reasonCodes: ['EXEC_RELEASE_EVIDENCE_UNAVAILABLE', ...EXEC_RELEASE_GATES.map(g => 'EXEC_RELEASE_' + g.toUpperCase() + '_UNVERIFIED'), ...EXEC_INPUT_GATES.map(g => 'EXEC_INPUT_' + g.toUpperCase() + '_UNVERIFIED')] };
29
30
  need(isReleaseExecMode(plan.mode) && exact(value, ['version', 'securityMode', 'transport', 'executionNonce', 'identity', 'invocationFingerprint', 'acceptedResidualRisks', 'release', 'inputs', 'warnings', ...(v3 ? ['policyScope'] : [])]) && value.version === (v3 ? 'cli-exec-release-evidence-v3' : 'cli-exec-release-evidence-v2') && value.securityMode === plan.mode && value.transport === EXEC_TRANSPORT, 'EXEC_RELEASE_EVIDENCE_SHAPE');
30
31
  need(value.executionNonce === plan.executionNonce && value.invocationFingerprint === plan.invocationFingerprint && isDeepStrictEqual(value.identity, plan.identity), 'EXEC_RELEASE_IDENTITY_MISMATCH');
31
- need(isDeepStrictEqual(value.acceptedResidualRisks, releaseRisks(plan.mode, record(value.policyScope) && record(value.policyScope.manual_verification) ? value.policyScope.manual_verification.mode : undefined)), 'EXEC_RELEASE_RISK_MISMATCH');
32
+ need(isDeepStrictEqual(value.acceptedResidualRisks, releaseRisks(plan.mode, record(value.policyScope) && record(value.policyScope.manual_verification) ? value.policyScope.manual_verification.mode : record(value.policyScope) && record(value.policyScope.privileged_verification) ? value.policyScope.privileged_verification.mode : undefined)), 'EXEC_RELEASE_RISK_MISMATCH');
32
33
  need(Array.isArray(value.warnings) && value.warnings.length === 0, 'EXEC_RELEASE_WARNING');
33
34
  const r = record(value.release) ? value.release : {};
34
35
  need(exact(r, ['source', 'binarySha256', 'manifestFingerprint', 'fixturesFingerprint', 'checks']) && r.source === 'reviewed_compiled_release' && r.binarySha256 === plan.identity.binarySha256 && r.manifestFingerprint === plan.identity.releaseManifestFingerprint && r.fixturesFingerprint === plan.identity.barrierFingerprint, 'EXEC_RELEASE_PIN_MISMATCH');
@@ -1,13 +1,18 @@
1
+ import { PRIVILEGED_MDM_MODE, validPolicyObserverPin } from './prelaunch-policy.js';
1
2
  import { isDeepStrictEqual } from 'node:util';
2
3
  import { assessMdmInventory } from '../../safety/codex-native-observation.js';
3
4
  import { securityRequirementsFingerprint as hash } from '../../safety/security-requirements.js';
4
5
  import { classifyUnmanagedEnvironment } from './unmanaged-scope.js';
5
6
  const denied = () => Error('CODEX_MANUAL_POLICY_REJECTED');
6
7
  export const MANUAL_MDM_RISK = 'manual_prelaunch_mdm_snapshot_not_continuously_reobserved';
7
- /** Trusted in-process controller only, never a task/env/receipt deserializer.
8
- * Administrator provenance belongs to that controller. Arbitrary JSON is not
9
- * OS attestation. This module cannot query profiles or create elevated code. */
10
- export function createManualPolicyScopeVerifier(input) {
8
+ export function createManualPolicyScopeVerifier(input) { return createPrelaunchVerifier(input); }
9
+ /** Trusted caller must obtain metadata from the pinned peer-fenced native client. */
10
+ export function createPrivilegedPolicyScopeVerifier(input, observer) {
11
+ if (!validPolicyObserverPin(observer))
12
+ throw denied();
13
+ return createPrelaunchVerifier(input, structuredClone(observer));
14
+ }
15
+ function createPrelaunchVerifier(input, observer) {
11
16
  const binding = structuredClone(input.binding), inventory = structuredClone(input.inventory), lease = input.leaseDigest, read = input.readLocal, now = input.now ?? Date.now;
12
17
  const identityKeys = ['jobId', 'generation', 'actorId', 'runnerId', 'sourceSha', 'binarySha256', 'releaseManifestFingerprint', 'requirementsFingerprint', 'contractFingerprint', 'taskPackageFingerprint', 'launchFingerprint', 'commandsFingerprint', 'toolSchemaFingerprint', 'barrierFingerprint'];
13
18
  if (!isDeepStrictEqual(Object.keys(binding).sort(), identityKeys.sort()) || !Number.isSafeInteger(binding.generation) || binding.generation < 1 || !['jobId', 'actorId', 'runnerId'].every(k => typeof binding[k] === 'string' && binding[k].length > 0 && binding[k].length <= 256 && !/[\r\n\0]/.test(binding[k])) || !/^[a-f0-9]{40}$/.test(binding.sourceSha) || !['binarySha256', 'releaseManifestFingerprint', 'requirementsFingerprint', 'contractFingerprint', 'taskPackageFingerprint', 'launchFingerprint', 'commandsFingerprint', 'toolSchemaFingerprint', 'barrierFingerprint'].every(k => /^[a-f0-9]{64}$/.test(binding[k])) || !/^[a-f0-9]{64}$/.test(lease))
@@ -47,8 +52,8 @@ export function createManualPolicyScopeVerifier(input) {
47
52
  catch {
48
53
  dead = true;
49
54
  } return !dead; };
50
- const verification = Object.freeze({ mode: 'manual_prelaunch', observedAt: inventory.observedAt, inventoryFingerprint: inventory.inventoryFingerprint, continuousMdmMonitoring: false, bindingFingerprint: hash({ ...binding, leaseDigest: lease }) });
51
- return Object.freeze({ sourcesFingerprint: first.sourcesFingerprint, accountFingerprint: first.accountFingerprint, accountRootFingerprint: first.accountRootFingerprint, observedAt: inventory.observedAt, manualVerification: verification, isCurrent: current,
55
+ const verification = Object.freeze({ mode: observer ? PRIVILEGED_MDM_MODE : 'manual_prelaunch', ...(observer ? { observer } : {}), observedAt: inventory.observedAt, inventoryFingerprint: inventory.inventoryFingerprint, continuousMdmMonitoring: false, bindingFingerprint: hash({ ...binding, leaseDigest: lease }) });
56
+ return Object.freeze({ sourcesFingerprint: first.sourcesFingerprint, accountFingerprint: first.accountFingerprint, accountRootFingerprint: first.accountRootFingerprint, observedAt: inventory.observedAt, ...(observer ? { privilegedVerification: verification } : { manualVerification: verification }), isCurrent: current,
52
57
  beforeSpawn(identity, leaseDigest) {
53
58
  if (started || spent || !isDeepStrictEqual(identity, binding) || leaseDigest !== lease || !current()) {
54
59
  dead = true;
@@ -0,0 +1,9 @@
1
+ /** Compiled observer identity, never a task-selected executable or command. */
2
+ export const PRIVILEGED_MDM_MODE = 'privileged_prelaunch';
3
+ export const PRIVILEGED_MDM_RISK = 'privileged_prelaunch_mdm_snapshot_not_continuously_reobserved';
4
+ export function validPolicyObserverPin(v) {
5
+ if (!v || typeof v !== 'object' || Array.isArray(v))
6
+ return false;
7
+ const p = v;
8
+ return Object.keys(p).sort().join(',') === 'protocol,sha256' && p.protocol === 'policy-inventory-v1' && typeof p.sha256 === 'string' && /^[a-f0-9]{64}$/.test(p.sha256);
9
+ }
@@ -1,3 +1,4 @@
1
+ import { PRIVILEGED_MDM_MODE, validPolicyObserverPin } from './prelaunch-policy.js';
1
2
  import { isDeepStrictEqual } from 'node:util';
2
3
  import { securityRequirementsFingerprint as hash } from '../../safety/security-requirements.js';
3
4
  export const UNMANAGED_EXEC_MODE = 'supported_cli_exec_release_policy_v3';
@@ -68,18 +69,26 @@ export function policyScopeReceipt(plan, leaseDigest, observation) {
68
69
  if (accountRootFingerprint !== observation.accountRootFingerprint)
69
70
  throw denied();
70
71
  const manual = observation.manualVerification;
71
- const value = { ...(manual ? { manual_verification: manual } : {}), version: 'codex-policy-scope-v1', scope: UNMANAGED_SCOPE, classification: 'unmanaged_personal', managed_enforcement: 'not_run', managed_hosts: 'unsupported', sources_fingerprint: observation.sourcesFingerprint, account_fingerprint: observation.accountFingerprint, account_root_fingerprint: accountRootFingerprint, observed_at: observation.observedAt, lease_digest: leaseDigest, binding_fingerprint: hash(scopeBinding(plan, leaseDigest, accountRootFingerprint, observation.sourcesFingerprint, observation.accountFingerprint)) };
72
+ const privileged = observation.privilegedVerification;
73
+ const value = { ...(manual ? { manual_verification: manual } : {}), ...(privileged ? { privileged_verification: privileged } : {}), version: 'codex-policy-scope-v1', scope: UNMANAGED_SCOPE, classification: 'unmanaged_personal', managed_enforcement: 'not_run', managed_hosts: 'unsupported', sources_fingerprint: observation.sourcesFingerprint, account_fingerprint: observation.accountFingerprint, account_root_fingerprint: accountRootFingerprint, observed_at: observation.observedAt, lease_digest: leaseDigest, binding_fingerprint: hash(scopeBinding(plan, leaseDigest, accountRootFingerprint, observation.sourcesFingerprint, observation.accountFingerprint)) };
72
74
  return { ...value, fingerprint: hash(value) };
73
75
  }
74
76
  /** Audit shape validation is not OS attestation; live trusted currentness must
75
77
  * additionally be supplied by the owning host before spawn and every tool. */
76
78
  export function requirePolicyScope(value, plan) {
79
+ if (value && typeof value === 'object' && 'manual_verification' in value && 'privileged_verification' in value)
80
+ throw denied();
77
81
  if (value && typeof value === 'object' && 'manual_verification' in value) {
78
82
  const m = value.manual_verification;
79
83
  if (!exact(m, ['mode', 'observedAt', 'inventoryFingerprint', 'continuousMdmMonitoring', 'bindingFingerprint']) || m.mode !== 'manual_prelaunch' || m.continuousMdmMonitoring !== false || !Number.isSafeInteger(m.observedAt) || m.observedAt < 0 || m.observedAt !== value.observed_at || !digest(m.inventoryFingerprint) || m.bindingFingerprint !== hash({ ...plan.identity, leaseDigest: value.lease_digest }))
80
84
  throw denied();
81
85
  }
82
- if (!exact(value, ['version', 'scope', 'classification', 'managed_enforcement', 'managed_hosts', 'sources_fingerprint', 'account_fingerprint', 'account_root_fingerprint', 'observed_at', 'lease_digest', 'binding_fingerprint', 'fingerprint', ...(value && typeof value === 'object' && 'manual_verification' in value ? ['manual_verification'] : [])]) || value.version !== 'codex-policy-scope-v1' || value.scope !== UNMANAGED_SCOPE || value.classification !== 'unmanaged_personal' || value.managed_enforcement !== 'not_run' || value.managed_hosts !== 'unsupported' || !Number.isSafeInteger(value.observed_at) || value.observed_at < 0
86
+ if (value && typeof value === 'object' && 'privileged_verification' in value) {
87
+ const m = value.privileged_verification;
88
+ if (!exact(m, ['mode', 'observedAt', 'inventoryFingerprint', 'continuousMdmMonitoring', 'bindingFingerprint', 'observer']) || m.mode !== PRIVILEGED_MDM_MODE || !validPolicyObserverPin(m.observer) || m.continuousMdmMonitoring !== false || !Number.isSafeInteger(m.observedAt) || m.observedAt < 0 || m.observedAt !== value.observed_at || !digest(m.inventoryFingerprint) || m.bindingFingerprint !== hash({ ...plan.identity, leaseDigest: value.lease_digest }))
89
+ throw denied();
90
+ }
91
+ if (!exact(value, ['version', 'scope', 'classification', 'managed_enforcement', 'managed_hosts', 'sources_fingerprint', 'account_fingerprint', 'account_root_fingerprint', 'observed_at', 'lease_digest', 'binding_fingerprint', 'fingerprint', ...(value && typeof value === 'object' && 'manual_verification' in value ? ['manual_verification'] : []), ...(value && typeof value === 'object' && 'privileged_verification' in value ? ['privileged_verification'] : [])]) || value.version !== 'codex-policy-scope-v1' || value.scope !== UNMANAGED_SCOPE || value.classification !== 'unmanaged_personal' || value.managed_enforcement !== 'not_run' || value.managed_hosts !== 'unsupported' || !Number.isSafeInteger(value.observed_at) || value.observed_at < 0
83
92
  || !['sources_fingerprint', 'account_fingerprint', 'account_root_fingerprint', 'lease_digest', 'binding_fingerprint', 'fingerprint'].every(k => digest(value[k])) || hash(value) !== value.fingerprint || hash(scopeBinding(plan, value.lease_digest, value.account_root_fingerprint, value.sources_fingerprint, value.account_fingerprint)) !== value.binding_fingerprint || hash({ accountRoot: plan.accountRoot }) !== value.account_root_fingerprint)
84
93
  throw denied();
85
94
  }
@@ -116,10 +116,11 @@ export class ExecutionCoordinator {
116
116
  throw new Error("Binding outcome requires reconciliation");
117
117
  }
118
118
  // Retain this exact safe terminal payload until Gateway acknowledges it.
119
+ const completionRequest = { jobId: job.id, generation: job.generation, leaseToken: job.leaseToken,
120
+ status: "blocked", result: { preparation_failure: { ...diagnostic } } };
119
121
  await this.deps.state.save({ jobId: job.id, generation: job.generation, leaseToken: job.leaseToken,
120
- phase: "preparation_failed", preparationFailure: diagnostic });
121
- await this.deps.gateway.complete({ jobId: job.id, generation: job.generation, leaseToken: job.leaseToken,
122
- status: "blocked", result: { preparation_failure: { ...diagnostic } } });
122
+ phase: "terminal_pending", preparationFailure: diagnostic, completionRequest: structuredClone(completionRequest) });
123
+ await this.deps.gateway.complete(completionRequest);
123
124
  await this.deps.state.clear();
124
125
  return;
125
126
  }
@@ -183,6 +184,16 @@ export class ExecutionCoordinator {
183
184
  const evidence = this.deps.terminalEvidenceCollector.collect({ checkoutPath: workspace.path, baseSha: workspace.baseCommitSha });
184
185
  payload = { ...payload, diff_hash: evidence.diffHash, changed_file_manifest_hash: evidence.changedFileManifestHash };
185
186
  }
187
+ // A vendor report cannot supply host observations, even when its shape looks valid.
188
+ if (payload) {
189
+ payload = { ...payload };
190
+ delete payload.local_read_observations;
191
+ }
192
+ if (isTerminalEventType(event.eventType) || event.eventType === 'blocked' || event.eventType === 'scope_conflict') {
193
+ const reads = safeLocalReadObservations(adapter.localReadObservations?.());
194
+ if (reads)
195
+ payload = { ...payload, local_read_observations: reads };
196
+ }
186
197
  const normalized = {
187
198
  protocolVersion: PROTOCOL_VERSION, bindingId: binding.bindingId,
188
199
  sequence: sequence + 1, eventType: event.eventType,
@@ -272,7 +283,14 @@ export class ExecutionCoordinator {
272
283
  const diagnostic = status === "failed" || terminal.eventType === "blocked" ? completionReportDiagnostic(terminal.payload?.completion_report) : undefined;
273
284
  const reportDiagnostic = diagnostic && (terminal.eventType === "blocked"
274
285
  ? diagnostic.code === "CODEX_COMPLETION_REPORT_BLOCKED" : diagnostic.code !== "CODEX_COMPLETION_REPORT_BLOCKED") ? diagnostic : undefined;
275
- await this.deps.gateway.complete({ jobId: job.id, generation: job.generation, leaseToken: job.leaseToken, status, result: { binding_id: binding.bindingId, event_type: terminal.eventType, summary: terminal.summary, ...(failureDiagnostic ? { execution_failure: { ...failureDiagnostic } } : {}), ...(reportDiagnostic ? { completion_report: reportDiagnostic } : {}), ...(adapter.securityReceipt?.() ? { security_receipt: adapter.securityReceipt() } : {}) } });
286
+ const securityReceipt = adapter.securityReceipt?.();
287
+ const reads = safeLocalReadObservations(adapter.localReadObservations?.());
288
+ const completionRequest = structuredClone({ jobId: job.id, generation: job.generation, leaseToken: job.leaseToken, status, result: { binding_id: binding.bindingId, event_type: terminal.eventType, summary: terminal.summary, ...(reads ? { local_read_observations: reads } : {}), ...(failureDiagnostic ? { execution_failure: { ...failureDiagnostic } } : {}), ...(reportDiagnostic ? { completion_report: reportDiagnostic } : {}), ...(securityReceipt ? { security_receipt: securityReceipt } : {}) } });
289
+ await this.deps.state.save({ jobId: job.id, generation: job.generation, leaseToken: job.leaseToken,
290
+ phase: "terminal_pending", bindingId: binding.bindingId, terminalEventType: terminal.eventType,
291
+ completionRequest: structuredClone(completionRequest) });
292
+ await this.deps.gateway.complete(completionRequest);
276
293
  await this.deps.state.clear();
277
294
  }
278
295
  }
296
+ import { safeLocalReadObservations } from '../safety/local-read-observations.js';
@@ -1,4 +1,6 @@
1
- import { createCodexRuntimeHost } from '../safety/codex-runtime-host.js';
1
+ import { createCodexDaemonHost } from '../safety/codex-daemon-host.js';
2
+ import { reconcileTerminalState } from './terminal-recovery.js';
3
+ import { packageVersion } from '../package-version.js';
2
4
  import { hostname } from "node:os";
3
5
  import { createHash } from "node:crypto";
4
6
  import { join } from "node:path";
@@ -21,7 +23,7 @@ import { packagedHookCommandParts } from "../safety/hook-command.js";
21
23
  export async function buildDaemon(input) {
22
24
  if (input.codexSupportedProbe && !input.codexSecurityHost)
23
25
  throw new Error('CODEX_SUPPORTED_HOST_REQUIRED');
24
- const production = !input.codexSecurityHost && !input.codexSupportedProbe ? createCodexRuntimeHost({ runnerId: input.runnerId, binary: input.env.AI_GATEWAY_LOCAL_RUNNER_CODEX_BINARY ?? 'codex', home: input.env.HOME ?? '', codexHome: input.env.CODEX_HOME, searchPath: input.env.PATH }) : undefined;
26
+ const production = !input.codexSecurityHost && !input.codexSupportedProbe ? createCodexDaemonHost({ runnerId: input.runnerId, binary: input.env.AI_GATEWAY_LOCAL_RUNNER_CODEX_BINARY ?? 'codex', home: input.env.HOME ?? '', codexHome: input.env.CODEX_HOME, searchPath: input.env.PATH }) : undefined;
25
27
  let pendingCodexClaim;
26
28
  const gateway = new McpGatewayClient({ mcpUrl: input.gatewayMcpUrl, tokenProvider: input.tokenProvider, runnerId: input.runnerId, fetchFn: input.fetchFn });
27
29
  // The same parts the CLI self-test ran before starting this daemon (verifyPackagedHook).
@@ -62,7 +64,8 @@ export async function buildDaemon(input) {
62
64
  if (renewed?.job) {
63
65
  if (renewed.job.id !== job.id || renewed.job.generation !== job.generation)
64
66
  throw new Error('CODEX_JOB_LEASE_FENCE_MISMATCH');
65
- production?.heartbeat(job.id, job.generation, renewed.job.lease_expires_at);
67
+ if (job.taskPackage.runtime_adapter === 'codex_cli')
68
+ production?.heartbeat(job.id, job.generation, renewed.job.lease_expires_at);
66
69
  }
67
70
  },
68
71
  complete: async (result) => { await gateway.completeLocalJob(result); await production?.releaseJob(result.jobId); pendingCodexClaim = undefined; },
@@ -80,12 +83,29 @@ export async function buildDaemon(input) {
80
83
  });
81
84
  const poller = new JobPoller({
82
85
  reportError: (diagnostic) => { process.stderr.write(`${JSON.stringify({ event: "poller_failed", ...diagnostic })}\n`); },
83
- heartbeat: () => gateway.heartbeatLocalRunner({
84
- deviceFingerprint: createHash("sha256").update(`${process.getuid?.() ?? 0}|${runnerDisplayName()}|${input.gatewayMcpUrl}`).digest("hex"),
85
- displayName: runnerDisplayName(), packageVersion: "0.1.11",
86
- capabilities: probes.map((probe) => ({ adapter: probe.id, version: probe.version,
87
- capabilities: probe.capabilities, authenticated: probe.available && probe.authenticated })),
88
- }).then(() => undefined),
86
+ heartbeat: async () => {
87
+ let current;
88
+ try {
89
+ current = await codex.probe();
90
+ }
91
+ catch {
92
+ current = { available: false, authenticated: false, capabilities: [] };
93
+ }
94
+ const ready = current.available && current.authenticated && !!current.executableVersion && codex.capabilities.every(c => current.capabilities.includes(c));
95
+ const index = probes.findIndex(p => p.id === 'codex_cli');
96
+ if (index >= 0)
97
+ probes[index] = { id: 'codex_cli', version: '1', ...current, authenticated: ready };
98
+ if (ready)
99
+ versions.codex_cli = '1';
100
+ else
101
+ delete versions.codex_cli;
102
+ await gateway.heartbeatLocalRunner({
103
+ deviceFingerprint: createHash("sha256").update(`${process.getuid?.() ?? 0}|${runnerDisplayName()}|${input.gatewayMcpUrl}`).digest("hex"),
104
+ displayName: runnerDisplayName(), packageVersion: packageVersion(),
105
+ capabilities: probes.map((probe) => ({ adapter: probe.id, version: probe.version,
106
+ capabilities: probe.capabilities, authenticated: probe.available && probe.authenticated })),
107
+ });
108
+ },
89
109
  wait: async () => {
90
110
  const claim = await gateway.waitForLocalJob(versions, 25);
91
111
  if (claim.status !== "claimed" || !claim.job || !claim.task_package || !claim.lease_token || !claim.launch_grant)
@@ -110,26 +130,12 @@ export async function buildDaemon(input) {
110
130
  lock: new ProcessLock(join(input.stateRoot, "daemon.lock")), state,
111
131
  reconcile: async (stored) => {
112
132
  const previous = stored;
133
+ if (await reconcileTerminalState(previous, { gateway: { complete: request => gateway.completeLocalJob(request) }, state }))
134
+ return;
113
135
  if (await reconcilePreparationState(previous, {
114
136
  complete: (result) => gateway.completeLocalJob(result), clear: () => state.clear(),
115
137
  }))
116
138
  return;
117
- if (previous.bindingId && previous.jobId && previous.generation && previous.leaseToken) {
118
- try {
119
- const receipt = await gateway.readBindingEvents(previous.bindingId, previous.eventCursor ?? 0);
120
- const status = receipt.terminal
121
- ? (receipt.terminalEventType ?? previous.terminalEventType) === "completed" ? "completed"
122
- : (receipt.terminalEventType ?? previous.terminalEventType) === "cancelled" ? "cancelled" : "failed"
123
- : "failed";
124
- await gateway.completeLocalJob({
125
- jobId: previous.jobId, generation: previous.generation, leaseToken: previous.leaseToken, status,
126
- result: { binding_id: previous.bindingId, recovery: receipt.terminal ? "terminal_event_reconciled" : "daemon_restarted_before_terminal_event" },
127
- });
128
- }
129
- catch {
130
- // An expired generation is safely fenced by Gateway and will be reclaimed.
131
- }
132
- }
133
139
  await state.clear();
134
140
  },
135
141
  poller,
@@ -0,0 +1,59 @@
1
+ import { isTerminalEventType } from '../protocol.js';
2
+ function record(value) {
3
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
4
+ }
5
+ function nonempty(value) { return typeof value === 'string' && value.trim().length > 0; }
6
+ function invalid() { throw new Error('Terminal outcome requires reconciliation: invalid pending completion receipt'); }
7
+ /** Replay only the stored completion request; never restart an adapter or reconstruct evidence. */
8
+ export async function reconcileTerminalState(previous, deps) {
9
+ if (previous === null || previous === undefined)
10
+ return false;
11
+ if (!record(previous))
12
+ invalid();
13
+ if (previous.phase !== 'terminal_pending') {
14
+ if (!['claimed', 'shadow', 'binding', 'binding_uncertain', 'preparation_failed'].includes(String(previous.phase))
15
+ || !nonempty(previous.jobId) || !Number.isSafeInteger(previous.generation) || previous.generation < 1
16
+ || !nonempty(previous.leaseToken))
17
+ invalid();
18
+ // Only preparation states without execution evidence may reach the legacy
19
+ // preparation handler (whose caller may clear an unhandled receipt).
20
+ const allowed = ['phase', 'jobId', 'generation', 'leaseToken', 'workspaceManifest'];
21
+ if (previous.phase === 'preparation_failed' || previous.phase === 'binding_uncertain')
22
+ allowed.push('preparationFailure');
23
+ if (Object.keys(previous).some(key => !allowed.includes(key)))
24
+ invalid();
25
+ if (previous.phase === 'preparation_failed' || previous.preparationFailure !== undefined) {
26
+ const diagnostic = previous.preparationFailure;
27
+ if (!record(diagnostic) || !nonempty(diagnostic.stage) || !nonempty(diagnostic.code)
28
+ || Object.keys(diagnostic).some(key => !['stage', 'code', 'tool'].includes(key))
29
+ || (diagnostic.tool !== undefined && !nonempty(diagnostic.tool)))
30
+ invalid();
31
+ }
32
+ return false;
33
+ }
34
+ const request = previous.completionRequest;
35
+ if (!record(request) || Object.keys(request).sort().join(',') !== 'generation,jobId,leaseToken,result,status'
36
+ || !nonempty(previous.jobId) || !Number.isSafeInteger(previous.generation) || previous.generation < 1
37
+ || !nonempty(previous.leaseToken) || request.jobId !== previous.jobId
38
+ || request.generation !== previous.generation || request.leaseToken !== previous.leaseToken || !record(request.result))
39
+ invalid();
40
+ const result = request.result;
41
+ if (previous.bindingId !== undefined) {
42
+ const eventType = result.event_type;
43
+ if (!nonempty(previous.bindingId) || result.binding_id !== previous.bindingId || typeof eventType !== 'string'
44
+ || (!isTerminalEventType(eventType) && eventType !== 'blocked' && eventType !== 'scope_conflict') || previous.terminalEventType !== eventType || typeof result.summary !== 'string')
45
+ invalid();
46
+ const status = eventType === 'completed' ? 'completed' : eventType === 'cancelled' ? 'cancelled'
47
+ : eventType === 'blocked' || eventType === 'scope_conflict' ? 'blocked' : 'failed';
48
+ if (request.status !== status)
49
+ invalid();
50
+ }
51
+ else if (request.status !== 'blocked' || !record(result.preparation_failure)
52
+ || result.binding_id !== undefined || result.event_type !== undefined || previous.terminalEventType !== undefined
53
+ || !nonempty(result.preparation_failure.stage) || !nonempty(result.preparation_failure.code))
54
+ invalid();
55
+ // Snapshot the validated receipt so a transport cannot mutate retained recovery state.
56
+ await deps.gateway.complete(structuredClone(request));
57
+ await deps.state.clear();
58
+ return true;
59
+ }
@@ -0,0 +1,23 @@
1
+ import { readFileSync } from 'node:fs';
2
+ // Both the source tree (src/) and packaged build (dist/src/) resolve their own metadata.
3
+ export function packageVersion(moduleUrl = import.meta.url) {
4
+ const semver = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-(?:(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
5
+ for (const relative of ['../package.json', '../../package.json']) {
6
+ let metadata;
7
+ try {
8
+ metadata = JSON.parse(readFileSync(new URL(relative, moduleUrl), 'utf8'));
9
+ }
10
+ catch (error) {
11
+ if (error.code === 'ENOENT')
12
+ continue;
13
+ throw new Error('Runner package version metadata is invalid');
14
+ }
15
+ if (metadata && typeof metadata === 'object' && metadata.name === 'mtech-ai-local-runner') {
16
+ const version = metadata.version;
17
+ if (typeof version === 'string' && semver.test(version))
18
+ return version;
19
+ throw new Error('Runner package version metadata is invalid');
20
+ }
21
+ }
22
+ throw new Error('Runner package version metadata is missing');
23
+ }
@@ -0,0 +1,101 @@
1
+ import { CODEX_DAEMON_EXEC_RELEASES } from './codex-daemon-releases.js';
2
+ import { registeredPrelaunchDependencies, CODEX_MANUAL_BINARY } from './codex-manual-registered.js';
3
+ import { createPrivilegedDaemonController } from './codex-manual-controller.js';
4
+ import { createPolicyInventoryClient, verifyPolicyObserver } from './policy-inventory-client.js';
5
+ import { PRIVILEGED_MDM_MODE } from '../adapters/codex/prelaunch-policy.js';
6
+ import { buildCodexExecJobOptions } from '../adapters/codex/exec-runtime-options.js';
7
+ import { reviewedCodexExecRelease } from './codex-exec-production-host.js';
8
+ /** Compiled/trusted host composition. No environment, task or saved JSON creates
9
+ * an admission capability. A missing reviewed release stays unavailable. */
10
+ export function createCodexDaemonHost(input, deps) {
11
+ const claims = new Map(), controllers = new Map(), hosts = new Map(), leases = new Map();
12
+ const releases = structuredClone(deps?.native.releases ?? CODEX_DAEMON_EXEC_RELEASES);
13
+ const native = deps?.native ?? (releases.length ? { ...registeredPrelaunchDependencies(input.home, input.codexHome, PRIVILEGED_MDM_MODE, releases), releases } : undefined);
14
+ let selected;
15
+ const available = async () => {
16
+ const matches = releases.filter(r => reviewedCodexExecRelease(r) && r.mdmVerification === PRIVILEGED_MDM_MODE && r.credentialBackend === 'auto');
17
+ if (!native || matches.length !== 1)
18
+ return { available: false, authenticated: false, capabilities: [], reason: 'CODEX_DAEMON_REVIEWED_OBSERVER_UNAVAILABLE' };
19
+ try {
20
+ const pin = matches[0].policyObserver;
21
+ (native.verifyObserver ?? verifyPolicyObserver)(pin);
22
+ await (native.checkObserverReady ?? (p => createPolicyInventoryClient(p).checkReady()))(pin);
23
+ return await native.probe();
24
+ }
25
+ catch {
26
+ return { available: false, authenticated: false, capabilities: [], reason: 'CODEX_DAEMON_REVIEWED_OBSERVER_UNAVAILABLE' };
27
+ }
28
+ };
29
+ return {
30
+ probe: available,
31
+ registerClaim(value) {
32
+ if (selected || claims.size)
33
+ throw Error('CODEX_ACTIVE_CLAIM_CONFLICT');
34
+ if (value.task_package?.security_requirements?.mdm_verification !== PRIVILEGED_MDM_MODE)
35
+ throw Error('CODEX_DAEMON_PROVENANCE_REQUIRED');
36
+ if (!native || releases.length !== 1)
37
+ throw Error('CODEX_DAEMON_REVIEWED_OBSERVER_UNAVAILABLE');
38
+ const c = structuredClone(value);
39
+ if (!c.job?.id || !Number.isSafeInteger(c.job.lease_expires_at))
40
+ throw Error('CODEX_DAEMON_CLAIM_REJECTED');
41
+ selected = c.job.id;
42
+ claims.set(selected, c);
43
+ leases.set(selected, { generation: c.job.generation, expires: c.job.lease_expires_at });
44
+ },
45
+ options(jobId, worktreePath, stateRoot, env) {
46
+ const claim = claims.get(jobId);
47
+ if (selected !== jobId || !claim || !native)
48
+ throw Error('CODEX_TRUSTED_CLAIM_REQUIRED');
49
+ let inner, cleanup, closed = false;
50
+ const lazy = { async prepare(ctx) {
51
+ if (closed || controllers.has(jobId))
52
+ throw Error('CODEX_DAEMON_PREPARATION_REUSED');
53
+ const pin = releases[0].policyObserver;
54
+ const client = createPolicyInventoryClient(pin);
55
+ const controller = createPrivilegedDaemonController({ runnerId: input.runnerId, binary: native.binary ?? CODEX_MANUAL_BINARY, home: input.home, codexHome: input.codexHome, claim }, { releases, observer: pin, readLocal: native.readLocal, readNativeAuth: native.readNativeAuth, collectInventory: native.collectInventory ?? client.collectOnce });
56
+ controllers.set(jobId, controller);
57
+ const host = await controller.prepare();
58
+ if (closed) {
59
+ await host.close();
60
+ throw Error('CODEX_DAEMON_CANCELLED');
61
+ }
62
+ hosts.set(jobId, host);
63
+ const lease = leases.get(jobId);
64
+ host.heartbeat(jobId, lease.generation, lease.expires);
65
+ inner = host.job(jobId, worktreePath);
66
+ if (cleanup)
67
+ inner.attachCleanup(cleanup);
68
+ return inner.prepare(ctx);
69
+ }, async inspect(plan) { if (!inner || closed)
70
+ throw Error('CODEX_DAEMON_NOT_PREPARED'); return inner.inspect(plan); }, revoke() { closed = true; controllers.get(jobId)?.cancel(); inner?.revoke(); }, attachCleanup(action) { if (cleanup || closed)
71
+ throw Error('CODEX_DAEMON_CLEANUP_REUSED'); cleanup = action; }, async close() { closed = true; controllers.get(jobId)?.cancel(); await inner?.close(); } };
72
+ return buildCodexExecJobOptions({ jobId, worktreePath, stateRoot, env, productionJob: () => lazy, processHost: deps?.processHost });
73
+ },
74
+ heartbeat(id, generation, expires) { const l = leases.get(id); if (!l || l.generation !== generation || !Number.isSafeInteger(expires) || expires * 1000 <= Date.now())
75
+ throw Error('CODEX_JOB_LEASE_FENCE_MISMATCH'); l.expires = expires; hosts.get(id)?.heartbeat(id, generation, expires); },
76
+ async releaseJob(id) { try {
77
+ await controllers.get(id)?.close();
78
+ }
79
+ finally {
80
+ controllers.delete(id);
81
+ hosts.delete(id);
82
+ claims.delete(id);
83
+ leases.delete(id);
84
+ if (selected === id)
85
+ selected = undefined;
86
+ } },
87
+ async close() { for (const c of controllers.values())
88
+ c.cancel(); try {
89
+ const results = await Promise.allSettled([...controllers.values()].map(c => c.close()));
90
+ if (results.some(r => r.status === 'rejected'))
91
+ throw Error('CODEX_DAEMON_CLEANUP_FAILED');
92
+ }
93
+ finally {
94
+ claims.clear();
95
+ controllers.clear();
96
+ hosts.clear();
97
+ leases.clear();
98
+ selected = undefined;
99
+ } },
100
+ };
101
+ }
@@ -0,0 +1,42 @@
1
+ /** Installed no-model observer evidence; actual daemon model acceptance remains pending.
2
+ * Prior exact CLI gate fixtures are inherited, not rerun or relabelled as daemon acceptance. */
3
+ export const CODEX_DAEMON_EXEC_RELEASES = Object.freeze([{
4
+ "version": "codex-exec-supported-release-v3",
5
+ "adapterVersion": "1",
6
+ "transport": "official_exec_v1",
7
+ "securityMode": "supported_cli_exec_release_policy_v3",
8
+ "policyScope": "unmanaged_personal_only",
9
+ "managedEnforcement": "not_run",
10
+ "executableVersion": "codex-cli 0.163.0-alpha.10.2",
11
+ "binarySha256": "1afb28c01d7d49e892bd75f11076b10f1451e34e0ee308a95e5ab7f69f9c84fe",
12
+ "modelIds": [
13
+ "gpt-5.6-sol"
14
+ ],
15
+ "decisionId": "d31b9b90-2aeb-4735-88eb-61b5de777d85",
16
+ "acceptedResidualRisks": [
17
+ "vendor_native_policy_enforcement",
18
+ "managed_policy_not_directly_observed_per_job",
19
+ "first_inference_order_not_observed_per_job",
20
+ "job_mcp_token_possession_not_os_identity",
21
+ "account_startup_may_refresh_shared_credentials",
22
+ "concurrent_policy_change_not_atomic",
23
+ "local_account_metadata_not_remote_entitlement",
24
+ "privileged_prelaunch_mdm_snapshot_not_continuously_reobserved"
25
+ ],
26
+ "credentialBackend": "auto",
27
+ "mdmVerification": "privileged_prelaunch",
28
+ "fixtureEvidence": {
29
+ "binary_release": "fa1cd6ce84178d0696c1ae397c05cf7cea35dcac5616e91dda85725e50976371",
30
+ "official_permissions": "cabb85e115996d50fe375f234aad4551dbfa5c7fe7a86cbda0f3d30c1d6fd471",
31
+ "managed_scope_exclusion": "a559199e229e153759f7a7cbd682aae423c0e3d33ef2a692fcccb5eed7c57ed3",
32
+ "required_mcp_startup": "e7305dcdfc4c86fd30d02d311ae7a061e7a21800ef86c3f7d1035330d49ae474",
33
+ "source_exclusion": "93697ceee0d5e864dc7267dab99fabd93db35c7d841dce50e39d6d6ad705bd08",
34
+ "auth_backend_compatibility": "7c42b882a52698fef3e14b4b956ede91ed1e3b29511fbff3176387063b8d74f0"
35
+ },
36
+ "fixturesFingerprint": "f1ccefe90b39aae420b47b902be5a2d61d957e2b600dd26081ae91e5e94dc0af",
37
+ "commands": [],
38
+ "policyObserver": {
39
+ "protocol": "policy-inventory-v1",
40
+ "sha256": "2a07812cd47782826a359b749096b681a6ee49e91f990d48abdc60f2d46e4c9c"
41
+ }
42
+ }]);
@@ -1,3 +1,5 @@
1
+ import { isDeepStrictEqual } from 'node:util';
2
+ import { PRIVILEGED_MDM_MODE, validPolicyObserverPin } from '../adapters/codex/prelaunch-policy.js';
1
3
  import { createHash } from 'node:crypto';
2
4
  import { UNMANAGED_EXEC_MODE, UNMANAGED_SCOPE, createUnmanagedScopeVerifier, policyScopeReceipt } from '../adapters/codex/unmanaged-scope.js';
3
5
  import { CODEX_GLOBAL_INSTRUCTION_WRAPPER, accountAncestorPaths, requireGlobalInstructionInvocation } from '../adapters/codex/global-instructions.js';
@@ -22,7 +24,7 @@ const exact = (v, keys) => JSON.stringify(Object.keys(v).sort()) === JSON.string
22
24
  const inside = (r, p) => p === r || p.startsWith(r + path.sep);
23
25
  export const execReleaseFingerprint = (value) => hash(value);
24
26
  export function reviewedCodexExecRelease(r) { try {
25
- return exact(r, ['version', 'adapterVersion', 'transport', 'securityMode', 'executableVersion', 'binarySha256', 'modelIds', 'decisionId', 'acceptedResidualRisks', 'credentialBackend', 'fixtureEvidence', 'fixturesFingerprint', 'commands', ...(r.securityMode === UNMANAGED_EXEC_MODE ? ['policyScope', 'managedEnforcement', ...(r.mdmVerification === undefined ? [] : ['mdmVerification'])] : [])]) && r.version === (r.securityMode === UNMANAGED_EXEC_MODE ? 'codex-exec-supported-release-v3' : 'codex-exec-supported-release-v2') && (r.securityMode !== UNMANAGED_EXEC_MODE || r.policyScope === UNMANAGED_SCOPE && r.managedEnforcement === 'not_run' && (r.mdmVerification === undefined || r.mdmVerification === 'manual_prelaunch')) && r.adapterVersion === '1' && r.transport === 'official_exec_v1' && isReleaseExecMode(r.securityMode) && (r.credentialBackend === 'file' || r.securityMode === UNMANAGED_EXEC_MODE && r.credentialBackend === 'auto') && typeof r.executableVersion === 'string' && !!r.executableVersion && !/[\r\n\0]/.test(r.executableVersion) && hex(r.binarySha256) && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(r.decisionId) && Array.isArray(r.modelIds) && r.modelIds.length > 0 && new Set(r.modelIds).size === r.modelIds.length && r.modelIds.every(m => typeof m === 'string' && m.length > 0 && m.length < 256 && !/[\r\n\0]/.test(m)) && JSON.stringify(r.acceptedResidualRisks) === JSON.stringify(releaseRisks(r.securityMode, r.mdmVerification)) && r.fixtureEvidence && exact(r.fixtureEvidence, [...releaseGates(r.securityMode)]) && Object.values(r.fixtureEvidence).every(hex) && r.fixturesFingerprint === fingerprint(r.fixtureEvidence) && Array.isArray(r.commands) && new Set(r.commands.map(c => c.id)).size === r.commands.length && r.commands.every(registeredCommandValid);
27
+ return exact(r, ['version', 'adapterVersion', 'transport', 'securityMode', 'executableVersion', 'binarySha256', 'modelIds', 'decisionId', 'acceptedResidualRisks', 'credentialBackend', 'fixtureEvidence', 'fixturesFingerprint', 'commands', ...(r.securityMode === UNMANAGED_EXEC_MODE ? ['policyScope', 'managedEnforcement', ...(r.mdmVerification === undefined ? [] : ['mdmVerification']), ...(r.mdmVerification === PRIVILEGED_MDM_MODE ? ['policyObserver'] : [])] : [])]) && r.version === (r.securityMode === UNMANAGED_EXEC_MODE ? 'codex-exec-supported-release-v3' : 'codex-exec-supported-release-v2') && (r.securityMode !== UNMANAGED_EXEC_MODE || r.policyScope === UNMANAGED_SCOPE && r.managedEnforcement === 'not_run' && (r.mdmVerification === undefined || r.mdmVerification === 'manual_prelaunch' || r.mdmVerification === PRIVILEGED_MDM_MODE && validPolicyObserverPin(r.policyObserver))) && r.adapterVersion === '1' && r.transport === 'official_exec_v1' && isReleaseExecMode(r.securityMode) && (r.credentialBackend === 'file' || r.securityMode === UNMANAGED_EXEC_MODE && r.credentialBackend === 'auto') && typeof r.executableVersion === 'string' && !!r.executableVersion && !/[\r\n\0]/.test(r.executableVersion) && hex(r.binarySha256) && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(r.decisionId) && Array.isArray(r.modelIds) && r.modelIds.length > 0 && new Set(r.modelIds).size === r.modelIds.length && r.modelIds.every(m => typeof m === 'string' && m.length > 0 && m.length < 256 && !/[\r\n\0]/.test(m)) && JSON.stringify(r.acceptedResidualRisks) === JSON.stringify(releaseRisks(r.securityMode, r.mdmVerification)) && r.fixtureEvidence && exact(r.fixtureEvidence, [...releaseGates(r.securityMode)]) && Object.values(r.fixtureEvidence).every(hex) && r.fixturesFingerprint === fingerprint(r.fixtureEvidence) && Array.isArray(r.commands) && new Set(r.commands.map(c => c.id)).size === r.commands.length && r.commands.every(registeredCommandValid);
26
28
  }
27
29
  catch {
28
30
  return false;
@@ -64,7 +66,7 @@ export function createCodexExecProductionHost(original) {
64
66
  if (!credential || credential.backend !== backend || !credential.available || !Number.isFinite(credential.expiresAt) || credential.expiresAt <= Date.now())
65
67
  throw reject();
66
68
  const policyObservation = matches[0].securityMode === UNMANAGED_EXEC_MODE ? policyVerifier.observe() : undefined;
67
- if (policyObservation && (policyObservation.accountRootFingerprint !== hash({ accountRoot: account }) || Boolean(policyObservation.manualVerification) !== Boolean(matches[0].mdmVerification)))
69
+ if (policyObservation && (policyObservation.accountRootFingerprint !== hash({ accountRoot: account }) || (policyObservation.manualVerification?.mode ?? policyObservation.privilegedVerification?.mode) !== matches[0].mdmVerification || matches[0].mdmVerification === PRIVILEGED_MDM_MODE && !isDeepStrictEqual(policyObservation.privilegedVerification?.observer, matches[0].policyObserver)))
68
70
  throw reject();
69
71
  return { policyObservation, binary, release: matches[0], config, binaryStamp, configStamp: configData.stamp };
70
72
  }
@@ -119,7 +121,7 @@ export function createCodexExecProductionHost(original) {
119
121
  try {
120
122
  selected = await inspect();
121
123
  const r = requireSecurityRequirements(p.security_requirements, { adapterId: 'codex_cli', adapterVersion: '1' }), release = selected.release;
122
- if (r.mdm_verification !== release.mdmVerification || r.security_mode !== release.securityMode || value.generation !== j.generation || value.resolvedModelId !== p.resolved_model_id || !release.modelIds.includes(value.resolvedModelId) || fingerprint(value.approvedTaskPackage) !== c.task_package_fingerprint || value.securityBinding?.contractFingerprint !== c.contract_fingerprint || value.securityBinding?.taskPackageFingerprint !== c.task_package_fingerprint || value.securityBinding?.launchFingerprint !== j.launch_fingerprint || r.release_manifest_sha256 !== execReleaseFingerprint(release) || r.binary_sha256 !== release.binarySha256 || r.fixtures_sha256 !== release.fixturesFingerprint || r.commands_sha256 !== commandRegistryFingerprint(release.commands) || r.tools_sha256 !== fingerprint({ tools: localToolInventory() }) || JSON.stringify(r.model_ids) !== JSON.stringify(release.modelIds))
124
+ if (r.mdm_verification !== release.mdmVerification || !isDeepStrictEqual(r.policy_observer, release.policyObserver) || r.security_mode !== release.securityMode || value.generation !== j.generation || value.resolvedModelId !== p.resolved_model_id || !release.modelIds.includes(value.resolvedModelId) || fingerprint(value.approvedTaskPackage) !== c.task_package_fingerprint || value.securityBinding?.contractFingerprint !== c.contract_fingerprint || value.securityBinding?.taskPackageFingerprint !== c.task_package_fingerprint || value.securityBinding?.launchFingerprint !== j.launch_fingerprint || r.release_manifest_sha256 !== execReleaseFingerprint(release) || r.binary_sha256 !== release.binarySha256 || r.fixtures_sha256 !== release.fixturesFingerprint || r.commands_sha256 !== commandRegistryFingerprint(release.commands) || r.tools_sha256 !== fingerprint({ tools: localToolInventory() }) || JSON.stringify(r.model_ids) !== JSON.stringify(release.modelIds))
123
125
  throw reject();
124
126
  const source = await realpath(sourceRoot), home = await realpath(account);
125
127
  if (inside(source, home) || inside(home, source))
@@ -1,3 +1,4 @@
1
+ import { PRIVILEGED_MDM_MODE, validPolicyObserverPin } from '../adapters/codex/prelaunch-policy.js';
1
2
  import { executionError } from './execution-failure.js';
2
3
  import { eligiblePersonalAccount } from '../adapters/codex/unmanaged-scope.js';
3
4
  import { securityRequirementsFingerprint as hash } from './security-requirements.js';
@@ -15,13 +16,15 @@ import { CODEX_EXEC_RELEASES } from './codex-exec-releases.js';
15
16
  import { measureCommandFile } from './registered-command-integrity.js';
16
17
  import { readStableText } from './stable-file-read.js';
17
18
  import { createManualProfileQuery } from './codex-manual-profile-query.js';
18
- import { createManualPolicyScopeVerifier } from '../adapters/codex/manual-policy-scope.js';
19
+ import { createManualPolicyScopeVerifier, createPrivilegedPolicyScopeVerifier } from '../adapters/codex/manual-policy-scope.js';
19
20
  const rejected = () => Error('CODEX_MANUAL_PILOT_REJECTED');
20
- /** Foreground composition for an already approved, authenticated Gateway claim.
21
- * Dependencies are compiled/trusted code, not serialized task/env authority.
22
- * No release/native reader fallback exists; missing dependencies refuse before
23
- * an administrator prompt. This never changes the installed daemon. */
24
- export function createManualPilotController(input, deps) {
21
+ export function createManualPilotController(input, deps) { return createPrelaunchController(input, deps, 'manual_prelaunch'); }
22
+ export function createPrivilegedDaemonController(input, deps) {
23
+ if (!validPolicyObserverPin(deps.observer) || typeof deps.collectInventory !== 'function')
24
+ throw rejected();
25
+ return createPrelaunchController(input, deps, PRIVILEGED_MDM_MODE);
26
+ }
27
+ function createPrelaunchController(input, deps, mode) {
25
28
  const claim = structuredClone(input.claim), releases = structuredClone(deps?.releases ?? CODEX_EXEC_RELEASES), readLocal = deps?.readLocal, now = deps?.now ?? Date.now;
26
29
  const account = path.resolve(input.codexHome ?? path.join(input.home, '.codex'));
27
30
  let used = false, closed = false, manual, host;
@@ -37,13 +40,13 @@ export function createManualPilotController(input, deps) {
37
40
  if (!readLocal || !path.isAbsolute(input.binary) || !path.isAbsolute(input.home) || await realpath(input.home) !== input.home || await realpath(account) !== account)
38
41
  throw rejected();
39
42
  stage = 'codex_manual_release';
40
- const sha = await measureCommandFile(input.binary), matches = releases.filter(r => reviewedCodexExecRelease(r) && r.mdmVerification === 'manual_prelaunch' && r.binarySha256 === sha);
43
+ const sha = await measureCommandFile(input.binary), matches = releases.filter(r => reviewedCodexExecRelease(r) && r.mdmVerification === mode && r.binarySha256 === sha);
41
44
  if (matches.length !== 1)
42
45
  throw rejected();
43
46
  const release = matches[0], requirements = claim.task_package?.security_requirements;
44
47
  stage = 'codex_manual_config';
45
48
  const config = parse((await readStableText(path.join(account, 'config.toml'))).text);
46
- if ((config.cli_auth_credentials_store ?? 'auto') !== release.credentialBackend || requirements?.release_manifest_sha256 !== execReleaseFingerprint(release) || requirements?.mdm_verification !== 'manual_prelaunch')
49
+ if ((config.cli_auth_credentials_store ?? 'auto') !== release.credentialBackend || requirements?.release_manifest_sha256 !== execReleaseFingerprint(release) || requirements?.mdm_verification !== mode || mode === PRIVILEGED_MDM_MODE && !isDeepStrictEqual(release.policyObserver, deps?.observer))
47
50
  throw rejected();
48
51
  host = createCodexExecProductionHost({ runnerId: input.runnerId, binary: input.binary, home: input.home, codexHome: account, releases: [release], readNativeAuth: deps?.readNativeAuth, policyScopeVerifier: { observe() { if (closed || !manual)
49
52
  throw rejected(); return manual.observe(); } } });
@@ -71,14 +74,15 @@ export function createManualPilotController(input, deps) {
71
74
  if (closed)
72
75
  throw rejected();
73
76
  stage = 'codex_manual_policy_scope';
74
- manual = createManualPolicyScopeVerifier({ binding: { jobId: job.id, generation: job.generation, actorId: claim.security_context.actor_id, runnerId: input.runnerId, sourceSha: p.repository.base_commit_sha, binarySha256: release.binarySha256, releaseManifestFingerprint: String(r.release_manifest_sha256), requirementsFingerprint: String(r.fingerprint), contractFingerprint: claim.contract_fingerprint, taskPackageFingerprint: claim.task_package_fingerprint, launchFingerprint: job.launch_fingerprint, commandsFingerprint: String(r.commands_sha256), toolSchemaFingerprint: String(r.tools_sha256), barrierFingerprint: String(r.fixtures_sha256) }, leaseDigest: createHash('sha256').update(claim.lease_token).digest('hex'), inventory, readLocal, now });
77
+ const scopeInput = { binding: { jobId: job.id, generation: job.generation, actorId: claim.security_context.actor_id, runnerId: input.runnerId, sourceSha: p.repository.base_commit_sha, binarySha256: release.binarySha256, releaseManifestFingerprint: String(r.release_manifest_sha256), requirementsFingerprint: String(r.fingerprint), contractFingerprint: claim.contract_fingerprint, taskPackageFingerprint: claim.task_package_fingerprint, launchFingerprint: job.launch_fingerprint, commandsFingerprint: String(r.commands_sha256), toolSchemaFingerprint: String(r.tools_sha256), barrierFingerprint: String(r.fixtures_sha256) }, leaseDigest: createHash('sha256').update(claim.lease_token).digest('hex'), inventory, readLocal, now };
78
+ manual = mode === PRIVILEGED_MDM_MODE ? createPrivilegedPolicyScopeVerifier(scopeInput, deps.observer) : createManualPolicyScopeVerifier(scopeInput);
75
79
  stage = 'codex_manual_host_probe';
76
80
  if (!(await host.probe()).available || closed)
77
81
  throw rejected();
78
82
  return host;
79
83
  }
80
84
  catch (error) {
81
- const safe = executionError(error, stage, 'CODEX_MANUAL_PILOT_REJECTED');
85
+ const safe = executionError(error, mode === PRIVILEGED_MDM_MODE ? 'codex_policy_inventory' : stage, mode === PRIVILEGED_MDM_MODE ? 'CODEX_POLICY_INVENTORY_REJECTED' : 'CODEX_MANUAL_PILOT_REJECTED');
82
86
  await close().catch(() => { });
83
87
  throw safe;
84
88
  }
@@ -12,13 +12,15 @@ import { CLAUDE_CAPABILITIES } from '../adapters/discovery.js';
12
12
  /** Compiled reviewed identity; never selected by task, environment or receipts. */
13
13
  export const CODEX_MANUAL_BINARY = '/Applications/ChatGPT.app/Contents/Resources/codex-cli/CodexCLI.app/Contents/MacOS/codex';
14
14
  export const CODEX_MANUAL_NATIVE_READER = Object.freeze({ "binary": "/Library/PrivilegedHelperTools/com.mtec.ai-gateway.codex-observe.681fb12e0b83ff8fbb612dc71f9a5f5c9be5334bcfead29314ca5b979b1dd986", "sha256": "681fb12e0b83ff8fbb612dc71f9a5f5c9be5334bcfead29314ca5b979b1dd986" });
15
- export function registeredManualDependencies(home, codexHome) {
15
+ export function registeredManualDependencies(home, codexHome) { return registeredPrelaunchDependencies(home, codexHome, 'manual_prelaunch', CODEX_EXEC_RELEASES); }
16
+ /** Trusted compiled registration shared by manual and daemon host composition. */
17
+ export function registeredPrelaunchDependencies(home, codexHome, mode, releases) {
16
18
  const account = path.resolve(codexHome ?? path.join(home, '.codex')), pin = CODEX_MANUAL_NATIVE_READER;
17
19
  if (!path.isAbsolute(home) || !pin)
18
20
  throw Error('CODEX_MANUAL_REGISTRATION_UNAVAILABLE');
19
21
  const reader = createNativeObservationReader({ accountRoot: account, backend: 'auto', keyringKind: 'direct', process: nativeMetadataProcess(pin.binary, pin.sha256), systemSource: systemPolicySource, mdmInventory: () => { throw Error('MANUAL_INVENTORY_ONLY'); } });
20
22
  return Object.freeze({ binary: CODEX_MANUAL_BINARY, readLocal: reader.readLocal, readNativeAuth: () => reader.readAuth(), async probe() {
21
- const digest = await measureCommandFile(CODEX_MANUAL_BINARY), matches = CODEX_EXEC_RELEASES.filter(r => reviewedCodexExecRelease(r) && r.binarySha256 === digest && r.mdmVerification === 'manual_prelaunch' && r.credentialBackend === 'auto');
23
+ const digest = await measureCommandFile(CODEX_MANUAL_BINARY), matches = releases.filter(r => reviewedCodexExecRelease(r) && r.binarySha256 === digest && r.mdmVerification === mode && r.credentialBackend === 'auto');
22
24
  const config = parse((await readStableText(path.join(account, 'config.toml'))).text);
23
25
  if (matches.length !== 1 || (config.cli_auth_credentials_store ?? 'auto') !== 'auto' || config.features?.secret_auth_storage === true)
24
26
  throw Error('CODEX_MANUAL_REGISTRATION_UNAVAILABLE');
@@ -0,0 +1,17 @@
1
+ export function safeLocalReadObservations(value) {
2
+ if (!value || typeof value !== 'object' || Array.isArray(value))
3
+ return;
4
+ const v = value, c = v.classification_counts;
5
+ if (Object.keys(v).sort().join() !== ['version', 'successful_read_calls', 'unique_successful_paths', 'failed_read_calls', 'classification_counts', 'truncated'].sort().join()
6
+ || v.version !== 'local-read-observations-v1' || typeof v.truncated !== 'boolean' || !c || typeof c !== 'object' || Array.isArray(c))
7
+ return;
8
+ const counts = c;
9
+ if (Object.keys(counts).sort().join() !== 'documentation,source')
10
+ return;
11
+ const numbers = [v.successful_read_calls, v.unique_successful_paths, v.failed_read_calls, counts.documentation, counts.source];
12
+ if (numbers.some(n => !Number.isSafeInteger(n) || Number(n) < 0 || Number(n) > 1_000_000)
13
+ || Number(v.unique_successful_paths) > 1024 || Number(v.unique_successful_paths) > Number(v.successful_read_calls)
14
+ || Number(counts.documentation) + Number(counts.source) !== v.successful_read_calls)
15
+ return;
16
+ return structuredClone(value);
17
+ }
@@ -1,5 +1,6 @@
1
1
  import { createServer } from 'node:http';
2
2
  import { createHash, randomBytes, timingSafeEqual } from 'node:crypto';
3
+ import path from 'node:path';
3
4
  import { LOCAL_TOOL_NAMES } from './local-tool-policy.js';
4
5
  const discoveryScopes = new WeakMap();
5
6
  export function localDiscoveryScopeMatches(scope, endpoint, token) {
@@ -10,6 +11,10 @@ export function localDiscoveryScopeMatches(scope, endpoint, token) {
10
11
  }
11
12
  // Bounded diagnostic metadata; possession grants neither discovery nor tool authority.
12
13
  const endpointObservations = new Map();
14
+ export function localEndpointReadObservations(url, token) {
15
+ const record = endpointObservations.get(url);
16
+ return record?.credentialHash === createHash('sha256').update(token).digest('hex') ? record.reads() : undefined;
17
+ }
13
18
  export function localEndpointObservations(url, token) {
14
19
  const record = endpointObservations.get(url);
15
20
  return record?.credentialHash === createHash('sha256').update(token).digest('hex') ? [...record.values] : [];
@@ -28,6 +33,32 @@ export function localToolInventory() {
28
33
  /** Job-only JSON MCP endpoint; intentionally has no Gateway transport or discovery. */
29
34
  export class LocalToolServer {
30
35
  options;
36
+ readCounts = { version: 'local-read-observations-v1', successful_read_calls: 0, unique_successful_paths: 0, failed_read_calls: 0, classification_counts: { documentation: 0, source: 0 }, truncated: false };
37
+ readPathDigests = new Set();
38
+ /** Counts are server-authored observations, not model evidence or a policy attestation. */
39
+ readObservations() { return structuredClone(this.readCounts); }
40
+ recordRead(success, args) {
41
+ const count = success ? 'successful_read_calls' : 'failed_read_calls';
42
+ if (this.readCounts[count] >= 1_000_000) {
43
+ this.readCounts.truncated = true;
44
+ return;
45
+ }
46
+ this.readCounts[count]++;
47
+ if (!success)
48
+ return;
49
+ const value = (args && typeof args === 'object' && !Array.isArray(args) ? args.path : undefined);
50
+ const file = path.posix.normalize(typeof value === 'string' ? value : '');
51
+ const category = file.startsWith('docs/') ? 'documentation' : 'source';
52
+ this.readCounts.classification_counts[category]++;
53
+ const digest = createHash('sha256').update(file).digest('hex');
54
+ if (!this.readPathDigests.has(digest)) {
55
+ if (this.readPathDigests.size < 1024)
56
+ this.readPathDigests.add(digest);
57
+ else
58
+ this.readCounts.truncated = true;
59
+ }
60
+ this.readCounts.unique_successful_paths = this.readPathDigests.size;
61
+ }
31
62
  observations = new Set();
32
63
  server = null;
33
64
  revoked = false;
@@ -174,6 +205,8 @@ export class LocalToolServer {
174
205
  return;
175
206
  }
176
207
  send({ content: [{ type: 'text', text: JSON.stringify(result) }] });
208
+ if (params.name === 'local_read')
209
+ this.recordRead(true, params.arguments);
177
210
  }
178
211
  catch (error) {
179
212
  if (this.token !== token || !this.allowed()) {
@@ -182,6 +215,8 @@ export class LocalToolServer {
182
215
  }
183
216
  // Only stable policy error identifiers cross this boundary, never raw paths/stderr.
184
217
  const code = error instanceof Error ? error.message.match(/^(DENIED_[A-Z_]+|LOCAL_SANDBOX_UNAVAILABLE)/)?.[0] : undefined;
218
+ if (params.name === 'local_read')
219
+ this.recordRead(false, params.arguments);
185
220
  send({ isError: true, content: [{ type: 'text', text: code ?? 'LOCAL_TOOL_FAILED' }] });
186
221
  }
187
222
  }
@@ -203,7 +238,7 @@ export class LocalToolServer {
203
238
  if (!address || typeof address === 'string')
204
239
  throw new Error('Failed to bind local tool server');
205
240
  this.url = `http://127.0.0.1:${address.port}/mcp`;
206
- endpointObservations.set(this.url, { credentialHash: createHash('sha256').update(this.token).digest('hex'), values: this.observations });
241
+ endpointObservations.set(this.url, { credentialHash: createHash('sha256').update(this.token).digest('hex'), values: this.observations, reads: () => this.readObservations() });
207
242
  discoveryScopes.set(this.discoveryScope, { endpoint: this.url, credentialHash: createHash('sha256').update(this.token).digest('hex') });
208
243
  return { url: this.url, token: this.token };
209
244
  }
@@ -0,0 +1,77 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { createHash, randomBytes } from 'node:crypto';
3
+ import { lstatSync, readFileSync, realpathSync } from 'node:fs';
4
+ import path from 'node:path';
5
+ import { regularFileStamp } from './stable-file-read.js';
6
+ import { validPolicyObserverPin } from '../adapters/codex/prelaunch-policy.js';
7
+ const denied = () => Error('CODEX_POLICY_INVENTORY_REJECTED');
8
+ export const policyObserverPath = (pin) => `/Library/PrivilegedHelperTools/com.mtec.ai-gateway.policy-observer.${pin.sha256}`;
9
+ /** Only compiled reviewed registrations select this executable, never task/env input. */
10
+ export function verifyPolicyObserver(pin) {
11
+ if (!validPolicyObserverPin(pin))
12
+ throw denied();
13
+ const binary = policyObserverPath(pin);
14
+ if (realpathSync(binary) !== binary)
15
+ throw denied();
16
+ const file = lstatSync(binary);
17
+ if (!file.isFile() || file.isSymbolicLink() || file.uid !== 0 || (file.mode & 0o022) !== 0 || (file.mode & 0o111) === 0)
18
+ throw denied();
19
+ for (let dir = path.dirname(binary);; dir = path.dirname(dir)) {
20
+ const s = lstatSync(dir);
21
+ if (!s.isDirectory() || s.isSymbolicLink() || s.uid !== 0 || (s.mode & 0o022) !== 0)
22
+ throw denied();
23
+ if (dir === path.dirname(dir))
24
+ break;
25
+ }
26
+ const before = regularFileStamp(binary);
27
+ if (createHash('sha256').update(readFileSync(binary)).digest('hex') !== pin.sha256 || regularFileStamp(binary) !== before)
28
+ throw denied();
29
+ }
30
+ const query = (binary, args, request, signal) => new Promise((resolve, reject) => {
31
+ const child = execFile(binary, [...args], { encoding: 'utf8', timeout: args[0] === '--ready' ? 5000 : 70_000, maxBuffer: 8192, signal, env: { PATH: '/usr/bin:/bin', LANG: 'C', LC_ALL: 'C' } }, (error, stdout, stderr) => { if (error || stderr || !stdout)
32
+ return reject(denied()); resolve(stdout); });
33
+ child.stdin?.on('error', () => reject(denied()));
34
+ child.stdin?.end(request);
35
+ });
36
+ /** Single-use asynchronous IPC; the native client verifies root peer credentials.
37
+ * Injection seams are trusted in-process test code, not configuration loaders. */
38
+ export function createPolicyInventoryClient(original, deps) {
39
+ if (!validPolicyObserverPin(original))
40
+ throw denied();
41
+ const pin = Object.freeze({ ...original });
42
+ let used = false;
43
+ const perform = async (operation, signal) => {
44
+ if (used)
45
+ throw denied();
46
+ used = true;
47
+ try {
48
+ if (signal?.aborted)
49
+ throw denied();
50
+ const verify = deps?.verify ?? verifyPolicyObserver, now = deps?.now ?? Date.now;
51
+ verify(pin);
52
+ const startedAt = now(), nonce = (deps?.nonce ?? (() => randomBytes(32).toString('hex')))();
53
+ if (!Number.isSafeInteger(startedAt) || startedAt < 0 || !/^[a-f0-9]{64}$/.test(nonce))
54
+ throw denied();
55
+ const raw = await (deps?.run ?? query)(policyObserverPath(pin), [operation === 'ready' ? '--ready' : '--query'], JSON.stringify({ version: 1, operation, nonce }), signal);
56
+ if (signal?.aborted || typeof raw !== 'string' || Buffer.byteLength(raw) > 8192)
57
+ throw denied();
58
+ verify(pin);
59
+ const at = now(), r = JSON.parse(raw), keys = operation === 'ready' ? 'nonce,observerSha256,operation,status,version' : 'complete,empty,inventoryFingerprint,nonce,observedAt,observerSha256,operation,status,version';
60
+ if (!r || typeof r !== 'object' || Array.isArray(r) || Object.keys(r).sort().join(',') !== keys || r.version !== 1 || r.operation !== operation || r.nonce !== nonce || r.observerSha256 !== pin.sha256 || !Number.isSafeInteger(at) || at < startedAt)
61
+ throw denied();
62
+ if (operation === 'ready') {
63
+ if (r.status !== 'ready')
64
+ throw denied();
65
+ return;
66
+ }
67
+ if (r.status !== 'completed' || r.complete !== true || r.empty !== true || !Number.isSafeInteger(r.observedAt) || r.observedAt < startedAt || r.observedAt > at || at - r.observedAt > 60_000 || typeof r.inventoryFingerprint !== 'string' || !/^[a-f0-9]{64}$/.test(r.inventoryFingerprint))
68
+ throw denied();
69
+ return Object.freeze({ version: 1, exit: 0, euid: 0, scope: 'machine_and_current_user', complete: true, empty: true, observedAt: r.observedAt, inventoryFingerprint: r.inventoryFingerprint });
70
+ }
71
+ catch {
72
+ throw denied();
73
+ }
74
+ };
75
+ return Object.freeze({ async checkReady(signal) { await perform('ready', signal); }, async collectOnce(signal) { const r = await perform('inventory', signal); if (!r)
76
+ throw denied(); return r; } });
77
+ }
@@ -20,9 +20,22 @@ export class FileDaemonStateStore {
20
20
  async save(state) {
21
21
  mkdirSync(this.root, { recursive: true, mode: 0o700 });
22
22
  const temporary = `${this.filePath}.${process.pid}.${Date.now()}.tmp`;
23
- await fs.writeFile(temporary, JSON.stringify(state, null, 2), { mode: 0o600 });
23
+ const file = await fs.open(temporary, 'wx', 0o600);
24
+ try {
25
+ await file.writeFile(JSON.stringify(state, null, 2));
26
+ await file.sync();
27
+ }
28
+ finally {
29
+ await file.close();
30
+ }
24
31
  await fs.rename(temporary, this.filePath);
25
- await fs.chmod(this.filePath, 0o600);
32
+ const directory = await fs.open(this.root, 'r');
33
+ try {
34
+ await directory.sync();
35
+ }
36
+ finally {
37
+ await directory.close();
38
+ }
26
39
  }
27
40
  async clear() { await fs.rm(this.filePath, { force: true }); }
28
41
  }
@@ -0,0 +1,22 @@
1
+ {
2
+ "fixture_class": "installed_observer_no_model_fixture",
3
+ "work_item_id": "bcb9164a-d4f8-4caa-8f61-5eeb7b8a1cc0",
4
+ "worklog_id": "dbf85621-b9ed-4843-8eeb-cc648ab758e1",
5
+ "checkpoint_id": "2748b184-39c2-412f-b542-d86fee2c13d8",
6
+ "observer_sha256": "2a07812cd47782826a359b749096b681a6ee49e91f990d48abdc60f2d46e4c9c",
7
+ "observer_protocol": "policy-inventory-v1",
8
+ "plist_sha256": "1fc069a257349b49a8dabd8c18defc12e9a94b14902ffa129c3b4438441c455d",
9
+ "employee_uid": 501,
10
+ "ready_requests": 1,
11
+ "machine_inventory_queries": 1,
12
+ "machine_complete": true,
13
+ "machine_empty": true,
14
+ "current_user_forced_config": false,
15
+ "current_user_forced_requirements": false,
16
+ "owned_query_children_remaining": 0,
17
+ "existing_employee_daemon_unloaded": true,
18
+ "actual_codex_daemon_model_acceptance": false,
19
+ "different_uid_and_cancellation_coverage": "synthetic_only",
20
+ "continuous_mdm_monitoring": false,
21
+ "inherited_manual_fixture_gate": "3bb8b2ad50a44cdea5a2a3f388f8009b51d8c60d46230fb3d0b7fed0b9f7452d"
22
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mtech-ai-local-runner",
3
- "version": "0.1.28",
3
+ "version": "0.1.30",
4
4
  "private": false,
5
5
  "license": "UNLICENSED",
6
6
  "bin": {
@@ -10,7 +10,9 @@
10
10
  "dist/src",
11
11
  "README.md",
12
12
  "LICENSE",
13
- "native/bin/codex-observe-darwin-arm64"
13
+ "native/bin/codex-observe-darwin-arm64",
14
+ "native/bin/policy-observer-darwin-arm64",
15
+ "native/policy-observer-installed-evidence.json"
14
16
  ],
15
17
  "type": "module",
16
18
  "engines": {