mtech-ai-local-runner 0.1.17 → 0.1.18
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 +12 -0
- package/dist/src/adapters/codex/exec-launch-host.js +28 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,6 +10,18 @@ The package stores safe runner configuration locally and keeps OAuth refresh cre
|
|
|
10
10
|
|
|
11
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`).
|
|
12
12
|
|
|
13
|
+
## 0.1.18: preparation deadline enforcement
|
|
14
|
+
|
|
15
|
+
The overall 30-second preparation budget also covers release inspection. A
|
|
16
|
+
synchronous metadata check can delay JavaScript timer callbacks. Local guards
|
|
17
|
+
now use a monotonic deadline after inspection and at spawn/input/admission
|
|
18
|
+
boundaries, refusing expired preparation even when its timer has not run. The
|
|
19
|
+
budget is not reset or extended; admitted employee work remains unbounded.
|
|
20
|
+
Fake callbacks reproduce the late-admission defect. Existing 0.1.17 receipts do
|
|
21
|
+
not contain phase timing, so this is not proof of the historical native wait's
|
|
22
|
+
cause or successful real-account connectivity. No new native stack sampler,
|
|
23
|
+
account operation, daemon change or actual CLI run is part of this correction.
|
|
24
|
+
|
|
13
25
|
## 0.1.17: owned startup, pipe and transport observations
|
|
14
26
|
|
|
15
27
|
An owned-process collector marker distinguishes available diagnostics from a
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { performance } from 'node:perf_hooks';
|
|
1
2
|
import { localEndpointObservations } from '../../safety/local-tool-server.js';
|
|
2
3
|
import { processStartupObservations } from '../../process/node-process-group-host.js';
|
|
3
4
|
import { executionError } from '../../safety/execution-failure.js';
|
|
@@ -10,6 +11,7 @@ import { officialExecObservationLimits } from './exec-observer.js';
|
|
|
10
11
|
import { EXEC_GATES, assessExecPreflight, requireExecHandshake } from './exec-preflight.js';
|
|
11
12
|
import { EXEC_MODE, isReleaseExecMode, EXEC_TRANSPORT, assertOfficialExecPlanAvailable, claimOfficialExecPlan, requireOfficialExecPlan } from './exec-invocation.js';
|
|
12
13
|
const fail = () => new Error('CODEX_EXEC_PREFLIGHT_REJECTED');
|
|
14
|
+
const PREPARATION_TIMEOUT_MS = 30_000;
|
|
13
15
|
/** No default native inspector, handshake observer or OS process host is created.
|
|
14
16
|
* Missing per-execution evidence blocks before spawn. Synthetic callbacks grant
|
|
15
17
|
* only the fake host used by tests, never a compiled production release entry. */
|
|
@@ -69,17 +71,26 @@ export class CodexExecLaunchHost {
|
|
|
69
71
|
const mcpDetail = () => stage === 'codex_mcp' ? this.deps.execTools?.discoveryDetail() : undefined;
|
|
70
72
|
const observations = () => { const invocation = requireOfficialExecPlan(this.plan); return [...(this.handle ? processStartupObservations(this.handle) : []), ...localEndpointObservations(this.plan.endpoint, invocation.env.AI_GATEWAY_JOB_MCP_TOKEN)]; };
|
|
71
73
|
const interrupt = (code) => { interruption ??= executionError(fail(), stage, code, mcpDetail(), observations()); };
|
|
74
|
+
// Timer callbacks cannot interrupt synchronous native metadata checks. Keep
|
|
75
|
+
// the same total budget and enforce it at effect boundaries with a monotonic
|
|
76
|
+
// clock, rather than spawning/submitting after an already expired inspection.
|
|
77
|
+
const deadline = performance.now() + PREPARATION_TIMEOUT_MS;
|
|
78
|
+
const assertPreparationBudget = () => { if (performance.now() >= deadline) {
|
|
79
|
+
interrupt('CODEX_EXEC_PREPARATION_TIMEOUT');
|
|
80
|
+
throw interruption;
|
|
81
|
+
} };
|
|
72
82
|
let timer, external;
|
|
73
83
|
try {
|
|
74
84
|
this.assertContext(ctx, jobId);
|
|
75
85
|
requireOfficialExecPlan(this.plan);
|
|
76
86
|
if (typeof prompt !== 'string' || !prompt || prompt.length > 1_000_000 || signal?.aborted)
|
|
77
87
|
throw fail();
|
|
78
|
-
const stopped = new Promise((_, reject) => { external = () => { interrupt('CODEX_EXEC_ABORTED'); void this.cancel().catch(() => { }); reject(interruption); }; this.abort.signal.addEventListener('abort', () => reject(interruption ?? fail()), { once: true }); signal?.addEventListener('abort', external, { once: true }); timer = setTimeout(() => { interrupt('CODEX_EXEC_PREPARATION_TIMEOUT'); external(); },
|
|
88
|
+
const stopped = new Promise((_, reject) => { external = () => { interrupt('CODEX_EXEC_ABORTED'); void this.cancel().catch(() => { }); reject(interruption); }; this.abort.signal.addEventListener('abort', () => reject(interruption ?? fail()), { once: true }); signal?.addEventListener('abort', external, { once: true }); timer = setTimeout(() => { interrupt('CODEX_EXEC_PREPARATION_TIMEOUT'); external(); }, PREPARATION_TIMEOUT_MS); });
|
|
79
89
|
const preparation = (async () => {
|
|
80
90
|
const supported = isReleaseExecMode(this.plan.mode);
|
|
81
91
|
stage = 'codex_inspection';
|
|
82
92
|
this.inspection = await (supported ? this.deps.inspectRelease?.(this.plan, this.abort.signal) : this.deps.inspect?.(this.plan, this.abort.signal));
|
|
93
|
+
assertPreparationBudget();
|
|
83
94
|
const assessment = supported ? assessReleaseExec(this.inspection?.snapshot, this.plan) : assessExecPreflight(this.inspection?.snapshot, this.plan);
|
|
84
95
|
if (!supported && !this.deps.inspect)
|
|
85
96
|
assessment.reasonCodes.push(...officialExecObservationLimits());
|
|
@@ -92,7 +103,9 @@ export class CodexExecLaunchHost {
|
|
|
92
103
|
const manual = ctx.approvedTaskPackage?.security_requirements?.mdm_verification === 'manual_prelaunch';
|
|
93
104
|
if (manual && typeof this.inspection.beforeSpawn !== 'function')
|
|
94
105
|
throw fail();
|
|
106
|
+
assertPreparationBudget();
|
|
95
107
|
this.inspection.beforeSpawn?.();
|
|
108
|
+
assertPreparationBudget();
|
|
96
109
|
stage = 'codex_spawn';
|
|
97
110
|
const h = this.deps.processHost.spawn(invocation.binary, invocation.args, { cwd: this.plan.cwd, env: invocation.env });
|
|
98
111
|
this.handle = h;
|
|
@@ -115,6 +128,7 @@ export class CodexExecLaunchHost {
|
|
|
115
128
|
void this.cancel().catch(() => { });
|
|
116
129
|
});
|
|
117
130
|
this.current();
|
|
131
|
+
assertPreparationBudget();
|
|
118
132
|
if (supported) {
|
|
119
133
|
const gate = this.deps.execTools;
|
|
120
134
|
gate.bind(h, () => { try {
|
|
@@ -126,35 +140,48 @@ export class CodexExecLaunchHost {
|
|
|
126
140
|
} });
|
|
127
141
|
// Vendor exec consumes stdin before it can initialize its required MCP.
|
|
128
142
|
// Prompt submission may cause provider/account effects; do not call this no-model.
|
|
143
|
+
assertPreparationBudget();
|
|
129
144
|
h.stdin.write(prompt);
|
|
130
145
|
this.current();
|
|
146
|
+
assertPreparationBudget();
|
|
131
147
|
h.stdin.end();
|
|
132
148
|
this.current();
|
|
149
|
+
assertPreparationBudget();
|
|
133
150
|
gate.promptSubmitted(h);
|
|
134
151
|
stage = 'codex_mcp';
|
|
135
152
|
const observed = await gate.wait(h, this.abort.signal);
|
|
136
153
|
this.current();
|
|
154
|
+
assertPreparationBudget();
|
|
137
155
|
requireExecHandshake(observed, h, this.plan);
|
|
156
|
+
assertPreparationBudget();
|
|
138
157
|
gate.authorize(h);
|
|
139
158
|
this.current();
|
|
159
|
+
assertPreparationBudget();
|
|
140
160
|
requireExecHandshake(observed, h, this.plan);
|
|
161
|
+
assertPreparationBudget();
|
|
141
162
|
this.receiptValue = Object.freeze({ receipt_version: this.plan.mode === UNMANAGED_EXEC_MODE ? 'cli-exec-job-security-receipt-v3' : 'cli-exec-job-security-receipt-v2', evidence_origin: 'employee_runner', transport: EXEC_TRANSPORT, security_mode: this.plan.mode, ...(this.plan.mode === UNMANAGED_EXEC_MODE ? { policy_scope: this.inspection.snapshot.policyScope } : {}), phase: 'post_prompt_mcp_authorized', policy_evidence: 'reviewed_release_and_current_inputs', mcp_evidence: 'authenticated_server_discovery', native_policy_observed: false, first_inference_observed: false, execution_nonce: this.plan.executionNonce, invocation_fingerprint: this.plan.invocationFingerprint, ...this.plan.identity });
|
|
142
163
|
return h;
|
|
143
164
|
}
|
|
144
165
|
const handshake = await this.deps.handshake?.(h, this.plan, this.abort.signal);
|
|
145
166
|
this.current();
|
|
167
|
+
assertPreparationBudget();
|
|
146
168
|
requireExecHandshake(handshake, h, this.plan);
|
|
147
169
|
this.handshake = handshake;
|
|
148
170
|
// An input hash is not a native effective-config certificate. Preserve the
|
|
149
171
|
// evidence origin in the audit receipt and never reuse AppServer authority.
|
|
150
172
|
const receipt = Object.freeze({ receipt_version: 'cli-exec-job-security-receipt-v1', evidence_origin: 'employee_runner', transport: EXEC_TRANSPORT, security_mode: EXEC_MODE, phase: 'exec_pre_prompt', execution_nonce: this.plan.executionNonce, invocation_fingerprint: this.plan.invocationFingerprint, ...this.plan.identity });
|
|
151
173
|
this.current();
|
|
174
|
+
assertPreparationBudget();
|
|
152
175
|
requireExecHandshake(handshake, h, this.plan);
|
|
176
|
+
assertPreparationBudget();
|
|
153
177
|
h.stdin.write(prompt);
|
|
154
178
|
this.current();
|
|
179
|
+
assertPreparationBudget();
|
|
155
180
|
h.stdin.end();
|
|
156
181
|
this.current();
|
|
182
|
+
assertPreparationBudget();
|
|
157
183
|
requireExecHandshake(handshake, h, this.plan);
|
|
184
|
+
assertPreparationBudget();
|
|
158
185
|
this.receiptValue = receipt;
|
|
159
186
|
return h;
|
|
160
187
|
})();
|