@sabaiway/agent-workflow-kit 14.22.0 → 14.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +74 -0
- package/README.md +1 -1
- package/SKILL.md +1 -1
- package/bridges/antigravity-cli-bridge/SKILL.md +16 -10
- package/bridges/antigravity-cli-bridge/bin/agy-jev.test.mjs +105 -0
- package/bridges/antigravity-cli-bridge/bin/agy-review.sh +20 -6
- package/bridges/antigravity-cli-bridge/bin/agy.sh +26 -11
- package/bridges/antigravity-cli-bridge/capability.json +3 -3
- package/bridges/antigravity-cli-bridge/references/models-and-flags.md +4 -3
- package/bridges/antigravity-cli-bridge/setup/README.md +2 -1
- package/bridges/codex-cli-bridge/SKILL.md +44 -24
- package/bridges/codex-cli-bridge/bin/codex-exec-jev.test.mjs +385 -0
- package/bridges/codex-cli-bridge/bin/codex-exec.sh +262 -42
- package/bridges/codex-cli-bridge/bin/codex-exec.test.mjs +16 -18
- package/bridges/codex-cli-bridge/bin/codex-model-posture.test.mjs +16 -7
- package/bridges/codex-cli-bridge/bin/codex-review-honesty.test.mjs +1 -0
- package/bridges/codex-cli-bridge/bin/codex-review-jev.test.mjs +301 -0
- package/bridges/codex-cli-bridge/bin/codex-review.sh +240 -26
- package/bridges/codex-cli-bridge/bin/codex-review.test.mjs +9 -9
- package/bridges/codex-cli-bridge/capability.json +10 -10
- package/bridges/codex-cli-bridge/references/driving-codex.md +18 -14
- package/bridges/codex-cli-bridge/references/sandbox-and-flags.md +61 -22
- package/bridges/codex-cli-bridge/setup/README.md +8 -5
- package/capability.json +1 -1
- package/package.json +1 -1
- package/references/modes/recommendations.md +5 -4
- package/references/modes/velocity.md +1 -1
- package/tools/ack-write.mjs +1 -1
- package/tools/bridge-sandbox-recipe.mjs +40 -0
- package/tools/detect-backends.mjs +3 -3
- package/tools/manifest/schema.md +9 -9
- package/tools/manifest/validate.mjs +5 -5
- package/tools/recommendations.mjs +17 -46
- package/tools/velocity-profile.mjs +64 -14
|
@@ -9,8 +9,11 @@
|
|
|
9
9
|
#
|
|
10
10
|
# Fixed policy (single source of truth — passed via flags + --ignore-user-config,
|
|
11
11
|
# so behaviour is deterministic regardless of ~/.codex/config.toml):
|
|
12
|
-
# -
|
|
13
|
-
#
|
|
12
|
+
# - codex may edit the repo and the temp dirs, nothing else: on Linux with codex-cli 0.160.0 or
|
|
13
|
+
# newer, from a repository outside every temp root and off a codex version and profile digest
|
|
14
|
+
# recorded as refused, the run carries the permissions profile `jev` (workspace write, network to
|
|
15
|
+
# api.typesafe.ai and docs.typesafe.ai only); elsewhere today's workspace-write sandbox with network off. The banner's
|
|
16
|
+
# jev= field says which; new dependencies / network installs are done by a human
|
|
14
17
|
# - approval_policy=never: there is no TTY in exec; anything needing escalation
|
|
15
18
|
# is refused and reported, then handled by hand
|
|
16
19
|
# - the host posture's model and reasoning effort (see below)
|
|
@@ -26,9 +29,10 @@
|
|
|
26
29
|
# re-sending context), never from a per-call downgrade.
|
|
27
30
|
#
|
|
28
31
|
# Auth: SUBSCRIPTION ONLY. Uses the cached ChatGPT login under CODEX_HOME
|
|
29
|
-
# (~/.codex). The wrapper unsets every *_API_KEY
|
|
30
|
-
# --ignore-user-config, so a stray
|
|
31
|
-
# never silently switch billing or change
|
|
32
|
+
# (~/.codex). The wrapper unsets OPENAI_BASE_URL and every *_API_KEY except
|
|
33
|
+
# TYPESAFE_API_KEY (which passes as given) and passes --ignore-user-config, so a stray
|
|
34
|
+
# key or a personal ~/.codex/config.toml can never silently switch billing or change
|
|
35
|
+
# behaviour. No credentials are bundled.
|
|
32
36
|
#
|
|
33
37
|
# Usage (installed on PATH as `codex-exec`):
|
|
34
38
|
# codex-exec docs/plans/<slug>.md # drive a plan file
|
|
@@ -47,7 +51,7 @@ set -euo pipefail
|
|
|
47
51
|
case "${1:-}" in
|
|
48
52
|
--help|-h)
|
|
49
53
|
cat <<'HELP'
|
|
50
|
-
codex-exec — delegate plan/instruction EXECUTION to the OpenAI Codex CLI (subscription-only;
|
|
54
|
+
codex-exec — delegate plan/instruction EXECUTION to the OpenAI Codex CLI (subscription-only; writes to the repository and the temp dirs only, network to api.typesafe.ai and docs.typesafe.ai where the profile runs and off elsewhere, git writes blocked — the orchestrator commits).
|
|
51
55
|
|
|
52
56
|
Usage:
|
|
53
57
|
codex-exec [--nonce <n>] <plan-file|->
|
|
@@ -66,6 +70,7 @@ Round-2 / resume:
|
|
|
66
70
|
Guarded passthrough after '--':
|
|
67
71
|
blocked always: -c* --config* -s* --sandbox* --dangerously-bypass-approvals-and-sandbox --dangerously-bypass-hook-trust --full-auto --oss --local-provider* -p* --profile* -m* --model* -o* --output-last-message* --json* --color* --output-schema* --ephemeral*
|
|
68
72
|
relaxed only under CODEX_PROBE=1: --add-dir* -C* --cd* --skip-git-repo-check --ignore-rules --enable* --disable*
|
|
73
|
+
(under the permissions profile `jev`, --add-dir -C --cd --enable --disable refuse even then)
|
|
69
74
|
|
|
70
75
|
Receipt:
|
|
71
76
|
side effect — a NONCED run mints ONE exec receipt beside the delegation store: the dispatch nonce
|
|
@@ -121,18 +126,24 @@ Notes:
|
|
|
121
126
|
(excludedCommands / a per-run consented bypass) on the OBSERVED bwrap/EPERM failure, never a
|
|
122
127
|
preemptive blanket
|
|
123
128
|
exec posture banner: ONE stderr line before dispatch states the ACTUAL run posture —
|
|
124
|
-
exec posture: model=… effort=… tier=… sandbox=workspace-write session=fresh|resume:<id> source=… timeout=… —
|
|
125
|
-
from RESOLVED post-validation values; the resume id is validated pre-spend,
|
|
126
|
-
|
|
129
|
+
exec posture: model=… effort=… tier=… sandbox=jev-profile|workspace-write session=fresh|resume:<id> jev=… source=… timeout=… —
|
|
130
|
+
from RESOLVED post-validation values; the resume id is validated pre-spend, control bytes in
|
|
131
|
+
a model, effort, tier or timeout value refuse pre-spend, and a jev= reason shows a control byte as ?
|
|
127
132
|
threat model: the sidecar byte and grammar screens detect corrupted input under a trusted parent
|
|
128
133
|
environment. A hostile parent environment — including exported shell functions or PATH
|
|
129
134
|
substitution of core/backend commands — is outside the threat model and can substitute the
|
|
130
135
|
backend itself. Targeted shadow-proof resolution protects banner/dispatch honesty from accidental
|
|
131
136
|
shadowing; it is not an environment security boundary
|
|
132
|
-
the exec posture banner
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
137
|
+
the exec posture banner states what the run reaches in a jev= field: jev=api.typesafe.ai,docs.typesafe.ai
|
|
138
|
+
when the run carries the permissions profile `jev` (Linux, codex-cli 0.160.0 or newer, a repository
|
|
139
|
+
outside every temp root, a version and profile digest not recorded as refused), else
|
|
140
|
+
jev=unreachable (<reason>) on today's workspace-write flags with network off; a run whose codex
|
|
141
|
+
refuses the profile exits 72 with no success receipt and, for a refused profile key, records its
|
|
142
|
+
version and digest when the state file can be written, so the next run on them takes today's flags; then a banner-only
|
|
143
|
+
source=model:<s>,effort:<s> field — where each value came from: default, setting or environment —
|
|
144
|
+
then a banner-only timeout=<duration|uncapped> field — exactly the duration handed to timeout(1),
|
|
145
|
+
uncapped when no timeout/gtimeout binary caps the run; INFORMATIONAL only: none of the three is
|
|
146
|
+
persisted in a receipt or session sidecar
|
|
136
147
|
quote the posture banner verbatim when labeling this dispatch — the banner is the machine-stated
|
|
137
148
|
posture; a prose re-type drifts
|
|
138
149
|
every-run nested-sandbox scan (DUAL policy, deliberately two different rules): the scan runs on
|
|
@@ -164,6 +175,9 @@ esac
|
|
|
164
175
|
AW_SETTINGS_APPLIED="CODEX_SERVICE_TIER CODEX_HARD_TIMEOUT"
|
|
165
176
|
DEFAULT_CODEX_MODEL="gpt-6.1-sol"
|
|
166
177
|
DEFAULT_CODEX_EFFORT="high"
|
|
178
|
+
# This wrapper's filesystem part of the permissions profile `jev` (the shared block below): the whole disk read,
|
|
179
|
+
# writes to the temp dirs and the repository root the wrapper preflights and runs from (P3).
|
|
180
|
+
AW_PROFILE_FILESYSTEM='{":root"="read",":tmpdir"="write",":slash_tmp"="write",":workspace_roots"={"."="write"}}'
|
|
167
181
|
|
|
168
182
|
# --- Bridge settings file (host-level, kit-independent) — byte-identical across the four wrappers ---
|
|
169
183
|
# ${XDG_CONFIG_HOME:-$HOME/.config}/agent-workflow/bridge-settings.conf holds KEY=VALUE lines,
|
|
@@ -352,6 +366,19 @@ aw_resolve_timeout_bin() {
|
|
|
352
366
|
[[ -f "$bin" && -x "$bin" ]] || { printf ''; return 0; }
|
|
353
367
|
printf '%s' "$bin"
|
|
354
368
|
}
|
|
369
|
+
aw_scrub_billing_keys() {
|
|
370
|
+
# The one key rule (spec jev-every-run): OPENAI_BASE_URL and every *_API_KEY are unset, so a stray key can never
|
|
371
|
+
# switch a run to paid billing; TYPESAFE_API_KEY is neither read nor set, so it reaches the CLI exactly as given.
|
|
372
|
+
local name
|
|
373
|
+
unset OPENAI_BASE_URL 2>/dev/null || true
|
|
374
|
+
for name in $(compgen -v); do
|
|
375
|
+
case "$name" in
|
|
376
|
+
TYPESAFE_API_KEY) ;;
|
|
377
|
+
*_API_KEY) unset "$name" 2>/dev/null || true ;;
|
|
378
|
+
esac
|
|
379
|
+
done
|
|
380
|
+
return 0
|
|
381
|
+
}
|
|
355
382
|
|
|
356
383
|
# Generous hard wall-clock cap, sized for a slow run at the host posture's effort (subscription latency
|
|
357
384
|
# varies — a trivial reply was observed taking minutes). Raise for a known-healthy
|
|
@@ -403,7 +430,7 @@ CHATGPT_LOGIN_GUARD="Logged in using ChatGPT"
|
|
|
403
430
|
# receipt this wrapper mints; scripts/release/version-sync.mjs bumps it under the one-anchor-per-file
|
|
404
431
|
# rule, so a release can never leave it behind (the AD-053 drift class).
|
|
405
432
|
AW_RECEIPT_BACKEND="codex"
|
|
406
|
-
AW_BRIDGE_VERSION="
|
|
433
|
+
AW_BRIDGE_VERSION="5.0.0" # aw-version-anchor
|
|
407
434
|
# The kill grace handed to timeout(1) as --kill-after, and recorded in the receipt as killGraceS:
|
|
408
435
|
# ONE constant, so the number the ledger checks against the dispatch deadline is the number the run
|
|
409
436
|
# actually applied.
|
|
@@ -416,12 +443,8 @@ fi
|
|
|
416
443
|
|
|
417
444
|
# --- Subscription-only guard -------------------------------------------------
|
|
418
445
|
# Never let an API key (or a user config) silently switch codex to paid api-key
|
|
419
|
-
# billing or alternate behaviour
|
|
420
|
-
|
|
421
|
-
unset OPENAI_API_KEY CODEX_API_KEY OPENAI_BASE_URL 2>/dev/null || true
|
|
422
|
-
while IFS= read -r _api_key_var; do
|
|
423
|
-
unset "$_api_key_var" 2>/dev/null || true
|
|
424
|
-
done < <(compgen -v 2>/dev/null | grep '_API_KEY$' || true)
|
|
446
|
+
# billing or alternate behaviour: the shared key rule, before the first spawn of codex.
|
|
447
|
+
aw_scrub_billing_keys
|
|
425
448
|
|
|
426
449
|
# --- Environment preflight (fail fast, before spending a subscription run) ----
|
|
427
450
|
if ! command -v codex >/dev/null 2>&1; then
|
|
@@ -472,6 +495,179 @@ if [[ ! -f AGENTS.md ]]; then
|
|
|
472
495
|
exit 2
|
|
473
496
|
fi
|
|
474
497
|
|
|
498
|
+
# --- The permissions profile `jev` (spec jev-every-run) — byte-identical in codex-exec.sh and codex-review.sh ---
|
|
499
|
+
# On Linux with codex-cli 0.160.0 or newer a run carries the profile `jev` by four -c overrides, with neither
|
|
500
|
+
# --sandbox nor sandbox_mode beside them: its filesystem part is this wrapper's AW_PROFILE_FILESYSTEM, and its
|
|
501
|
+
# network reaches api.typesafe.ai and docs.typesafe.ai and no other host (a literal of this bridge, which never
|
|
502
|
+
# imports the kit). Elsewhere, and on a codex version and profile digest recorded as refused, the run keeps
|
|
503
|
+
# today's flags and its banner says jev=unreachable (<reason>). Each run, a resume included, chooses afresh.
|
|
504
|
+
AW_PROFILE_FLOOR="0.160.0"
|
|
505
|
+
AW_PROFILE_HOSTS="api.typesafe.ai,docs.typesafe.ai"
|
|
506
|
+
AW_PROFILE_FLAGS=(-c default_permissions=jev -c permissions.jev.network.enabled=true
|
|
507
|
+
-c 'permissions.jev.network.domains={"api.typesafe.ai"="allow","docs.typesafe.ai"="allow"}'
|
|
508
|
+
-c "permissions.jev.filesystem=$AW_PROFILE_FILESYSTEM")
|
|
509
|
+
# The version on the first line of `codex --version` ("codex-cli X.Y.Z[-pre]"); nothing when unreadable.
|
|
510
|
+
aw_codex_version() { # $1 = the timeout binary or ""
|
|
511
|
+
local raw="" line="" re='^codex-cli[[:space:]]+([0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?)[[:space:]]*$'
|
|
512
|
+
if [[ -n "$1" ]]; then
|
|
513
|
+
raw="$("$1" --kill-after=5s 15 codex --version </dev/null 2>/dev/null)" || raw=""
|
|
514
|
+
else
|
|
515
|
+
raw="$(codex --version </dev/null 2>/dev/null)" || raw=""
|
|
516
|
+
fi
|
|
517
|
+
line="${raw%%$'\n'*}"
|
|
518
|
+
line="${line%$'\r'}"
|
|
519
|
+
if [[ "$line" =~ $re ]]; then printf '%s' "${BASH_REMATCH[1]}"; fi
|
|
520
|
+
return 0
|
|
521
|
+
}
|
|
522
|
+
# 0 when version $1 is at or above $2, compared as major.minor.patch; a prerelease sorts below its release.
|
|
523
|
+
aw_version_at_least() {
|
|
524
|
+
local core="${1%%-*}" have=() want=() i
|
|
525
|
+
IFS=. read -r -a have <<<"$core"
|
|
526
|
+
IFS=. read -r -a want <<<"$2"
|
|
527
|
+
for i in 0 1 2; do
|
|
528
|
+
if (( 10#${have[i]} != 10#${want[i]} )); then
|
|
529
|
+
(( 10#${have[i]} > 10#${want[i]} ))
|
|
530
|
+
return
|
|
531
|
+
fi
|
|
532
|
+
done
|
|
533
|
+
[[ "$1" == "$core" ]]
|
|
534
|
+
}
|
|
535
|
+
aw_profile_state_file() {
|
|
536
|
+
printf '%s/agent-workflow/codex-jev-profile-refused' "${XDG_STATE_HOME:-$HOME/.local/state}"
|
|
537
|
+
}
|
|
538
|
+
# sha256 of this run's profile overrides, one per line; nothing without a sha256 tool.
|
|
539
|
+
aw_profile_digest() {
|
|
540
|
+
local sum=""
|
|
541
|
+
if command -v sha256sum >/dev/null 2>&1; then
|
|
542
|
+
sum="$(printf '%s\n' "${AW_PROFILE_FLAGS[@]}" | sha256sum 2>/dev/null)" || sum=""
|
|
543
|
+
elif command -v shasum >/dev/null 2>&1; then
|
|
544
|
+
sum="$(printf '%s\n' "${AW_PROFILE_FLAGS[@]}" | shasum -a 256 2>/dev/null)" || sum=""
|
|
545
|
+
fi
|
|
546
|
+
printf '%s' "${sum%% *}"
|
|
547
|
+
}
|
|
548
|
+
# 0 when the state file $1 lists "<version $2> <digest $3>"; an unreadable file reads as empty.
|
|
549
|
+
aw_profile_refused() {
|
|
550
|
+
local line=""
|
|
551
|
+
[[ -n "$3" && -f "$1" && -r "$1" ]] || return 1
|
|
552
|
+
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
553
|
+
if [[ "${line%$'\r'}" == "$2 $3" ]]; then return 0; fi
|
|
554
|
+
done <"$1"
|
|
555
|
+
return 1
|
|
556
|
+
}
|
|
557
|
+
# Appends "<version $2> <digest $3>" to the state file $1, its directory made 0700; one stderr line when it cannot.
|
|
558
|
+
aw_profile_record() {
|
|
559
|
+
local dir="${1%/*}"
|
|
560
|
+
if [[ -n "$3" ]] && { ( umask 077 && mkdir -p -- "$dir" ) && chmod 700 -- "$dir" && printf '%s %s\n' "$2" "$3" >>"$1"; } 2>/dev/null; then
|
|
561
|
+
return 0
|
|
562
|
+
fi
|
|
563
|
+
echo "warning: could not append the refused profile to $1 — until it can be written, every run on this codex version spends one run on the refusal." >&2
|
|
564
|
+
return 1
|
|
565
|
+
}
|
|
566
|
+
# Chooses this run's posture: profile_on (1 or 0), profile_reason, and the banner's jev= value in profile_label.
|
|
567
|
+
aw_profile_choose() { # $1 = the timeout binary or "", $2 = the repository's temp root or ""
|
|
568
|
+
local os=""
|
|
569
|
+
profile_on=0
|
|
570
|
+
profile_reason=""
|
|
571
|
+
profile_version="$(aw_codex_version "$1")"
|
|
572
|
+
profile_digest="$(aw_profile_digest)"
|
|
573
|
+
os="$(uname -s 2>/dev/null)" || os=""
|
|
574
|
+
if [[ "$os" != "Linux" ]]; then
|
|
575
|
+
profile_reason="not linux (${os:-unreadable})"
|
|
576
|
+
elif [[ -z "$profile_version" ]]; then
|
|
577
|
+
profile_reason="codex version unreadable"
|
|
578
|
+
elif ! aw_version_at_least "$profile_version" "$AW_PROFILE_FLOOR"; then
|
|
579
|
+
profile_reason="codex $profile_version below $AW_PROFILE_FLOOR"
|
|
580
|
+
elif [[ -n "$2" ]]; then
|
|
581
|
+
profile_reason="repository under $2"
|
|
582
|
+
elif aw_profile_refused "$(aw_profile_state_file)" "$profile_version" "$profile_digest"; then
|
|
583
|
+
profile_reason="profile not recognized by codex $profile_version, recorded in $(aw_profile_state_file)"
|
|
584
|
+
else
|
|
585
|
+
profile_on=1
|
|
586
|
+
fi
|
|
587
|
+
if (( profile_on )); then profile_label="$AW_PROFILE_HOSTS"; else profile_label="unreachable ($profile_reason)"; fi
|
|
588
|
+
profile_label="${profile_label//[$'\x01'-$'\x1f'$'\x7f']/?}"
|
|
589
|
+
return 0
|
|
590
|
+
}
|
|
591
|
+
# The loud failure, judged on ONE attempt's trace: 0 on a match, its kind in profile_refusal (1 = a P1/P13-shaped
|
|
592
|
+
# error item before the first turn.started, 2 = `bwrap: execvp` in a command item whose failure is proven) and
|
|
593
|
+
# the matched text in profile_refusal_text. The same strings in a successful command or the final message never fire.
|
|
594
|
+
aw_profile_scan() { # $1 = the trace
|
|
595
|
+
local line="" started=0
|
|
596
|
+
local item_re='^\{"type":"item\.completed","item":\{("id":"[^"]*",)?"type":"error","message":"((Permissions profile `jev` |Configured filesystem path `[^`"]*` is not recognized )[^"\\]*)"'
|
|
597
|
+
profile_refusal=0
|
|
598
|
+
profile_refusal_text=""
|
|
599
|
+
[[ -r "$1" ]] || return 1
|
|
600
|
+
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
601
|
+
if [[ "$line" == '{"type":"turn.started"'* ]]; then started=1; fi
|
|
602
|
+
if (( ! started )) && [[ "$line" =~ $item_re ]]; then
|
|
603
|
+
profile_refusal=1
|
|
604
|
+
profile_refusal_text="${BASH_REMATCH[2]}"
|
|
605
|
+
return 0
|
|
606
|
+
fi
|
|
607
|
+
if aw_ns_item_evidence "$line" && [[ "$AW_NS_OUTPUT" == *'bwrap: execvp'* ]]; then
|
|
608
|
+
profile_refusal=2
|
|
609
|
+
profile_refusal_text="${AW_NS_OUTPUT%%\\n*}"
|
|
610
|
+
return 0
|
|
611
|
+
fi
|
|
612
|
+
done <"$1"
|
|
613
|
+
return 1
|
|
614
|
+
}
|
|
615
|
+
# Prints the matched refusal (no "update codex" remedy: none is known) and records a refused profile key.
|
|
616
|
+
aw_profile_refusal_report() {
|
|
617
|
+
local file=""
|
|
618
|
+
echo "error: codex refused this run's permissions profile \`jev\` (exit 72): $profile_refusal_text" >&2
|
|
619
|
+
if (( profile_refusal == 1 )); then
|
|
620
|
+
file="$(aw_profile_state_file)"
|
|
621
|
+
if aw_profile_record "$file" "$profile_version" "$profile_digest"; then
|
|
622
|
+
echo " recorded in $file: the next run on codex $profile_version with this profile runs today's flags (jev=unreachable)." >&2
|
|
623
|
+
fi
|
|
624
|
+
fi
|
|
625
|
+
return 0
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
# The temp root this repository sits under, printed; nothing when none. The exec profile leaves a .git under /tmp
|
|
629
|
+
# or $TMPDIR writable (P12a) while today's flags keep it read-only (P16), so such a run keeps today's flags.
|
|
630
|
+
aw_repo_temp_root() {
|
|
631
|
+
local root="" real_root="" path="" real=""
|
|
632
|
+
for root in /tmp "${TMPDIR:-}"; do
|
|
633
|
+
[[ -n "$root" ]] || continue
|
|
634
|
+
real_root="$(builtin cd -P -- "$root" 2>/dev/null && builtin pwd -P)" || continue
|
|
635
|
+
# --git-common-dir may answer relative to this directory, where cd -P resolves it; no --path-format (git 2.31+).
|
|
636
|
+
for path in "$(git rev-parse --show-toplevel 2>/dev/null)" "$(git rev-parse --git-common-dir 2>/dev/null)"; do
|
|
637
|
+
[[ -n "$path" ]] || continue
|
|
638
|
+
real="$(builtin cd -P -- "$path" 2>/dev/null && builtin pwd -P)" || continue
|
|
639
|
+
if [[ "$real" == "$real_root" || "$real" == "${real_root%/}/"* ]]; then
|
|
640
|
+
if [[ "$root" == /tmp ]]; then printf '/tmp'; else printf '%s' "$real_root"; fi
|
|
641
|
+
return 0
|
|
642
|
+
fi
|
|
643
|
+
done
|
|
644
|
+
done
|
|
645
|
+
return 0
|
|
646
|
+
}
|
|
647
|
+
# The version read is capped by the same timeout binary as the run (resolved once; the banner reuses it).
|
|
648
|
+
timeout_bin="$(aw_resolve_timeout_bin)"
|
|
649
|
+
aw_profile_choose "$timeout_bin" "$(aw_repo_temp_root)"
|
|
650
|
+
network_flags=()
|
|
651
|
+
if (( profile_on )); then
|
|
652
|
+
aw_sandbox_label="jev-profile"
|
|
653
|
+
read -r -d '' aw_network_item <<'ITEM' || true
|
|
654
|
+
5. Do NOT commit. Network: the hosts api.typesafe.ai and docs.typesafe.ai are
|
|
655
|
+
reachable; network access to any other host is an escalation: STOP and report.
|
|
656
|
+
If you hit another blocker needing escalation (writes outside the repo, a live
|
|
657
|
+
approval, or an ambiguous decision), STOP and report it clearly — never guess.
|
|
658
|
+
ITEM
|
|
659
|
+
aw_network_line="Network, as of this run (it replaces any earlier statement): the hosts api.typesafe.ai and docs.typesafe.ai are reachable; network access to any other host is an escalation: STOP and report."
|
|
660
|
+
else
|
|
661
|
+
aw_sandbox_label="workspace-write"
|
|
662
|
+
network_flags=(-c sandbox_workspace_write.network_access=false)
|
|
663
|
+
read -r -d '' aw_network_item <<'ITEM' || true
|
|
664
|
+
5. Do NOT commit. If you hit a blocker needing escalation (network access, writes
|
|
665
|
+
outside the repo, a live approval, or an ambiguous decision), STOP and report
|
|
666
|
+
it clearly — never guess.
|
|
667
|
+
ITEM
|
|
668
|
+
aw_network_line="Network, as of this run (it replaces any earlier statement): network access is an escalation: STOP and report."
|
|
669
|
+
fi
|
|
670
|
+
|
|
475
671
|
read -r -d '' ORCHESTRATOR_DIRECTIVE <<'DIRECTIVE' || true
|
|
476
672
|
ORCHESTRATOR EXECUTION CONTRACT — read before the task, follow it exactly:
|
|
477
673
|
1. Work directly in the current working tree on the current git branch. NEVER run
|
|
@@ -486,21 +682,17 @@ ORCHESTRATOR EXECUTION CONTRACT — read before the task, follow it exactly:
|
|
|
486
682
|
4. Run the verification / gate set the project declares (in AGENTS.md or the
|
|
487
683
|
task). If the project declares NO gate set, STOP and report — do NOT invent
|
|
488
684
|
checks. Fix every failure before finishing.
|
|
489
|
-
5. Do NOT commit. If you hit a blocker needing escalation (network access, writes
|
|
490
|
-
outside the repo, a live approval, or an ambiguous decision), STOP and report
|
|
491
|
-
it clearly — never guess.
|
|
492
|
-
|
|
493
|
-
TASK:
|
|
494
685
|
DIRECTIVE
|
|
686
|
+
# Item 5 states this run's posture (spec jev-every-run): the two hosts on a profile run, network access whole on a fallback run.
|
|
687
|
+
ORCHESTRATOR_DIRECTIVE="$ORCHESTRATOR_DIRECTIVE"$'\n'"$aw_network_item"$'\n\nTASK:'
|
|
495
688
|
|
|
496
689
|
read -r -d '' RESUME_REMINDER <<'REMINDER' || true
|
|
497
690
|
CONTINUE the existing task in the same working tree under the SAME contract: never
|
|
498
691
|
run a git write command (the orchestrator commits), obey the project's root AGENTS.md
|
|
499
692
|
Hard Constraints, run the project's declared gates, do NOT commit, and STOP + report
|
|
500
693
|
any blocker — never guess.
|
|
501
|
-
|
|
502
|
-
NEW INSTRUCTION:
|
|
503
694
|
REMINDER
|
|
695
|
+
RESUME_REMINDER="$RESUME_REMINDER"$'\n'"$aw_network_line"$'\n\nNEW INSTRUCTION:'
|
|
504
696
|
|
|
505
697
|
if [[ $# -lt 1 ]]; then
|
|
506
698
|
echo "usage: $0 <plan-file|-> [-- extra codex args...]" >&2
|
|
@@ -659,6 +851,12 @@ else
|
|
|
659
851
|
echo " Set CODEX_PROBE=1 for a throwaway probe, or invoke 'codex' directly." >&2
|
|
660
852
|
exit 2
|
|
661
853
|
fi
|
|
854
|
+
# Under the permissions profile `jev` these five would move its workspace root or its features.
|
|
855
|
+
if (( profile_on )) && [[ "$_arg" == --add-dir* || "$_arg" == -C* || "$_arg" == --cd* || "$_arg" == --enable* || "$_arg" == --disable* ]]; then
|
|
856
|
+
echo "error: passthrough flag '$_arg' is refused while the run carries the permissions profile \`jev\` — it would move the" >&2
|
|
857
|
+
echo " profile's workspace root or features; nothing was spent. A run on today's flags (jev=unreachable) admits it." >&2
|
|
858
|
+
exit 2
|
|
859
|
+
fi
|
|
662
860
|
;;
|
|
663
861
|
esac
|
|
664
862
|
done
|
|
@@ -930,13 +1128,19 @@ if [[ -n "$resume_mode" ]]; then
|
|
|
930
1128
|
# so the capture posture is shared with a fresh run MINUS that flag. Shipping --color here once
|
|
931
1129
|
# broke every resume invocation with a pre-spend exit 2 — the fake CLI in the suite accepts any
|
|
932
1130
|
# argv, so only the real one can answer this question. See RESUME_ACCEPTED_FLAGS in the test.
|
|
1131
|
+
# The posture rides -c on a resume too: the profile's overrides (P7), else sandbox_mode with network off.
|
|
1132
|
+
if (( profile_on )); then
|
|
1133
|
+
posture_flags=("${AW_PROFILE_FLAGS[@]}")
|
|
1134
|
+
else
|
|
1135
|
+
posture_flags=(-c sandbox_mode=workspace-write)
|
|
1136
|
+
fi
|
|
933
1137
|
codex_cmd=(codex exec resume "$resume_id"
|
|
934
1138
|
--ignore-user-config
|
|
935
1139
|
-m "$CODEX_MODEL"
|
|
936
1140
|
-c model_reasoning_effort="$CODEX_EFFORT"
|
|
937
|
-
|
|
1141
|
+
"${posture_flags[@]}"
|
|
938
1142
|
-c approval_policy=never
|
|
939
|
-
|
|
1143
|
+
"${network_flags[@]+"${network_flags[@]}"}"
|
|
940
1144
|
-c hide_agent_reasoning=true
|
|
941
1145
|
-c model_reasoning_summary=none
|
|
942
1146
|
"${tier_flags[@]+"${tier_flags[@]}"}"
|
|
@@ -949,11 +1153,16 @@ else
|
|
|
949
1153
|
# (thread.started carries the session id). CoT is dropped and colour disabled, so
|
|
950
1154
|
# the captured surfaces stay clean. Reasoning still runs at the run's effort — quality is
|
|
951
1155
|
# unchanged; we only stop printing the noise.
|
|
1156
|
+
if (( profile_on )); then
|
|
1157
|
+
posture_flags=("${AW_PROFILE_FLAGS[@]}")
|
|
1158
|
+
else
|
|
1159
|
+
posture_flags=(--sandbox workspace-write)
|
|
1160
|
+
fi
|
|
952
1161
|
codex_cmd=(codex exec
|
|
953
1162
|
--ignore-user-config
|
|
954
|
-
|
|
1163
|
+
"${posture_flags[@]}"
|
|
955
1164
|
-c approval_policy="never"
|
|
956
|
-
|
|
1165
|
+
"${network_flags[@]+"${network_flags[@]}"}"
|
|
957
1166
|
-c model_reasoning_effort="$CODEX_EFFORT"
|
|
958
1167
|
-c hide_agent_reasoning=true
|
|
959
1168
|
-c model_reasoning_summary=none
|
|
@@ -976,10 +1185,9 @@ run_env=(env "PATH=$shim_dir:$PATH")
|
|
|
976
1185
|
# after the initial TERM if codex ignores it (a live probe confirmed plain
|
|
977
1186
|
# `timeout` reaps the whole codex child tree — no --foreground needed). If neither
|
|
978
1187
|
# binary exists we warn loudly and run uncapped rather than fail silently.
|
|
979
|
-
# aw_resolve_timeout_bin: builtin type -P (an exported function
|
|
980
|
-
# `type` itself), normalized to an ABSOLUTE path fail-closed — the dispatch
|
|
981
|
-
# absolute path the banner rendered from (banner and run never make independent conclusions).
|
|
982
|
-
timeout_bin="$(aw_resolve_timeout_bin)"
|
|
1188
|
+
# aw_resolve_timeout_bin (resolved once, before the version read): builtin type -P (an exported function
|
|
1189
|
+
# can shadow neither `timeout` nor `type` itself), normalized to an ABSOLUTE path fail-closed — the dispatch
|
|
1190
|
+
# invokes the same absolute path the banner rendered from (banner and run never make independent conclusions).
|
|
983
1191
|
if [[ -z "$timeout_bin" ]]; then
|
|
984
1192
|
# D8 — an ACCOUNTED dispatch that cannot be capped can never honour the terminal-exit rule: the
|
|
985
1193
|
# ledger records capS + killGraceS and refuses a dispatch whose deadline is below their sum, so a
|
|
@@ -1087,7 +1295,7 @@ fi
|
|
|
1087
1295
|
aw_timeout_banner="$(aw_timeout_label "$timeout_bin" "$CODEX_HARD_TIMEOUT")"
|
|
1088
1296
|
aw_session_label="fresh"
|
|
1089
1297
|
[[ -n "$resume_mode" ]] && aw_session_label="resume:$resume_id"
|
|
1090
|
-
echo "exec posture: model=$CODEX_MODEL effort=$CODEX_EFFORT tier=${CODEX_SERVICE_TIER:-standard} sandbox
|
|
1298
|
+
echo "exec posture: model=$CODEX_MODEL effort=$CODEX_EFFORT tier=${CODEX_SERVICE_TIER:-standard} sandbox=$aw_sandbox_label session=$aw_session_label jev=$profile_label source=model:$CODEX_MODEL_SOURCE,effort:$CODEX_EFFORT_SOURCE timeout=$aw_timeout_banner" >&2
|
|
1091
1299
|
|
|
1092
1300
|
# --- The PRE-SPEND reservation (delegation Plan 2 / D1) ------------------------
|
|
1093
1301
|
# Written immediately before the CLI runs — after EVERY preflight and after the posture banner, so a
|
|
@@ -1227,9 +1435,11 @@ aw_ns_is_string_content() {
|
|
|
1227
1435
|
return 0
|
|
1228
1436
|
}
|
|
1229
1437
|
|
|
1230
|
-
# One trace line → 0 only when THAT line is a command_execution item with a PROVEN failure
|
|
1231
|
-
# aggregated_output
|
|
1232
|
-
#
|
|
1438
|
+
# One trace line → 0 only when THAT line is a command_execution item with a PROVEN failure; its
|
|
1439
|
+
# aggregated_output (still JSON-escaped) lands in AW_NS_OUTPUT for the caller's token test — both
|
|
1440
|
+
# token classes for the nested-sandbox scan, `bwrap: execvp` for the profile scan. codex-review.sh
|
|
1441
|
+
# carries this walk and aw_ns_is_string_content byte-identical. The wrapper stays dependency-free,
|
|
1442
|
+
# so this is not a JSON parse — it is ONE anchored walk over the CLI's observed serialization in which every SKIPPED
|
|
1233
1443
|
# gap is PROVEN to be a single JSON string's content (aw_ns_is_string_content). Both halves are
|
|
1234
1444
|
# load-bearing: testing the fields as independent substrings lets a decoy object supply the anchor
|
|
1235
1445
|
# while the failure fields belong to another item, and skipping a gap without validating it lets the
|
|
@@ -1276,7 +1486,8 @@ aw_ns_item_evidence() {
|
|
|
1276
1486
|
else
|
|
1277
1487
|
return 1
|
|
1278
1488
|
fi
|
|
1279
|
-
|
|
1489
|
+
AW_NS_OUTPUT="$agg"
|
|
1490
|
+
return 0
|
|
1280
1491
|
}
|
|
1281
1492
|
|
|
1282
1493
|
aw_scan_nested_sandbox() { # $1 = rc, $2 = trace path
|
|
@@ -1292,7 +1503,7 @@ aw_scan_nested_sandbox() { # $1 = rc, $2 = trace path
|
|
|
1292
1503
|
fi
|
|
1293
1504
|
local line
|
|
1294
1505
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
1295
|
-
if aw_ns_item_evidence "$line"; then
|
|
1506
|
+
if aw_ns_item_evidence "$line" && aw_ns_both_tokens "$AW_NS_OUTPUT"; then
|
|
1296
1507
|
echo "warning: NESTED-SANDBOX — this run COMPLETED, but a tool call inside it FAILED with a sandbox-setup" >&2
|
|
1297
1508
|
echo " signature. codex-exec ships its own OS sandbox (bwrap), which cannot run nested inside a" >&2
|
|
1298
1509
|
echo " harness sandbox (the FS turns read-only), so the backend most likely could not read what" >&2
|
|
@@ -1319,6 +1530,15 @@ fi
|
|
|
1319
1530
|
rc=$?
|
|
1320
1531
|
set -e
|
|
1321
1532
|
|
|
1533
|
+
# --- The loud failure (spec jev-every-run): codex refusing the profile, before any receipt or final message ---
|
|
1534
|
+
# Exit 72 is a status no other arm uses; a nonced run's terminal receipt then records transport-failure.
|
|
1535
|
+
profile_refused=0
|
|
1536
|
+
if (( profile_on )) && aw_profile_scan "$trace"; then
|
|
1537
|
+
aw_profile_refusal_report
|
|
1538
|
+
profile_refused=1
|
|
1539
|
+
rc=72
|
|
1540
|
+
fi
|
|
1541
|
+
|
|
1322
1542
|
# --- The session id, captured BEFORE any outcome branching (D3) ----------------
|
|
1323
1543
|
# It used to be extracted only on the SUCCESS path, after the timeout and failure exits, so a run
|
|
1324
1544
|
# that failed or timed out recorded no identity at all — and the session id is exactly the handle an
|
|
@@ -1484,7 +1704,7 @@ AW_JS
|
|
|
1484
1704
|
if [[ $rc -eq 124 || $rc -eq 137 ]]; then
|
|
1485
1705
|
echo "error: codex exec exceeded the hard cap CODEX_HARD_TIMEOUT=${CODEX_HARD_TIMEOUT}s and was terminated." >&2
|
|
1486
1706
|
echo " Raise CODEX_HARD_TIMEOUT for a known-healthy slow run, or narrow the task, then re-dispatch." >&2
|
|
1487
|
-
elif [[ $rc -ne 0 ]]; then
|
|
1707
|
+
elif [[ $rc -ne 0 && $profile_refused -eq 0 ]]; then
|
|
1488
1708
|
echo "error: codex exec failed (exit $rc). Last lines of the run trace:" >&2
|
|
1489
1709
|
tail -n 40 "$trace" >&2 || true
|
|
1490
1710
|
aw_scan_nested_sandbox "$rc" "$trace" || true
|
|
@@ -2,7 +2,7 @@ import { describe, it, after } from 'node:test';
|
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import {
|
|
4
4
|
mkdtempSync, mkdirSync, writeFileSync, chmodSync, rmSync, readFileSync,
|
|
5
|
-
existsSync, readdirSync, symlinkSync, cpSync,
|
|
5
|
+
existsSync, readdirSync, symlinkSync, cpSync, realpathSync,
|
|
6
6
|
} from 'node:fs';
|
|
7
7
|
import { tmpdir } from 'node:os';
|
|
8
8
|
import { join, dirname, resolve } from 'node:path';
|
|
@@ -22,6 +22,7 @@ const FAKE_CODEX = [
|
|
|
22
22
|
'#!/usr/bin/env bash',
|
|
23
23
|
'set -u',
|
|
24
24
|
'if [[ "${1:-}" == "login" ]]; then echo "${CODEX_FAKE_LOGIN:-Logged in using ChatGPT}"; exit 0; fi',
|
|
25
|
+
'if [[ "${1:-}" == "--version" ]]; then echo "codex-cli ${CODEX_FAKE_VERSION:-0.160.0}"; exit 0; fi',
|
|
25
26
|
'if [[ "${1:-}" == "debug" ]]; then cat <<EOF',
|
|
26
27
|
'{"models":[{"slug":"gpt-6.1-sol","priority":1,"visibility":"list","default_reasoning_level":"low","supported_reasoning_levels":[{"effort":"low"},{"effort":"medium"},{"effort":"high"}]},{"slug":"gpt-6-astra","priority":2,"visibility":"list","default_reasoning_level":"medium","supported_reasoning_levels":[{"effort":"medium"},{"effort":"high"}]}]}',
|
|
27
28
|
'EOF',
|
|
@@ -83,6 +84,7 @@ const FAKE_CODEX = [
|
|
|
83
84
|
'exit "${CODEX_FAKE_EXIT:-0}"',
|
|
84
85
|
'',
|
|
85
86
|
].join('\n');
|
|
87
|
+
const FAKE_UNAME = `#!/usr/bin/env bash\nif [[ $# -eq 0 || "$1" == "-s" ]]; then echo Linux; exit 0; fi\nexec "${spawnSync('bash', ['-c', 'type -P uname'], { encoding: 'utf8' }).stdout.trim()}" "$@"\n`; // Linux on any host
|
|
86
88
|
|
|
87
89
|
// The sandbox base and the PATH farms are READ-ONLY per invocation, so both are built ONCE and
|
|
88
90
|
// shared (a per-test `git init`+commit and a per-call farm rebuild dominate the wall otherwise).
|
|
@@ -94,6 +96,7 @@ const TEMPLATE_ROOT = (() => {
|
|
|
94
96
|
const bin = join(root, 'bin');
|
|
95
97
|
mkdirSync(bin, { recursive: true });
|
|
96
98
|
writeFileSync(join(bin, 'codex'), FAKE_CODEX, { mode: 0o755 });
|
|
99
|
+
writeFileSync(join(bin, 'uname'), FAKE_UNAME, { mode: 0o755 });
|
|
97
100
|
// A git work tree with a root AGENTS.md — the wrapper preflights both.
|
|
98
101
|
const repo = join(root, 'repo');
|
|
99
102
|
mkdirSync(repo);
|
|
@@ -111,9 +114,10 @@ const makeSandbox = () => {
|
|
|
111
114
|
const root = mkdtempSync(join(tmpdir(), 'codex-exec-test-'));
|
|
112
115
|
cpSync(TEMPLATE_ROOT, root, { recursive: true });
|
|
113
116
|
const bin = join(root, 'bin');
|
|
114
|
-
chmodSync(join(bin,
|
|
117
|
+
for (const name of ['codex', 'uname']) chmodSync(join(bin, name), 0o755);
|
|
115
118
|
return { root, bin, repo: join(root, 'repo') };
|
|
116
119
|
};
|
|
120
|
+
const tempRootOf = (sb) => (realpathSync(sb.repo).startsWith(`${realpathSync('/tmp')}/`) ? '/tmp' : realpathSync(process.env.TMPDIR ?? '/tmp')); // spec jev-every-run: a sandbox sits under a temp root, so codex-exec takes the fallback naming it
|
|
117
121
|
|
|
118
122
|
// A PATH dir mirroring the real one MINUS the named binaries, to exercise the
|
|
119
123
|
// missing-binary fallbacks (no-cap when timeout is gone; the codex/git preflight
|
|
@@ -669,13 +673,16 @@ describe('codex-exec.sh — resume entrypoint restates every invariant (3.1)', {
|
|
|
669
673
|
assert.equal(sent.includes('--color'), false, 'the regression this list exists to prevent');
|
|
670
674
|
});
|
|
671
675
|
|
|
672
|
-
|
|
676
|
+
// The profile half (spec jev-every-run S2) is codex-exec-jev.test.mjs's: this sandbox sits under a temp root.
|
|
677
|
+
it('--resume <id> on a fallback run: composes `exec resume <id>` with the full restated policy', async () => {
|
|
673
678
|
const sb = makeSandbox();
|
|
679
|
+
const root = tempRootOf(sb);
|
|
674
680
|
const r = await run(sb, { args: ['--resume', 'sess-xyz', '-'], input: 'continue please' });
|
|
675
681
|
rmSync(sb.root, { recursive: true, force: true });
|
|
676
682
|
assert.equal(r.status, 0, r.stderr);
|
|
677
683
|
assert.match(r.argv, /(^|\n)sess-xyz(\n|$)/, 'the session id is passed positionally');
|
|
678
684
|
for (const inv of RESUME_INVARIANTS) assert.match(r.argv, inv, `resume argv must include ${inv}`);
|
|
685
|
+
assert.ok(r.stderr.includes(` jev=unreachable (repository under ${root}) source=`), r.stderr);
|
|
679
686
|
assert.match(r.stdout, /FAKE_FINAL_MESSAGE/, 'resume prints the final message');
|
|
680
687
|
});
|
|
681
688
|
|
|
@@ -1511,15 +1518,15 @@ describe('codex-exec.sh — settings surface ⟷ manifest (D6, manifest-pinned)'
|
|
|
1511
1518
|
describe('codex-exec.sh — dispatch-posture labeling (D5, AD-061)', { concurrency: 2 }, () => {
|
|
1512
1519
|
const banners = (stderr) => stderr.split('\n').filter((l) => l.startsWith('exec posture: '));
|
|
1513
1520
|
|
|
1514
|
-
it('ONE banner line carries the ACTUAL {model, effort, tier, sandbox, session, timeout} on a fresh run', async () => {
|
|
1521
|
+
it('ONE banner line carries the ACTUAL {model, effort, tier, sandbox, session, jev, timeout} on a fresh fallback run', async () => {
|
|
1515
1522
|
const sb = makeSandbox();
|
|
1523
|
+
const root = tempRootOf(sb);
|
|
1516
1524
|
const r = await run(sb, {});
|
|
1517
1525
|
rmSync(sb.root, { recursive: true, force: true });
|
|
1518
1526
|
assert.equal(r.status, 0, r.stderr);
|
|
1519
1527
|
const lines = banners(r.stderr);
|
|
1520
1528
|
assert.equal(lines.length, 1, 'EXACTLY ONE banner line per run');
|
|
1521
|
-
assert.equal(lines[0],
|
|
1522
|
-
'exec posture: model=gpt-6.1-sol effort=high tier=standard sandbox=workspace-write session=fresh source=model:default,effort:default timeout=3600s');
|
|
1529
|
+
assert.equal(lines[0], `exec posture: model=gpt-6.1-sol effort=high tier=standard sandbox=workspace-write session=fresh jev=unreachable (repository under ${root}) source=model:default,effort:default timeout=3600s`);
|
|
1523
1530
|
});
|
|
1524
1531
|
|
|
1525
1532
|
it('an ARMED Fast tier rides the banner (tier=priority)', async () => {
|
|
@@ -1695,10 +1702,7 @@ describe('codex-exec.sh — dispatch-posture labeling (D5, AD-061)', { concurren
|
|
|
1695
1702
|
'exec "$@"',
|
|
1696
1703
|
'',
|
|
1697
1704
|
].join('\n'), { mode: 0o755 });
|
|
1698
|
-
const r = await run(sb, {
|
|
1699
|
-
path: `relbin:${sb.bin}:${farmFor(['timeout', 'gtimeout'])}`,
|
|
1700
|
-
env: { TIMEOUT_STUB_CAP: cap },
|
|
1701
|
-
});
|
|
1705
|
+
const r = await run(sb, { path: `relbin:${sb.bin}:${farmFor(['timeout', 'gtimeout'])}`, env: { TIMEOUT_STUB_CAP: cap } });
|
|
1702
1706
|
const argv0 = existsSync(cap) ? readFileSync(cap, 'utf8').trim() : '';
|
|
1703
1707
|
rmSync(sb.root, { recursive: true, force: true });
|
|
1704
1708
|
assert.equal(r.status, 0, r.stderr);
|
|
@@ -2181,10 +2185,7 @@ fs.lstatSync = (target, ...rest) => {
|
|
|
2181
2185
|
it('a TIMEOUT publishes exitStatus 124 with outcome transport-failure', async () => {
|
|
2182
2186
|
const sb = makeSandbox();
|
|
2183
2187
|
const file = writeContract(sb, 'slow');
|
|
2184
|
-
const r = await runAsync(sb, {
|
|
2185
|
-
args: ['--nonce', 'slow', file],
|
|
2186
|
-
env: { CODEX_FAKE_SLEEP: '3', CODEX_HARD_TIMEOUT: '1' },
|
|
2187
|
-
});
|
|
2188
|
+
const r = await runAsync(sb, { args: ['--nonce', 'slow', file], env: { CODEX_FAKE_SLEEP: '3', CODEX_HARD_TIMEOUT: '1' } });
|
|
2188
2189
|
const receipt = readReceipt(storeDirOf(sb), 'slow');
|
|
2189
2190
|
rmSync(sb.root, { recursive: true, force: true });
|
|
2190
2191
|
assert.equal(r.status, 124, r.stderr);
|
|
@@ -2215,10 +2216,7 @@ fs.lstatSync = (target, ...rest) => {
|
|
|
2215
2216
|
const sb = makeSandbox();
|
|
2216
2217
|
const file = writeContract(sb, 'noreport');
|
|
2217
2218
|
const dir = storeDirOf(sb);
|
|
2218
|
-
const r = await run(sb, {
|
|
2219
|
-
args: ['--nonce', 'noreport', file],
|
|
2220
|
-
env: { CODEX_FAKE_MKDIR: join(dir, reportName('noreport')) },
|
|
2221
|
-
});
|
|
2219
|
+
const r = await run(sb, { args: ['--nonce', 'noreport', file], env: { CODEX_FAKE_MKDIR: join(dir, reportName('noreport')) } });
|
|
2222
2220
|
const receipt = readReceipt(dir, 'noreport');
|
|
2223
2221
|
rmSync(sb.root, { recursive: true, force: true });
|
|
2224
2222
|
assert.equal(r.status, 71, r.stderr);
|